Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-11-19 14:30:17 +01:00
Merge remote-tracking branch 'origin/minecraftauth' into minecraftauth
Dieser Commit ist enthalten in:
Commit
adff671088
14
.editorconfig
Normale Datei
14
.editorconfig
Normale Datei
@ -0,0 +1,14 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
tab_width = 4
|
||||
max_line_length = off
|
||||
|
||||
[*.java]
|
||||
ij_java_class_count_to_use_import_on_demand = 9999
|
||||
ij_java_doc_align_exception_comments = false
|
||||
ij_java_doc_align_param_comments = false
|
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@ -16,7 +16,6 @@ on:
|
||||
- 'LICENSE'
|
||||
- 'Jenkinsfile '
|
||||
- 'README.md'
|
||||
- 'licenseheader.txt'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -14,7 +14,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
|
||||
|
||||
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
|
||||
|
||||
### Currently supporting Minecraft Bedrock 1.20.80 - 1.21.0 and Minecraft Java 1.21
|
||||
### Currently supporting Minecraft Bedrock 1.20.80 - 1.21.1 and Minecraft Java 1.21
|
||||
|
||||
## Setting Up
|
||||
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
|
||||
|
@ -74,7 +74,7 @@ tasks.processResources {
|
||||
expand(
|
||||
"branch" to info.branch,
|
||||
"buildNumber" to info.buildNumber,
|
||||
"projectVersion" to project.version,
|
||||
"projectVersion" to info.version,
|
||||
"commit" to info.commit,
|
||||
"commitAbbrev" to info.commitAbbrev,
|
||||
"commitMessage" to info.commitMessage,
|
||||
@ -88,20 +88,25 @@ sourceSets {
|
||||
blossom {
|
||||
val info = GitInfo()
|
||||
javaSources {
|
||||
property("version", "${project.version} (${info.gitVersion})")
|
||||
property("version", info.version)
|
||||
property("gitVersion", info.gitVersion)
|
||||
property("buildNumber", info.buildNumber.toString())
|
||||
property("branch", info.branch)
|
||||
property("commit", info.commit)
|
||||
property("repository", info.repository)
|
||||
property("devVersion", info.isDev.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.buildNumber(): Int =
|
||||
fun buildNumber(): Int =
|
||||
(System.getenv("BUILD_NUMBER"))?.let { Integer.parseInt(it) } ?: -1
|
||||
|
||||
fun isDevBuild(branch: String, repository: String): Boolean {
|
||||
return branch != "master" || repository.equals("https://github.com/GeyserMC/Geyser", ignoreCase = true).not()
|
||||
}
|
||||
|
||||
inner class GitInfo {
|
||||
val branch: String
|
||||
val commit: String
|
||||
@ -114,22 +119,25 @@ inner class GitInfo {
|
||||
val commitMessage: String
|
||||
val repository: String
|
||||
|
||||
val isDev: Boolean
|
||||
|
||||
init {
|
||||
// On Jenkins, a detached head is checked out, so indra cannot determine the branch.
|
||||
// Fortunately, this environment variable is available.
|
||||
branch = indraGit.branchName() ?: System.getenv("BRANCH_NAME") ?: "DEV"
|
||||
branch = indraGit.branchName() ?: "DEV"
|
||||
|
||||
val commit = indraGit.commit()
|
||||
this.commit = commit?.name ?: "0".repeat(40)
|
||||
commitAbbrev = commit?.name?.substring(0, 7) ?: "0".repeat(7)
|
||||
|
||||
gitVersion = "git-${branch}-${commitAbbrev}"
|
||||
version = "${project.version} ($gitVersion)"
|
||||
buildNumber = buildNumber()
|
||||
|
||||
val git = indraGit.git()
|
||||
commitMessage = git?.commit()?.message ?: ""
|
||||
repository = git?.repository?.config?.getString("remote", "origin", "url") ?: ""
|
||||
|
||||
buildNumber = buildNumber()
|
||||
isDev = isDevBuild(branch, repository)
|
||||
val projectVersion = if (isDev) project.version else project.version.toString().replace("SNAPSHOT", "b${buildNumber}")
|
||||
version = "$projectVersion ($gitVersion)"
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,4 +155,4 @@ tasks.register<DownloadFilesTask>("downloadBedrockData") {
|
||||
suffixedFiles = listOf("block_palette.nbt", "creative_items.json", "runtime_item_states.json")
|
||||
|
||||
destinationDir = "$projectDir/src/main/resources/bedrock"
|
||||
}
|
||||
}
|
||||
|
@ -34,4 +34,9 @@ public class BuildData {
|
||||
public static final String BRANCH = "{{ branch }}";
|
||||
public static final String COMMIT = "{{ commit }}";
|
||||
public static final String REPOSITORY = "{{ repository }}";
|
||||
private static final String DEV = "{{ devVersion }}";
|
||||
|
||||
public static boolean isDevBuild() {
|
||||
return Boolean.parseBoolean(DEV);
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +122,7 @@ public class GeyserImpl implements GeyserApi {
|
||||
public static final String BRANCH = BuildData.BRANCH;
|
||||
public static final String COMMIT = BuildData.COMMIT;
|
||||
public static final String REPOSITORY = BuildData.REPOSITORY;
|
||||
public static final boolean IS_DEV = BuildData.isDevBuild();
|
||||
|
||||
/**
|
||||
* Oauth client ID for Microsoft authentication
|
||||
@ -207,6 +208,12 @@ public class GeyserImpl implements GeyserApi {
|
||||
logger.info("");
|
||||
logger.info(GeyserLocale.getLocaleStringLog("geyser.core.load", NAME, VERSION));
|
||||
logger.info("");
|
||||
if (IS_DEV) {
|
||||
// TODO cloud use language string
|
||||
//logger.info(GeyserLocale.getLocaleStringLog("geyser.core.dev_build", "https://discord.gg/geysermc"));
|
||||
logger.info("You are running a development build of Geyser! Please report any bugs you find on our Discord server: %s".formatted("https://discord.gg/geysermc"));
|
||||
logger.info("");
|
||||
}
|
||||
logger.info("******************************************");
|
||||
|
||||
/* Initialize registries */
|
||||
@ -684,9 +691,10 @@ public class GeyserImpl implements GeyserApi {
|
||||
*
|
||||
* @return true if the version number is not 'DEV'.
|
||||
*/
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean isProductionEnvironment() {
|
||||
// First is if Blossom runs, second is if Blossom doesn't run
|
||||
//noinspection ConstantConditions,MismatchedStringCase - changes in production
|
||||
//noinspection ConstantConditions - changes in production
|
||||
return !("git-local/dev-0000000".equals(GeyserImpl.GIT_VERSION) || "${gitVersion}".equals(GeyserImpl.GIT_VERSION));
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,8 @@
|
||||
|
||||
package org.geysermc.geyser.command.defaults;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
@ -37,8 +37,7 @@ import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.WebUtils;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class VersionCommand extends GeyserCommand {
|
||||
@ -72,27 +71,36 @@ public class VersionCommand extends GeyserCommand {
|
||||
GeyserImpl.NAME, GeyserImpl.VERSION, javaVersions, bedrockVersions));
|
||||
|
||||
// Disable update checking in dev mode and for players in Geyser Standalone
|
||||
if (GeyserImpl.getInstance().isProductionEnvironment() && !(!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) {
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.locale()));
|
||||
try {
|
||||
String buildXML = WebUtils.getBody("https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/" +
|
||||
URLEncoder.encode(GeyserImpl.BRANCH, StandardCharsets.UTF_8) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber");
|
||||
if (buildXML.startsWith("<buildNumber>")) {
|
||||
int latestBuildNum = Integer.parseInt(buildXML.replaceAll("<(\\\\)?(/)?buildNumber>", "").trim());
|
||||
int buildNum = this.geyser.buildNumber();
|
||||
if (latestBuildNum == buildNum) {
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.locale()));
|
||||
} else {
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.outdated",
|
||||
sender.locale(), (latestBuildNum - buildNum), Constants.GEYSER_DOWNLOAD_LOCATION));
|
||||
}
|
||||
} else {
|
||||
throw new AssertionError("buildNumber missing");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.version.failed"), e);
|
||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", sender.locale()));
|
||||
if (!GeyserImpl.getInstance().isProductionEnvironment() || (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GeyserImpl.IS_DEV) {
|
||||
// TODO cloud use language string
|
||||
sender.sendMessage("You are running a development build of Geyser! Please report any bugs you find on our Discord server: %s"
|
||||
.formatted("https://discord.gg/geysermc"));
|
||||
//sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.core.dev_build", sender.locale(), "https://discord.gg/geysermc"));
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.locale()));
|
||||
try {
|
||||
int buildNumber = this.geyser.buildNumber();
|
||||
JsonNode response = WebUtils.getJson("https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest");
|
||||
int latestBuildNumber = response.get("build").asInt();
|
||||
|
||||
if (latestBuildNumber == buildNumber) {
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.locale()));
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString(
|
||||
"geyser.commands.version.outdated",
|
||||
sender.locale(), (latestBuildNumber - buildNumber), "https://geysermc.org/download"
|
||||
));
|
||||
} catch (IOException e) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.version.failed"), e);
|
||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", sender.locale()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,7 @@ import java.util.function.BiConsumer;
|
||||
* metadata translators needed to translate the properties sent from the server. The translators are structured in such
|
||||
* a way that inserting a new one (for example in version updates) is convenient.
|
||||
*
|
||||
* @param identifier the Bedrock identifier of this entity
|
||||
* @param <T> the entity type this definition represents
|
||||
*/
|
||||
public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, EntityType entityType, String identifier,
|
||||
|
@ -86,7 +86,11 @@ public class PaintingEntity extends Entity {
|
||||
|
||||
private Vector3f fixOffset(PaintingType paintingName) {
|
||||
Vector3f position = super.position;
|
||||
position = position.add(0.5, 0.5, 0.5);
|
||||
// ViaVersion already adds the offset for us on older versions,
|
||||
// so no need to do it then otherwise it will be spaced
|
||||
if (session.isEmulatePost1_18Logic()) {
|
||||
position = position.add(0.5, 0.5, 0.5);
|
||||
}
|
||||
double widthOffset = paintingName.getWidth() > 1 && paintingName.getWidth() != 3 ? 0.5 : 0;
|
||||
double heightOffset = paintingName.getHeight() > 1 && paintingName.getHeight() != 3 ? 0.5 : 0;
|
||||
|
||||
|
@ -27,15 +27,18 @@ package org.geysermc.geyser.entity.type.player;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.level.BedrockDimension;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.AttributeUtils;
|
||||
import org.geysermc.geyser.util.DimensionUtils;
|
||||
@ -69,6 +72,15 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
|
||||
private int lastAirSupply = getMaxAir();
|
||||
|
||||
/**
|
||||
* Determines if our position is currently out-of-sync with the Java server
|
||||
* due to our workaround for the void floor
|
||||
* <p>
|
||||
* Must be reset when dying, switching worlds, or being teleported out of the void
|
||||
*/
|
||||
@Getter @Setter
|
||||
private boolean voidPositionDesynched;
|
||||
|
||||
public SessionPlayerEntity(GeyserSession session) {
|
||||
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
|
||||
|
||||
@ -87,10 +99,25 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
|
||||
@Override
|
||||
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
|
||||
if (voidPositionDesynched) {
|
||||
if (!isBelowVoidFloor()) {
|
||||
voidPositionDesynched = false; // No need to fix our offset; we've been moved
|
||||
}
|
||||
}
|
||||
super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
|
||||
session.getCollisionManager().updatePlayerBoundingBox(this.position.down(definition.offset()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
|
||||
if (voidPositionDesynched) {
|
||||
if (!isBelowVoidFloor()) {
|
||||
voidPositionDesynched = false; // No need to fix our offset; we've been moved
|
||||
}
|
||||
}
|
||||
super.moveAbsolute(position, yaw, pitch, headYaw, isOnGround, teleported);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(Vector3f position) {
|
||||
if (valid) { // Don't update during session init
|
||||
@ -225,6 +252,9 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
} else {
|
||||
dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, false);
|
||||
}
|
||||
|
||||
// We're either respawning or switching worlds, either way, we are no longer desynched
|
||||
this.setVoidPositionDesynched(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -276,4 +306,48 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
public void resetAir() {
|
||||
this.setAirSupply(getMaxAir());
|
||||
}
|
||||
|
||||
private boolean isBelowVoidFloor() {
|
||||
return position.getY() < voidFloorPosition();
|
||||
}
|
||||
|
||||
public int voidFloorPosition() {
|
||||
// The void floor is offset about 40 blocks below the bottom of the world
|
||||
BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension();
|
||||
return bedrockDimension.minY() - 40;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles teleporting the player below or above the Bedrock void floor.
|
||||
* The Java server should never see this desync as we adjust the position that we send to it
|
||||
*
|
||||
* @param up in which direction to teleport - true to resync our position, or false to be
|
||||
* teleported below the void floor.
|
||||
*/
|
||||
public void teleportVoidFloorFix(boolean up) {
|
||||
// Safety to avoid double teleports
|
||||
if ((voidPositionDesynched && !up) || (!voidPositionDesynched && up)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Work around there being a floor at the bottom of the world and teleport the player below it
|
||||
// Moving from below to above the void floor works fine
|
||||
Vector3f newPosition = this.getPosition();
|
||||
if (up) {
|
||||
newPosition = newPosition.up(4f);
|
||||
voidPositionDesynched = false;
|
||||
} else {
|
||||
newPosition = newPosition.down(4f);
|
||||
voidPositionDesynched = true;
|
||||
}
|
||||
|
||||
this.setPositionManual(newPosition);
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(geyserId);
|
||||
movePlayerPacket.setPosition(newPosition);
|
||||
movePlayerPacket.setRotation(getBedrockRotation());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
|
||||
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
|
||||
session.sendUpstreamPacketImmediately(movePlayerPacket);
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ public class FilledMapItem extends MapItem {
|
||||
switch (mapColor) {
|
||||
case 3830373 -> builder.damage(3); // Ocean Monument
|
||||
case 5393476 -> builder.damage(4); // Woodland explorer
|
||||
case 12741452 -> builder.damage(14); // Trial Chamber
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,8 @@ public enum BedrockMapIcon {
|
||||
ICON_SNOWY_VILLAGE(MapIconType.SNOWY_VILLAGE, 20),
|
||||
ICON_TAIGA_VILLAGE(MapIconType.TAIGA_VILLAGE, 21),
|
||||
ICON_JUNGLE_TEMPLE(MapIconType.JUNGLE_TEMPLE, 22),
|
||||
ICON_SWAMP_HUT(MapIconType.SWAMP_HUT, 23);
|
||||
ICON_SWAMP_HUT(MapIconType.SWAMP_HUT, 23),
|
||||
ICON_TRIAL_CHAMBERS(MapIconType.TRIAL_CHAMBERS, 24);
|
||||
|
||||
private static final BedrockMapIcon[] VALUES = values();
|
||||
|
||||
|
@ -48,7 +48,7 @@ public final class GameProtocol {
|
||||
* release of the game that Geyser supports.
|
||||
*/
|
||||
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v685.CODEC.toBuilder()
|
||||
.minecraftVersion("1.21.0")
|
||||
.minecraftVersion("1.21.1")
|
||||
.build());
|
||||
|
||||
/**
|
||||
@ -67,7 +67,7 @@ public final class GameProtocol {
|
||||
.minecraftVersion("1.20.80/1.20.81")
|
||||
.build()));
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(DEFAULT_BEDROCK_CODEC.toBuilder()
|
||||
.minecraftVersion("1.21.0")
|
||||
.minecraftVersion("1.21.0/1.20.1")
|
||||
.build()));
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ import java.util.Set;
|
||||
|
||||
@Builder
|
||||
@Value
|
||||
public class BlockMappings implements DefinitionRegistry<GeyserBedrockBlock> {
|
||||
public class BlockMappings implements DefinitionRegistry<BlockDefinition> {
|
||||
GeyserBedrockBlock bedrockAir;
|
||||
BlockDefinition bedrockWater;
|
||||
BlockDefinition bedrockMovingBlock;
|
||||
@ -134,7 +134,7 @@ public class BlockMappings implements DefinitionRegistry<GeyserBedrockBlock> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered(GeyserBedrockBlock bedrockBlock) {
|
||||
public boolean isRegistered(BlockDefinition bedrockBlock) {
|
||||
return getDefinition(bedrockBlock.getRuntimeId()) == bedrockBlock;
|
||||
}
|
||||
}
|
@ -60,7 +60,6 @@ import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.*;
|
||||
import org.cloudburstmc.protocol.common.DefinitionRegistry;
|
||||
import org.cloudburstmc.protocol.common.util.OptionalBoolean;
|
||||
import org.geysermc.api.util.BedrockPlatform;
|
||||
import org.geysermc.api.util.InputMode;
|
||||
@ -985,7 +984,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
@Override
|
||||
public void disconnected(DisconnectedEvent event) {
|
||||
loggingIn = false;
|
||||
loggedIn = false;
|
||||
|
||||
String disconnectMessage;
|
||||
Throwable cause = event.getCause();
|
||||
@ -1025,13 +1023,19 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
} else {
|
||||
GeyserImpl.getInstance().getLogger().error("An exception occurred: ", cause);
|
||||
}
|
||||
// GeyserSession is disconnected via session.disconnect() called indirectly be the server
|
||||
// This only needs to be "initiated" here when there is an exception, hence the cause clause
|
||||
GeyserSession.this.disconnect(disconnectMessage);
|
||||
if (geyser.getConfig().isDebugMode()) {
|
||||
cause.printStackTrace();
|
||||
}
|
||||
}
|
||||
if ((!GeyserSession.this.closed && GeyserSession.this.loggedIn) || cause != null) {
|
||||
// GeyserSession is disconnected via session.disconnect() called indirectly be the server
|
||||
// This needs to be "initiated" here when there is an exception, but also when the Netty connection
|
||||
// is closed without a disconnect packet - in this case, closed will still be false, but loggedIn
|
||||
// will also be true as GeyserSession#disconnect will not have been called.
|
||||
GeyserSession.this.disconnect(disconnectMessage);
|
||||
}
|
||||
|
||||
loggedIn = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1450,7 +1454,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
|
||||
private void startGame() {
|
||||
this.upstream.getCodecHelper().setItemDefinitions(this.itemMappings);
|
||||
this.upstream.getCodecHelper().setBlockDefinitions((DefinitionRegistry) this.blockMappings); //FIXME
|
||||
this.upstream.getCodecHelper().setBlockDefinitions(this.blockMappings);
|
||||
this.upstream.getCodecHelper().setCameraPresetDefinitions(CameraDefinitions.CAMERA_DEFINITIONS);
|
||||
|
||||
StartGamePacket startGamePacket = new StartGamePacket();
|
||||
|
@ -309,6 +309,15 @@ public class SkinManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (DEFAULT_FLOODGATE_STEVE.equals(skinUrl)) {
|
||||
// https://github.com/GeyserMC/Floodgate/commit/00b8b1b6364116ff4bc9b00e2015ce35bae8abb1 ensures that
|
||||
// Bedrock players on online-mode servers will always have a textures property. However, this skin is
|
||||
// also sent our way, and isn't overwritten. It's very likely that this skin is *only* a placeholder,
|
||||
// and no one should ever be using it outside of Floodgate, and therefore no one wants to see this
|
||||
// specific Steve skin.
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean isAlex = skinTexture.has("metadata");
|
||||
|
||||
String capeUrl = null;
|
||||
@ -322,5 +331,7 @@ public class SkinManager {
|
||||
|
||||
return new GameProfileData(skinUrl, capeUrl, isAlex);
|
||||
}
|
||||
|
||||
private static final String DEFAULT_FLOODGATE_STEVE = "https://textures.minecraft.net/texture/31f477eb1a7beee631c2ca64d06f8f68fa93a3386d04452ab27f43acdf1b60cb";
|
||||
}
|
||||
}
|
@ -29,9 +29,6 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrays;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
@ -56,7 +53,9 @@ import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
|
@ -417,13 +417,15 @@ public final class ItemTranslator {
|
||||
if (components != null) {
|
||||
// ItemStack#getHoverName as of 1.20.5
|
||||
Component customName = components.get(DataComponentType.CUSTOM_NAME);
|
||||
if (customName == null) {
|
||||
customName = components.get(DataComponentType.ITEM_NAME);
|
||||
}
|
||||
if (customName != null) {
|
||||
// Get the translated name and prefix it with a reset char
|
||||
return MessageTranslator.convertMessage(customName, session.locale());
|
||||
}
|
||||
customName = components.get(DataComponentType.ITEM_NAME);
|
||||
if (customName != null) {
|
||||
// Get the translated name and prefix it with a reset char to prevent italics - matches Java Edition
|
||||
// behavior as of 1.21
|
||||
return ChatColor.RESET + ChatColor.ESCAPE + translationColor + MessageTranslator.convertMessage(customName, session.locale());
|
||||
}
|
||||
}
|
||||
|
||||
if (mapping.hasTranslation()) {
|
||||
|
@ -107,7 +107,7 @@ public class SpawnerBlockEntityTranslator extends BlockEntityTranslator {
|
||||
bedrockNbt.put("isMovable", (byte) 1);
|
||||
}
|
||||
|
||||
static void translateSpawnData(@NonNull NbtMapBuilder builder, @Nullable NbtMap spawnData) {
|
||||
private static void translateSpawnData(@NonNull NbtMapBuilder builder, @Nullable NbtMap spawnData) {
|
||||
if (spawnData == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -27,22 +27,31 @@ package org.geysermc.geyser.translator.level.block.entity;
|
||||
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
|
||||
|
||||
@BlockEntity(type = BlockEntityType.TRIAL_SPAWNER)
|
||||
public class TrialSpawnerBlockEntityTranslator extends BlockEntityTranslator {
|
||||
// Note that it would appear block entity updates don't include the NBT, but we do need it on chunk load.
|
||||
@Override
|
||||
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, BlockState blockState) {
|
||||
if (javaNbt == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// trial spawners have "spawn_data" instead of "SpawnData"
|
||||
SpawnerBlockEntityTranslator.translateSpawnData(bedrockNbt, javaNbt.getCompound("spawn_data", null));
|
||||
|
||||
// Because trial spawners don't exist on bedrock yet
|
||||
bedrockNbt.put("id", "MobSpawner");
|
||||
NbtMap entityData = javaNbt.getCompound("spawn_data").getCompound("entity");
|
||||
if (entityData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
NbtMapBuilder spawnData = NbtMap.builder();
|
||||
EntityDefinition<?> definition = Registries.JAVA_ENTITY_IDENTIFIERS.get(entityData.getString("id"));
|
||||
if (definition != null) {
|
||||
spawnData.putString("TypeId", definition.identifier());
|
||||
}
|
||||
spawnData.putInt("Weight", entityData.getInt("Size", 1)); // ??? presumably since these are the only other two extra attributes
|
||||
bedrockNbt.putCompound("spawn_data", spawnData.build());
|
||||
}
|
||||
}
|
||||
|
@ -25,20 +25,19 @@
|
||||
|
||||
package org.geysermc.geyser.translator.protocol.bedrock.entity.player;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerRotPacket;
|
||||
import org.geysermc.mcprotocollib.network.packet.Packet;
|
||||
import org.cloudburstmc.math.vector.Vector3d;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.level.BedrockDimension;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.mcprotocollib.network.packet.Packet;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerRotPacket;
|
||||
|
||||
@Translator(packet = MovePlayerPacket.class)
|
||||
public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPacket> {
|
||||
@ -93,29 +92,42 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||
Vector3d position = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), packet.isOnGround(), packet.getMode() == MovePlayerPacket.Mode.TELEPORT);
|
||||
if (position != null) { // A null return value cancels the packet
|
||||
boolean onGround = packet.isOnGround();
|
||||
boolean isBelowVoid = entity.isVoidPositionDesynched();
|
||||
|
||||
boolean teleportThroughVoidFloor;
|
||||
boolean teleportThroughVoidFloor, mustResyncPosition;
|
||||
// Compare positions here for void floor fix below before the player's position variable is set to the packet position
|
||||
if (entity.getPosition().getY() >= packet.getPosition().getY()) {
|
||||
if (entity.getPosition().getY() >= packet.getPosition().getY() && !isBelowVoid) {
|
||||
int floorY = position.getFloorY();
|
||||
// The void floor is offset about 40 blocks below the bottom of the world
|
||||
BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension();
|
||||
int voidFloorLocation = bedrockDimension.minY() - 40;
|
||||
teleportThroughVoidFloor = floorY <= (voidFloorLocation + 2) && floorY >= voidFloorLocation;
|
||||
if (teleportThroughVoidFloor) {
|
||||
// https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground.
|
||||
onGround = false;
|
||||
}
|
||||
int voidFloorLocation = entity.voidFloorPosition();
|
||||
teleportThroughVoidFloor = floorY <= (voidFloorLocation + 1) && floorY >= voidFloorLocation;
|
||||
} else {
|
||||
teleportThroughVoidFloor = false;
|
||||
}
|
||||
|
||||
if (teleportThroughVoidFloor || isBelowVoid) {
|
||||
// https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground.
|
||||
onGround = false;
|
||||
}
|
||||
|
||||
if (isBelowVoid) {
|
||||
int floorY = position.getFloorY();
|
||||
int voidFloorLocation = entity.voidFloorPosition();
|
||||
mustResyncPosition = floorY < voidFloorLocation && floorY >= voidFloorLocation - 1;
|
||||
} else {
|
||||
mustResyncPosition = false;
|
||||
}
|
||||
|
||||
double yPosition = position.getY();
|
||||
if (entity.isVoidPositionDesynched()) { // not using the cached variable on purpose
|
||||
yPosition += 4; // We are de-synched since we had to teleport below the void floor.
|
||||
}
|
||||
|
||||
Packet movePacket;
|
||||
if (rotationChanged) {
|
||||
// Send rotation updates as well
|
||||
movePacket = new ServerboundMovePlayerPosRotPacket(
|
||||
onGround,
|
||||
position.getX(), position.getY(), position.getZ(),
|
||||
position.getX(), yPosition, position.getZ(),
|
||||
yaw, pitch
|
||||
);
|
||||
entity.setYaw(yaw);
|
||||
@ -123,7 +135,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||
entity.setHeadYaw(headYaw);
|
||||
} else {
|
||||
// Rotation did not change; don't send an update with rotation
|
||||
movePacket = new ServerboundMovePlayerPosPacket(onGround, position.getX(), position.getY(), position.getZ());
|
||||
movePacket = new ServerboundMovePlayerPosPacket(onGround, position.getX(), yPosition, position.getZ());
|
||||
}
|
||||
|
||||
entity.setPositionManual(packet.getPosition());
|
||||
@ -133,16 +145,9 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||
session.sendDownstreamGamePacket(movePacket);
|
||||
|
||||
if (teleportThroughVoidFloor) {
|
||||
// Work around there being a floor at the bottom of the world and teleport the player below it
|
||||
// Moving from below to above the void floor works fine
|
||||
entity.setPosition(entity.getPosition().sub(0, 4f, 0));
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
movePlayerPacket.setPosition(entity.getPosition());
|
||||
movePlayerPacket.setRotation(entity.getBedrockRotation());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
|
||||
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
entity.teleportVoidFloorFix(false);
|
||||
} else if (mustResyncPosition) {
|
||||
entity.teleportVoidFloorFix(true);
|
||||
}
|
||||
|
||||
session.getSkullCache().updateVisibleSkulls();
|
||||
|
@ -60,6 +60,7 @@ import java.util.function.Function;
|
||||
|
||||
@Translator(packet = ClientboundLevelParticlesPacket.class)
|
||||
public class JavaLevelParticlesTranslator extends PacketTranslator<ClientboundLevelParticlesPacket> {
|
||||
private static final int MAX_PARTICLES = 100;
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundLevelParticlesPacket packet) {
|
||||
@ -71,7 +72,8 @@ public class JavaLevelParticlesTranslator extends PacketTranslator<ClientboundLe
|
||||
session.sendUpstreamPacket(particleCreateFunction.apply(position));
|
||||
} else {
|
||||
Random random = ThreadLocalRandom.current();
|
||||
for (int i = 0; i < packet.getAmount(); i++) {
|
||||
int amount = Math.min(MAX_PARTICLES, packet.getAmount());
|
||||
for (int i = 0; i < amount; i++) {
|
||||
double offsetX = random.nextGaussian() * (double) packet.getOffsetX();
|
||||
double offsetY = random.nextGaussian() * (double) packet.getOffsetY();
|
||||
double offsetZ = random.nextGaussian() * (double) packet.getOffsetZ();
|
||||
@ -213,4 +215,4 @@ public class JavaLevelParticlesTranslator extends PacketTranslator<ClientboundLe
|
||||
.putFloat("z", position.getZ())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren