From 5095518b449af8058be7d4aa0423e11fd587b864 Mon Sep 17 00:00:00 2001 From: Runemoro Date: Tue, 24 Apr 2018 20:02:42 -0400 Subject: [PATCH] Fix lots of vanilla teleportation-related bugs --- build.gradle | 7 +- .../org/dimdev/ddutils/schem/Schematic.java | 2 - .../blocks/BlockDimensionalTrapdoor.java | 1 - .../{ItemRecord.java => ItemModRecord.java} | 5 +- .../dimdoors/shared/items/ItemRiftBlade.java | 1 - .../dimdoors/shared/items/ModItems.java | 2 +- .../tileentities/TileEntityEntranceRift.java | 1 - .../mixins/MixinNetHandlerPlayServer.java | 318 ++++++++++++------ .../resources/assets/dimdoors/lang/en_US.lang | 3 +- .../resources/assets/dimdoors/lang/fr_CA.lang | 4 +- 10 files changed, 231 insertions(+), 113 deletions(-) rename src/main/java/org/dimdev/dimdoors/shared/items/{ItemRecord.java => ItemModRecord.java} (63%) diff --git a/build.gradle b/build.gradle index 32637436..35942898 100644 --- a/build.gradle +++ b/build.gradle @@ -74,12 +74,13 @@ minecraft { replace '${version}', fullVersion makeObfSourceJar = false //noinspection GroovyUnusedAssignment - def args = [ // TODO: https://github.com/SpongePowered/Mixin/issues/140 + def args = [ "-Dfml.noGrab=false", "-Dfml.coreMods.load=org.dimdev.vanillafix.VanillaFixCoreMod", "-Dmixin.env.compatLevel=JAVA_8", - //"-Dmixin.debug.verbose=true", - //"-Dmixin.debug.export=true", + "-Dmixin.debug=true", + "-Dmixin.debug.verbose=true", + "-Dmixin.debug.export=true", "-Dmixin.checks.interfaces=true" ] } diff --git a/src/main/java/org/dimdev/ddutils/schem/Schematic.java b/src/main/java/org/dimdev/ddutils/schem/Schematic.java index 16e8b42f..024489b4 100644 --- a/src/main/java/org/dimdev/ddutils/schem/Schematic.java +++ b/src/main/java/org/dimdev/ddutils/schem/Schematic.java @@ -393,8 +393,6 @@ public class Schematic { long setTime = 0; long relightTime = 0; // CubicChunks makes cubic worlds implement ICubicWorld - // Just "world instanceof ICubicWorld" would throw a class not found error - //noinspection InstanceofIncompatibleInterface if (cubicChunks && ((ICubicWorld) world).isCubicWorld()) { DimDoors.log.info("Setting cube blockstates"); ICubicWorld cubicWorld = (ICubicWorld) world; diff --git a/src/main/java/org/dimdev/dimdoors/shared/blocks/BlockDimensionalTrapdoor.java b/src/main/java/org/dimdev/dimdoors/shared/blocks/BlockDimensionalTrapdoor.java index 03c22b4b..0cc57ba8 100644 --- a/src/main/java/org/dimdev/dimdoors/shared/blocks/BlockDimensionalTrapdoor.java +++ b/src/main/java/org/dimdev/dimdoors/shared/blocks/BlockDimensionalTrapdoor.java @@ -1,6 +1,5 @@ package org.dimdev.dimdoors.shared.blocks; -import net.minecraft.block.Block; import net.minecraft.block.BlockDoor; import net.minecraft.block.BlockTrapDoor; import net.minecraft.block.ITileEntityProvider; diff --git a/src/main/java/org/dimdev/dimdoors/shared/items/ItemRecord.java b/src/main/java/org/dimdev/dimdoors/shared/items/ItemModRecord.java similarity index 63% rename from src/main/java/org/dimdev/dimdoors/shared/items/ItemRecord.java rename to src/main/java/org/dimdev/dimdoors/shared/items/ItemModRecord.java index 9517d96c..ce8c734b 100644 --- a/src/main/java/org/dimdev/dimdoors/shared/items/ItemRecord.java +++ b/src/main/java/org/dimdev/dimdoors/shared/items/ItemModRecord.java @@ -1,10 +1,11 @@ package org.dimdev.dimdoors.shared.items; +import net.minecraft.item.ItemRecord; import net.minecraft.util.SoundEvent; import org.dimdev.dimdoors.DimDoors; -public class ItemRecord extends net.minecraft.item.ItemRecord { - protected ItemRecord(String recordName, SoundEvent soundIn) { +public class ItemModRecord extends ItemRecord { + protected ItemModRecord(String recordName, SoundEvent soundIn) { super(recordName, soundIn); setRegistryName(DimDoors.MODID, "record_" + recordName); setUnlocalizedName("record"); diff --git a/src/main/java/org/dimdev/dimdoors/shared/items/ItemRiftBlade.java b/src/main/java/org/dimdev/dimdoors/shared/items/ItemRiftBlade.java index 003214ec..33d15cd2 100644 --- a/src/main/java/org/dimdev/dimdoors/shared/items/ItemRiftBlade.java +++ b/src/main/java/org/dimdev/dimdoors/shared/items/ItemRiftBlade.java @@ -5,7 +5,6 @@ import net.minecraft.util.text.TextComponentTranslation; import org.dimdev.dimdoors.DimDoors; import org.dimdev.dimdoors.client.TileEntityFloatingRiftRenderer; import org.dimdev.dimdoors.shared.ModConfig; -import org.dimdev.dimdoors.shared.tileentities.TileEntityFloatingRift; import org.dimdev.ddutils.Location; import org.dimdev.ddutils.TeleportUtils; import net.minecraft.client.util.ITooltipFlag; diff --git a/src/main/java/org/dimdev/dimdoors/shared/items/ModItems.java b/src/main/java/org/dimdev/dimdoors/shared/items/ModItems.java index f72ea8f8..2e65b819 100644 --- a/src/main/java/org/dimdev/dimdoors/shared/items/ModItems.java +++ b/src/main/java/org/dimdev/dimdoors/shared/items/ModItems.java @@ -53,7 +53,7 @@ public final class ModItems { public static final ItemDimensionalTrapdoorWood WOOD_DIMENSIONAL_TRAPDOOR = new ItemDimensionalTrapdoorWood(); // Records - public static final ItemRecord CREEPY_RECORD = new ItemRecord("creepy", CREEPY); + public static final ItemModRecord CREEPY_RECORD = new ItemModRecord("creepy", CREEPY); @SubscribeEvent public static void registerItems(RegistryEvent.Register event) { diff --git a/src/main/java/org/dimdev/dimdoors/shared/tileentities/TileEntityEntranceRift.java b/src/main/java/org/dimdev/dimdoors/shared/tileentities/TileEntityEntranceRift.java index 804b8923..6f669a84 100644 --- a/src/main/java/org/dimdev/dimdoors/shared/tileentities/TileEntityEntranceRift.java +++ b/src/main/java/org/dimdev/dimdoors/shared/tileentities/TileEntityEntranceRift.java @@ -22,7 +22,6 @@ import net.minecraft.entity.Entity; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import org.dimdev.dimdoors.shared.blocks.BlockDimensionalDoor; -import org.dimdev.dimdoors.shared.blocks.ModBlocks; import java.util.Random; diff --git a/src/main/java/org/dimdev/vanillafix/mixins/MixinNetHandlerPlayServer.java b/src/main/java/org/dimdev/vanillafix/mixins/MixinNetHandlerPlayServer.java index 95cf1e24..ad3a7a90 100644 --- a/src/main/java/org/dimdev/vanillafix/mixins/MixinNetHandlerPlayServer.java +++ b/src/main/java/org/dimdev/vanillafix/mixins/MixinNetHandlerPlayServer.java @@ -6,24 +6,27 @@ import net.minecraft.init.MobEffects; import net.minecraft.network.NetHandlerPlayServer; import net.minecraft.network.PacketThreadUtil; import net.minecraft.network.play.INetHandlerPlayServer; +import net.minecraft.network.play.client.CPacketConfirmTeleport; import net.minecraft.network.play.client.CPacketPlayer; import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.world.GameType; import net.minecraft.world.WorldServer; import org.apache.logging.log4j.Logger; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; -@SuppressWarnings("unused") // Shadow +@SuppressWarnings({"unused", "NonConstantFieldWithUpperCaseName"}) // Shadow @Mixin(NetHandlerPlayServer.class) public abstract class MixinNetHandlerPlayServer implements INetHandlerPlayServer { @Shadow public EntityPlayerMP player; - @Shadow private /*final*/ MinecraftServer server; + @Shadow @Final private MinecraftServer server; @Shadow private int networkTickCount; @Shadow private double firstGoodX; @Shadow private double firstGoodY; @@ -34,9 +37,10 @@ public abstract class MixinNetHandlerPlayServer implements INetHandlerPlayServer @Shadow private int lastPositionUpdate; @Shadow private boolean floating; @Shadow private Vec3d targetPos; - @Shadow private static final Logger LOGGER = null; + @Shadow @Final private static Logger LOGGER; @Shadow private int movePacketCounter; @Shadow private int lastMovePacketCounter; + @Shadow private int teleportId; @Shadow public void disconnect(final ITextComponent textComponent) {} @@ -46,6 +50,34 @@ public abstract class MixinNetHandlerPlayServer implements INetHandlerPlayServer @Shadow public void setPlayerLocation(double x, double y, double z, float yaw, float pitch) {} + /** + * Rewrite buggy vanilla method: + *

+ * 1. After a teleport, the player should be teleported to the correct location even if they don't + * send a confirmation packet. This fixes MC-98153, preventing players from abusing it to teleport + * to the nether without changing coordinates by disconnecting before sending that packet, or to remain + * invulnerable (but unable to move) by not sending the confirmation packet. Interdimensional teleportation + * also seems much faster now. + *

+ * 2. Block collisions should not be called until the move has been confirmed to be correct (the player + * didn't cheat). This prevents players from cheating to trigger block collision methods such as end + * portals. Also fixes MC-123364, where if anti-cheat is triggered when walking into an end portal, + * the position gets reverted but not the world, likely dropping the player into the void. + *

+ * Bugs fixed: + * - https://bugs.mojang.com/browse/MC-89928 + * - https://bugs.mojang.com/browse/MC-98153 + * - https://bugs.mojang.com/browse/MC-123364 + * - Movements made by server are reverted every second until client confirms teleport + * - Movement stats get increased with illegal moves + * - If teleport packet is received with ping > 1s, game is unplayable until player stops moving + * - lastGoodX/Y/Z not captured after teleport, resulting in "moved too quickly" spam after the client + * accepts a teleport in the same tick. + *

+ * Improvements: + * - Correct position rather than cause jump-backs when player moves wrongly/into a block + */ + @Overwrite @Override public void processPlayer(CPacketPlayer packet) { @@ -53,120 +85,210 @@ public abstract class MixinNetHandlerPlayServer implements INetHandlerPlayServer if (isMovePlayerPacketInvalid(packet)) { disconnect(new TextComponentTranslation("multiplayer.disconnect.invalid_player_movement")); - } else { - WorldServer world = server.getWorld(player.dimension); + return; + } - if (player.queuedEndExit) return; + // Optimization: Check for queuedEndExit first, get or load the world if necessary only. + if (player.queuedEndExit) return; - if (networkTickCount == 0) { - captureCurrentPosition(); - } + WorldServer world = server.getWorld(player.dimension); - if (targetPos != null) { - if (networkTickCount - lastPositionUpdate > 20) { - lastPositionUpdate = networkTickCount; - setPlayerLocation(targetPos.x, targetPos.y, targetPos.z, player.rotationYaw, player.rotationPitch); - } - } else { + if (networkTickCount == 0) { + captureCurrentPosition(); + } + + if (targetPos != null) { + // Fix: Vanilla did this, but why? It's impossible for a packet to get lost with TCP. If we send + // another update packet after just one second, a one-second ping will make the game completely + // unplayable after the server updates the player's position, since by the time the client confirms + // a position change, the server will have sent it another one, repeatedly causing the player to jump + // back until they stop moving for whatever the ping time is. + + /*if (networkTickCount - lastPositionUpdate > 20) { lastPositionUpdate = networkTickCount; - if (player.isRiding()) { - player.setPositionAndRotation(player.posX, player.posY, player.posZ, packet.getYaw(player.rotationYaw), packet.getPitch(player.rotationPitch)); - server.getPlayerList().serverUpdateMovingPlayer(player); - } else { - double oldX = player.posX; - double oldY = player.posY; - double oldZ = player.posZ; - double oldY2 = player.posY; + // Fix: Vanilla code was: setPlayerLocation(targetPos.x, targetPos.y, targetPos.z, + // player.rotationYaw, player.rotationPitch); Why would the server revert movements + // after a teleport just because the client didn't confirm it? I'm assuming that they + // intended to send another updated move packet: + setPlayerLocation(player.posX, player.posY, player.posZ, player.rotationYaw, player.rotationPitch); + }*/ - double packetX = packet.getX(player.posX); - double packetY = packet.getY(player.posY); - double packetZ = packet.getZ(player.posZ); - float packetYaw = packet.getYaw(player.rotationYaw); - float packetPitch = packet.getPitch(player.rotationPitch); + // Instead, we will send another packet both here and in processConfirmTeleport if the position the client + // was sent is no longer good (exceeds tolerance): - double xDiff = packetX - firstGoodX; - double yDiff = packetY - firstGoodY; - double zDiff = packetZ - firstGoodZ; - double speedSq = player.motionX * player.motionX + player.motionY * player.motionY + player.motionZ * player.motionZ; - double distanceSq = xDiff * xDiff + yDiff * yDiff + zDiff * zDiff; + LOGGER.info("Player sent move packets before confirming"); + if (targetPos.squareDistanceTo(player.posX, player.posY, player.posZ) > 1.0D) { + LOGGER.info("Distance became too big: " + targetPos + " vs. " + new Vec3d(player.posX, player.posY, player.posY)); + setPlayerLocation(player.posX, player.posY, player.posZ, player.rotationYaw, player.rotationPitch); + } - if (player.isPlayerSleeping()) { - if (distanceSq > 1.0D) { - setPlayerLocation(player.posX, player.posY, player.posZ, packet.getYaw(player.rotationYaw), packet.getPitch(player.rotationPitch)); - } - } else { - ++movePacketCounter; - int packetCount = movePacketCounter - lastMovePacketCounter; + // Ignore the packet, the client thinks it's still at the old position + return; + } - if (packetCount > 5) { - LOGGER.debug("{} is sending move packets too frequently ({} packets since last tick)", player.getName(), packetCount); - packetCount = 1; - } + lastPositionUpdate = networkTickCount; - if (!player.isInvulnerableDimensionChange() && (!player.getServerWorld().getGameRules().getBoolean("disableElytraMovementCheck") || !player.isElytraFlying())) { - float maxDistancePerTic = player.isElytraFlying() ? 300.0F : 100.0F; + if (player.isRiding()) { + player.setPositionAndRotation(player.posX, player.posY, player.posZ, packet.getYaw(player.rotationYaw), packet.getPitch(player.rotationPitch)); + server.getPlayerList().serverUpdateMovingPlayer(player); + } else { + double prevX = player.posX; + double prevY = player.posY; + double prevZ = player.posZ; - if (distanceSq - speedSq > maxDistancePerTic * packetCount && (!server.isSinglePlayer() || !server.getServerOwner().equals(player.getName()))) { - LOGGER.warn("{} moved too quickly! {},{},{}", player.getName(), xDiff, yDiff, zDiff); - setPlayerLocation(player.posX, player.posY, player.posZ, player.rotationYaw, player.rotationPitch); - return; - } - } + double packetX = packet.getX(player.posX); + double packetY = packet.getY(player.posY); + double packetZ = packet.getZ(player.posZ); + float packetYaw = packet.getYaw(player.rotationYaw); + float packetPitch = packet.getPitch(player.rotationPitch); - boolean notInsideBlock = world.getCollisionBoxes(player, player.getEntityBoundingBox().shrink(0.0625D)).isEmpty(); - xDiff = packetX - lastGoodX; - yDiff = packetY - lastGoodY; - zDiff = packetZ - lastGoodZ; + // Calculate difference from last tick + double xDiff = packetX - firstGoodX; + double yDiff = packetY - firstGoodY; + double zDiff = packetZ - firstGoodZ; + double distanceSq = xDiff * xDiff + yDiff * yDiff + zDiff * zDiff; - if (player.onGround && !packet.isOnGround() && yDiff > 0.0D) { - player.jump(); - } + double motionSq = player.motionX * player.motionX + player.motionY * player.motionY + player.motionZ * player.motionZ; - player.move(MoverType.PLAYER, xDiff, yDiff, zDiff); - player.onGround = packet.isOnGround(); - double oldYDiff = yDiff; + if (player.isPlayerSleeping()) { + if (distanceSq > 1.0D) { + // The player tried to move while sleeping. Send the player the correct position. + setPlayerLocation(player.posX, player.posY, player.posZ, packet.getYaw(player.rotationYaw), packet.getPitch(player.rotationPitch)); + } + } else { + movePacketCounter++; + int packetCount = movePacketCounter - lastMovePacketCounter; - xDiff = packetX - player.posX; - yDiff = packetY - player.posY; - if (yDiff > -0.5D || yDiff < 0.5D) { // TODO: But why? - yDiff = 0.0D; - } - zDiff = packetZ - player.posZ; - distanceSq = xDiff * xDiff + yDiff * yDiff + zDiff * zDiff; + if (packetCount > 5) { + LOGGER.debug("{} is sending move packets too frequently ({} packets since last tick)", player.getName(), packetCount); + packetCount = 1; + } - boolean movedWrongly = false; - if (!player.isInvulnerableDimensionChange() && distanceSq > 0.0625D && !player.isPlayerSleeping() && !player.interactionManager.isCreative() && player.interactionManager.getGameType() != GameType.SPECTATOR) { - movedWrongly = true; - LOGGER.warn("{} moved wrongly!", player.getName()); - } + // Fix: Disable isInvulnerableDimensionChange, not necessary if we don't revert position after teleport anymore + if (/*!player.isInvulnerableDimensionChange() && */!(player.getServerWorld().getGameRules().getBoolean("disableElytraMovementCheck") && player.isElytraFlying())) { + // Not really anti-cheat. Max speed would be 447 blocks/second with 5 packets/tick... Also, shouldn't + // distance increase linearly with the packet count rather than proportional to sqrt(packetCount)? + float maxDistanceSqPerPacket = player.isElytraFlying() ? 300.0F : 100.0F; - // Fix https://bugs.mojang.com/browse/MC-98153 - //player.setPositionAndRotation(packetX, packetY, packetZ, packetYaw, packetPitch); - //player.addMovementStat(player.posX - oldX, player.posY - oldY, player.posZ - oldZ); - player.addMovementStat(packetX - oldX, packetY - oldY, packetZ - oldZ); - - // Fix https://bugs.mojang.com/browse/MC-123364 (partially, players can still cheat to teleport into a portal) - if (!player.isInvulnerableDimensionChange() && !player.noClip && !player.isPlayerSleeping()) { - boolean oldPositionEmpty = world.getCollisionBoxes(player, player.getEntityBoundingBox().shrink(0.0625D)).isEmpty(); - - if (notInsideBlock && (movedWrongly || !oldPositionEmpty)) { - setPlayerLocation(oldX, oldY, oldZ, packetYaw, packetPitch); - return; - } - } - - floating = oldYDiff >= -0.03125D; - floating &= !server.isFlightAllowed() && !player.capabilities.allowFlying; - floating &= !player.isPotionActive(MobEffects.LEVITATION) && !player.isElytraFlying() && !world.checkBlockCollision(player.getEntityBoundingBox().grow(0.0625D).expand(0.0D, -0.55D, 0.0D)); - player.onGround = packet.isOnGround(); - server.getPlayerList().serverUpdateMovingPlayer(player); - player.handleFalling(player.posY - oldY2, packet.isOnGround()); - lastGoodX = player.posX; - lastGoodY = player.posY; - lastGoodZ = player.posZ; + if (distanceSq - motionSq > maxDistanceSqPerPacket * packetCount/* && !(server.isSinglePlayer() && server.getServerOwner().equals(player.getName()))*/) { // TODO: Single-player server owner check disabled for testing + LOGGER.warn("{} moved too quickly! {},{},{}", player.getName(), xDiff, yDiff, zDiff); + setPlayerLocation(player.posX, player.posY, player.posZ, player.rotationYaw, player.rotationPitch); + return; } } + + boolean wasInsideBlock = !world.getCollisionBoxes(player, player.getEntityBoundingBox().shrink(0.0625D)).isEmpty(); + + // Calculate difference from last packet + xDiff = packetX - lastGoodX; + yDiff = packetY - lastGoodY; + zDiff = packetZ - lastGoodZ; + + if (player.onGround && !packet.isOnGround() && yDiff > 0.0D) { + player.jump(); + } + + // Move the player towards the desired position, but not passing through solid + // blocks, past block edges while sneaking, or climbing stairs that are too tall. + // TODO: Players could cheat to avoid collisions with non-solid blocks (end portal), fix this. + player.move(MoverType.PLAYER, xDiff, yDiff, zDiff); + player.onGround = packet.isOnGround(); + + // Fix: If a collision teleported the player, just sync the client without checking that the move was legal. + // Entity.move already made sure that the player didn't cheat, and reverting the move would be wrong because + // the prevX/Y/Z is no longer good in the new dimension. This fixes MC-123364. + if (player.isInvulnerableDimensionChange()) { + LOGGER.info("Doing teleport"); + // A better name for invulnerableDimensionChange would be "lastBlockCollisionCausedPlayerMove". See + // https://github.com/ModCoderPack/MCPBot-Issues/issues/624. This happens when the move caused a + // collision that teleported the player elsewhere. + + // Fix: Immediately set the correct position after a teleport and, rather than reverting it and waiting + // for the player to confirm the teleport (which they might never do). This fixes MC-98153. + setPlayerLocation(player.posX, player.posY, player.posZ, player.rotationYaw, player.rotationPitch); + + // Fix: Disable invulnerability after teleport, this could be abused by players, allowing them to stay + // invulnerable but unable to move after a teleport. + player.clearInvulnerableDimensionChange(); + + // Fix: In processConfirmTeleport (which we moved here, since we're doing the move immediately), only + // lastGoodX/Y/Z are set to the current position, but firstGoodX/Y/Z should be updated too (this can + // be done using captureCurrentPosition). Otherwise, this would result in "moved wrongly" messages if + // the client both accepts the teleport and starts sending move packets that are correct within the same tick. + captureCurrentPosition(); + + yDiff = 0; // Reset fall distance + } else { + // Calculate difference from position accepted by Entity.move + xDiff = packetX - player.posX; + yDiff = packetY - player.posY; + zDiff = packetZ - player.posZ; + + double yDiffA = yDiff > -0.5D || yDiff < 0.5D ? 0.0D : yDiff; + distanceSq = xDiff * xDiff + yDiffA * yDiffA + zDiff * zDiff; + boolean movedWrongly = false; + // Optimization: Remove !player.isPlayerSleeping() check, that was already checked + if (distanceSq > 0.0625D /*&& !player.isPlayerSleeping()*/ && !player.interactionManager.isCreative() && player.interactionManager.getGameType() != GameType.SPECTATOR) { + movedWrongly = true; + LOGGER.warn("{} moved wrongly!", player.getName()); + } + + boolean wouldMoveInsideBlock = !world.getCollisionBoxes(player, player.getEntityBoundingBox().offset(xDiff, yDiff, zDiff).shrink(0.0625D)).isEmpty(); + if (!movedWrongly && !wouldMoveInsideBlock || wasInsideBlock || player.noClip) { + // If the position the player wanted is close enough to what Entity.move calculated + // and not inside a block, or the player was noclipping (either because player.noClip + // was true or they were inside a block), use the client's position instead + player.setPositionAndRotation(packetX, packetY, packetZ, packetYaw, packetPitch); + } else { + // Improvement: Instead of reverting the move to prevX/Y/Z, accept the corrected move, since the + // Entity.move method made sure that it was legal. This has the advantage of just correcting the + // position rather than causing jump-backs and is safer against in case a block collision forgets + // to set invulnerableDimensionChange after a teleport (only causes a log message and some extra + // unnecessary checks). + setPlayerLocation(player.posX, player.posY, player.posZ, packetYaw, packetPitch); + player.addMovementStat(player.posX - prevX, player.posY - prevY, player.posZ - prevZ); + return; + } + // Fix: Update movement stats to the corrected position rather than the position the client wanted. + // This prevents illegal, cancelled (vanilla) or corrected (with improvement above), moves from updating + // stats. + player.addMovementStat(player.posX - prevX, player.posY - prevY, player.posZ - prevZ); + } + + floating = yDiff >= -0.03125D; + floating &= !server.isFlightAllowed() && !player.capabilities.allowFlying; + floating &= !player.isPotionActive(MobEffects.LEVITATION) && !player.isElytraFlying() && !world.checkBlockCollision(player.getEntityBoundingBox().grow(0.0625D).expand(0.0D, -0.55D, 0.0D)); + player.onGround = packet.isOnGround(); + server.getPlayerList().serverUpdateMovingPlayer(player); + player.handleFalling(player.posY - prevY, packet.isOnGround()); + + lastGoodX = player.posX; + lastGoodY = player.posY; + lastGoodZ = player.posZ; + } + } + } + + /** + * Update this method for changes made above. + *

+ * Bugs fixed: + * - Movements made by server are reverted when client confirms teleport + */ + @Overwrite + @Override + public void processConfirmTeleport(CPacketConfirmTeleport packet) { + PacketThreadUtil.checkThreadAndEnqueue(packet, this, player.getServerWorld()); + + if (packet.getTeleportId() == teleportId) { + if (targetPos.squareDistanceTo(player.posX, player.posY, player.posZ) > 1.0D) { + LOGGER.info("Position accepted no longer good"); + // The client accepted the position change, but it's too late, something moved the player. Sync it again. + setPlayerLocation(player.posX, player.posY, player.posZ, player.rotationYaw, player.rotationPitch); + } else { + LOGGER.info("Teleport accepted"); + targetPos = null; } } } diff --git a/src/main/resources/assets/dimdoors/lang/en_US.lang b/src/main/resources/assets/dimdoors/lang/en_US.lang index 67158a3e..d0538159 100644 --- a/src/main/resources/assets/dimdoors/lang/en_US.lang +++ b/src/main/resources/assets/dimdoors/lang/en_US.lang @@ -143,7 +143,6 @@ dimdoors.general.depthSpreadFactor=Depth Spread Factor dimdoors.general.depthSpreadFactor.tooltip=The scale of the dispersion when escaping from a pocket or limbo, in blocks/depth. It is important to remember that by default, limbo is treated as depth 50. dimdoors.general.riftCloseSpeed=Rift Close Speed dimdoors.general.riftCloseSpeed.tooltip=The speed at which rifts close when using the rift remover, in units of rift size per tick. -dimdoors.general.closeDoorBehind.tooltip=When true, Dimensional Doors will automatically close when the player enters their portal. dimdoors.general.teleportOffset=Teleport Offset dimdoors.general.teleportOffset.tooltip=Distance in blocks to teleport the player in front of the dimensional door. dimdoors.general.riftBoundingBoxInCreative=Rift Bounding Box in Creative @@ -201,4 +200,4 @@ dimdoors.graphics.showRiftCore.tooltip=Set this to true to always show rifts' co dimdoors.graphics.riftSize=Rift Size dimdoors.graphics.riftSize.tooltip=Multiplier affecting how large rifts should be rendered, 1 being the default size. dimdoors.graphics.riftJitter=Rift Jitter -dimdoors.graphics.riftSize.tooltip=Multiplier affecting how much rifts should jitter, 1 being the default size. +dimdoors.graphics.riftJitter.tooltip=Multiplier affecting how much rifts should jitter, 1 being the default size. diff --git a/src/main/resources/assets/dimdoors/lang/fr_CA.lang b/src/main/resources/assets/dimdoors/lang/fr_CA.lang index 2cba5790..e4aa9a2c 100644 --- a/src/main/resources/assets/dimdoors/lang/fr_CA.lang +++ b/src/main/resources/assets/dimdoors/lang/fr_CA.lang @@ -109,8 +109,8 @@ dimdoors.general=Options Généralles dimdoors.general.tooltip=Options de configuration généralles pour le mod dimdoors.general.baseDimensionID=Numéro de dimension de base dimdoors.general.baseDimensionID.tooltip=Le numéro de la première dimension de Portes dimensionelles. Les autres dimensions utiliseront des numéros consécutifs. Il est fortement recommendé de laisser cette option à sa valeur par défaut. -dimdoors.general.useStatusBar.name=Messages dans la barre d'état -dimdoors.general.useStatusBar.comment=Utiliser la barre d'état pour envoyer des messages plutôt que la boîte de discussion. +dimdoors.general.useStatusBar=Messages dans la barre d'état +dimdoors.general.useStatusBar.tooltip=Utiliser la barre d'état pour envoyer des messages plutôt que la boîte de discussion. dimdoors.pockets=Options sur les dimensions de poche dimdoors.pockets.tooltip=Options qui déterminent l'espacement et la grandeur maximalle des dimensions de poche dans la dimension qui les contient dimdoors.world=Options de génération de monde