Fix lots of vanilla teleportation-related bugs
This commit is contained in:
parent
7a5d7aecb6
commit
5095518b44
10 changed files with 231 additions and 113 deletions
|
@ -74,12 +74,13 @@ minecraft {
|
||||||
replace '${version}', fullVersion
|
replace '${version}', fullVersion
|
||||||
makeObfSourceJar = false
|
makeObfSourceJar = false
|
||||||
//noinspection GroovyUnusedAssignment
|
//noinspection GroovyUnusedAssignment
|
||||||
def args = [ // TODO: https://github.com/SpongePowered/Mixin/issues/140
|
def args = [
|
||||||
"-Dfml.noGrab=false",
|
"-Dfml.noGrab=false",
|
||||||
"-Dfml.coreMods.load=org.dimdev.vanillafix.VanillaFixCoreMod",
|
"-Dfml.coreMods.load=org.dimdev.vanillafix.VanillaFixCoreMod",
|
||||||
"-Dmixin.env.compatLevel=JAVA_8",
|
"-Dmixin.env.compatLevel=JAVA_8",
|
||||||
//"-Dmixin.debug.verbose=true",
|
"-Dmixin.debug=true",
|
||||||
//"-Dmixin.debug.export=true",
|
"-Dmixin.debug.verbose=true",
|
||||||
|
"-Dmixin.debug.export=true",
|
||||||
"-Dmixin.checks.interfaces=true"
|
"-Dmixin.checks.interfaces=true"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -393,8 +393,6 @@ public class Schematic {
|
||||||
long setTime = 0;
|
long setTime = 0;
|
||||||
long relightTime = 0;
|
long relightTime = 0;
|
||||||
// CubicChunks makes cubic worlds implement ICubicWorld
|
// CubicChunks makes cubic worlds implement ICubicWorld
|
||||||
// Just "world instanceof ICubicWorld" would throw a class not found error
|
|
||||||
//noinspection InstanceofIncompatibleInterface
|
|
||||||
if (cubicChunks && ((ICubicWorld) world).isCubicWorld()) {
|
if (cubicChunks && ((ICubicWorld) world).isCubicWorld()) {
|
||||||
DimDoors.log.info("Setting cube blockstates");
|
DimDoors.log.info("Setting cube blockstates");
|
||||||
ICubicWorld cubicWorld = (ICubicWorld) world;
|
ICubicWorld cubicWorld = (ICubicWorld) world;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package org.dimdev.dimdoors.shared.blocks;
|
package org.dimdev.dimdoors.shared.blocks;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
|
||||||
import net.minecraft.block.BlockDoor;
|
import net.minecraft.block.BlockDoor;
|
||||||
import net.minecraft.block.BlockTrapDoor;
|
import net.minecraft.block.BlockTrapDoor;
|
||||||
import net.minecraft.block.ITileEntityProvider;
|
import net.minecraft.block.ITileEntityProvider;
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
package org.dimdev.dimdoors.shared.items;
|
package org.dimdev.dimdoors.shared.items;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemRecord;
|
||||||
import net.minecraft.util.SoundEvent;
|
import net.minecraft.util.SoundEvent;
|
||||||
import org.dimdev.dimdoors.DimDoors;
|
import org.dimdev.dimdoors.DimDoors;
|
||||||
|
|
||||||
public class ItemRecord extends net.minecraft.item.ItemRecord {
|
public class ItemModRecord extends ItemRecord {
|
||||||
protected ItemRecord(String recordName, SoundEvent soundIn) {
|
protected ItemModRecord(String recordName, SoundEvent soundIn) {
|
||||||
super(recordName, soundIn);
|
super(recordName, soundIn);
|
||||||
setRegistryName(DimDoors.MODID, "record_" + recordName);
|
setRegistryName(DimDoors.MODID, "record_" + recordName);
|
||||||
setUnlocalizedName("record");
|
setUnlocalizedName("record");
|
|
@ -5,7 +5,6 @@ import net.minecraft.util.text.TextComponentTranslation;
|
||||||
import org.dimdev.dimdoors.DimDoors;
|
import org.dimdev.dimdoors.DimDoors;
|
||||||
import org.dimdev.dimdoors.client.TileEntityFloatingRiftRenderer;
|
import org.dimdev.dimdoors.client.TileEntityFloatingRiftRenderer;
|
||||||
import org.dimdev.dimdoors.shared.ModConfig;
|
import org.dimdev.dimdoors.shared.ModConfig;
|
||||||
import org.dimdev.dimdoors.shared.tileentities.TileEntityFloatingRift;
|
|
||||||
import org.dimdev.ddutils.Location;
|
import org.dimdev.ddutils.Location;
|
||||||
import org.dimdev.ddutils.TeleportUtils;
|
import org.dimdev.ddutils.TeleportUtils;
|
||||||
import net.minecraft.client.util.ITooltipFlag;
|
import net.minecraft.client.util.ITooltipFlag;
|
||||||
|
|
|
@ -53,7 +53,7 @@ public final class ModItems {
|
||||||
public static final ItemDimensionalTrapdoorWood WOOD_DIMENSIONAL_TRAPDOOR = new ItemDimensionalTrapdoorWood();
|
public static final ItemDimensionalTrapdoorWood WOOD_DIMENSIONAL_TRAPDOOR = new ItemDimensionalTrapdoorWood();
|
||||||
|
|
||||||
// Records
|
// Records
|
||||||
public static final ItemRecord CREEPY_RECORD = new ItemRecord("creepy", CREEPY);
|
public static final ItemModRecord CREEPY_RECORD = new ItemModRecord("creepy", CREEPY);
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void registerItems(RegistryEvent.Register<Item> event) {
|
public static void registerItems(RegistryEvent.Register<Item> event) {
|
||||||
|
|
|
@ -22,7 +22,6 @@ import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.nbt.NBTTagCompound;
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
import net.minecraft.util.EnumFacing;
|
import net.minecraft.util.EnumFacing;
|
||||||
import org.dimdev.dimdoors.shared.blocks.BlockDimensionalDoor;
|
import org.dimdev.dimdoors.shared.blocks.BlockDimensionalDoor;
|
||||||
import org.dimdev.dimdoors.shared.blocks.ModBlocks;
|
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
|
|
@ -6,24 +6,27 @@ import net.minecraft.init.MobEffects;
|
||||||
import net.minecraft.network.NetHandlerPlayServer;
|
import net.minecraft.network.NetHandlerPlayServer;
|
||||||
import net.minecraft.network.PacketThreadUtil;
|
import net.minecraft.network.PacketThreadUtil;
|
||||||
import net.minecraft.network.play.INetHandlerPlayServer;
|
import net.minecraft.network.play.INetHandlerPlayServer;
|
||||||
|
import net.minecraft.network.play.client.CPacketConfirmTeleport;
|
||||||
import net.minecraft.network.play.client.CPacketPlayer;
|
import net.minecraft.network.play.client.CPacketPlayer;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.util.text.ITextComponent;
|
import net.minecraft.util.text.ITextComponent;
|
||||||
import net.minecraft.util.text.TextComponentTranslation;
|
import net.minecraft.util.text.TextComponentTranslation;
|
||||||
import net.minecraft.world.GameType;
|
import net.minecraft.world.GameType;
|
||||||
import net.minecraft.world.WorldServer;
|
import net.minecraft.world.WorldServer;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
@SuppressWarnings("unused") // Shadow
|
@SuppressWarnings({"unused", "NonConstantFieldWithUpperCaseName"}) // Shadow
|
||||||
@Mixin(NetHandlerPlayServer.class)
|
@Mixin(NetHandlerPlayServer.class)
|
||||||
public abstract class MixinNetHandlerPlayServer implements INetHandlerPlayServer {
|
public abstract class MixinNetHandlerPlayServer implements INetHandlerPlayServer {
|
||||||
|
|
||||||
@Shadow public EntityPlayerMP player;
|
@Shadow public EntityPlayerMP player;
|
||||||
@Shadow private /*final*/ MinecraftServer server;
|
@Shadow @Final private MinecraftServer server;
|
||||||
@Shadow private int networkTickCount;
|
@Shadow private int networkTickCount;
|
||||||
@Shadow private double firstGoodX;
|
@Shadow private double firstGoodX;
|
||||||
@Shadow private double firstGoodY;
|
@Shadow private double firstGoodY;
|
||||||
|
@ -34,9 +37,10 @@ public abstract class MixinNetHandlerPlayServer implements INetHandlerPlayServer
|
||||||
@Shadow private int lastPositionUpdate;
|
@Shadow private int lastPositionUpdate;
|
||||||
@Shadow private boolean floating;
|
@Shadow private boolean floating;
|
||||||
@Shadow private Vec3d targetPos;
|
@Shadow private Vec3d targetPos;
|
||||||
@Shadow private static final Logger LOGGER = null;
|
@Shadow @Final private static Logger LOGGER;
|
||||||
@Shadow private int movePacketCounter;
|
@Shadow private int movePacketCounter;
|
||||||
@Shadow private int lastMovePacketCounter;
|
@Shadow private int lastMovePacketCounter;
|
||||||
|
@Shadow private int teleportId;
|
||||||
|
|
||||||
@Shadow public void disconnect(final ITextComponent textComponent) {}
|
@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) {}
|
@Shadow public void setPlayerLocation(double x, double y, double z, float yaw, float pitch) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrite buggy vanilla method:
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* Improvements:
|
||||||
|
* - Correct position rather than cause jump-backs when player moves wrongly/into a block
|
||||||
|
*/
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
@Override
|
@Override
|
||||||
public void processPlayer(CPacketPlayer packet) {
|
public void processPlayer(CPacketPlayer packet) {
|
||||||
|
@ -53,120 +85,210 @@ public abstract class MixinNetHandlerPlayServer implements INetHandlerPlayServer
|
||||||
|
|
||||||
if (isMovePlayerPacketInvalid(packet)) {
|
if (isMovePlayerPacketInvalid(packet)) {
|
||||||
disconnect(new TextComponentTranslation("multiplayer.disconnect.invalid_player_movement"));
|
disconnect(new TextComponentTranslation("multiplayer.disconnect.invalid_player_movement"));
|
||||||
} else {
|
return;
|
||||||
WorldServer world = server.getWorld(player.dimension);
|
}
|
||||||
|
|
||||||
if (player.queuedEndExit) return;
|
// Optimization: Check for queuedEndExit first, get or load the world if necessary only.
|
||||||
|
if (player.queuedEndExit) return;
|
||||||
|
|
||||||
if (networkTickCount == 0) {
|
WorldServer world = server.getWorld(player.dimension);
|
||||||
captureCurrentPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetPos != null) {
|
if (networkTickCount == 0) {
|
||||||
if (networkTickCount - lastPositionUpdate > 20) {
|
captureCurrentPosition();
|
||||||
lastPositionUpdate = networkTickCount;
|
}
|
||||||
setPlayerLocation(targetPos.x, targetPos.y, targetPos.z, player.rotationYaw, player.rotationPitch);
|
|
||||||
}
|
if (targetPos != null) {
|
||||||
} else {
|
// 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;
|
lastPositionUpdate = networkTickCount;
|
||||||
|
|
||||||
if (player.isRiding()) {
|
// Fix: Vanilla code was: setPlayerLocation(targetPos.x, targetPos.y, targetPos.z,
|
||||||
player.setPositionAndRotation(player.posX, player.posY, player.posZ, packet.getYaw(player.rotationYaw), packet.getPitch(player.rotationPitch));
|
// player.rotationYaw, player.rotationPitch); Why would the server revert movements
|
||||||
server.getPlayerList().serverUpdateMovingPlayer(player);
|
// after a teleport just because the client didn't confirm it? I'm assuming that they
|
||||||
} else {
|
// intended to send another updated move packet:
|
||||||
double oldX = player.posX;
|
setPlayerLocation(player.posX, player.posY, player.posZ, player.rotationYaw, player.rotationPitch);
|
||||||
double oldY = player.posY;
|
}*/
|
||||||
double oldZ = player.posZ;
|
|
||||||
double oldY2 = player.posY;
|
|
||||||
|
|
||||||
double packetX = packet.getX(player.posX);
|
// Instead, we will send another packet both here and in processConfirmTeleport if the position the client
|
||||||
double packetY = packet.getY(player.posY);
|
// was sent is no longer good (exceeds tolerance):
|
||||||
double packetZ = packet.getZ(player.posZ);
|
|
||||||
float packetYaw = packet.getYaw(player.rotationYaw);
|
|
||||||
float packetPitch = packet.getPitch(player.rotationPitch);
|
|
||||||
|
|
||||||
double xDiff = packetX - firstGoodX;
|
LOGGER.info("Player sent move packets before confirming");
|
||||||
double yDiff = packetY - firstGoodY;
|
if (targetPos.squareDistanceTo(player.posX, player.posY, player.posZ) > 1.0D) {
|
||||||
double zDiff = packetZ - firstGoodZ;
|
LOGGER.info("Distance became too big: " + targetPos + " vs. " + new Vec3d(player.posX, player.posY, player.posY));
|
||||||
double speedSq = player.motionX * player.motionX + player.motionY * player.motionY + player.motionZ * player.motionZ;
|
setPlayerLocation(player.posX, player.posY, player.posZ, player.rotationYaw, player.rotationPitch);
|
||||||
double distanceSq = xDiff * xDiff + yDiff * yDiff + zDiff * zDiff;
|
}
|
||||||
|
|
||||||
if (player.isPlayerSleeping()) {
|
// Ignore the packet, the client thinks it's still at the old position
|
||||||
if (distanceSq > 1.0D) {
|
return;
|
||||||
setPlayerLocation(player.posX, player.posY, player.posZ, packet.getYaw(player.rotationYaw), packet.getPitch(player.rotationPitch));
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
++movePacketCounter;
|
|
||||||
int packetCount = movePacketCounter - lastMovePacketCounter;
|
|
||||||
|
|
||||||
if (packetCount > 5) {
|
lastPositionUpdate = networkTickCount;
|
||||||
LOGGER.debug("{} is sending move packets too frequently ({} packets since last tick)", player.getName(), packetCount);
|
|
||||||
packetCount = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!player.isInvulnerableDimensionChange() && (!player.getServerWorld().getGameRules().getBoolean("disableElytraMovementCheck") || !player.isElytraFlying())) {
|
if (player.isRiding()) {
|
||||||
float maxDistancePerTic = player.isElytraFlying() ? 300.0F : 100.0F;
|
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()))) {
|
double packetX = packet.getX(player.posX);
|
||||||
LOGGER.warn("{} moved too quickly! {},{},{}", player.getName(), xDiff, yDiff, zDiff);
|
double packetY = packet.getY(player.posY);
|
||||||
setPlayerLocation(player.posX, player.posY, player.posZ, player.rotationYaw, player.rotationPitch);
|
double packetZ = packet.getZ(player.posZ);
|
||||||
return;
|
float packetYaw = packet.getYaw(player.rotationYaw);
|
||||||
}
|
float packetPitch = packet.getPitch(player.rotationPitch);
|
||||||
}
|
|
||||||
|
|
||||||
boolean notInsideBlock = world.getCollisionBoxes(player, player.getEntityBoundingBox().shrink(0.0625D)).isEmpty();
|
// Calculate difference from last tick
|
||||||
xDiff = packetX - lastGoodX;
|
double xDiff = packetX - firstGoodX;
|
||||||
yDiff = packetY - lastGoodY;
|
double yDiff = packetY - firstGoodY;
|
||||||
zDiff = packetZ - lastGoodZ;
|
double zDiff = packetZ - firstGoodZ;
|
||||||
|
double distanceSq = xDiff * xDiff + yDiff * yDiff + zDiff * zDiff;
|
||||||
|
|
||||||
if (player.onGround && !packet.isOnGround() && yDiff > 0.0D) {
|
double motionSq = player.motionX * player.motionX + player.motionY * player.motionY + player.motionZ * player.motionZ;
|
||||||
player.jump();
|
|
||||||
}
|
|
||||||
|
|
||||||
player.move(MoverType.PLAYER, xDiff, yDiff, zDiff);
|
if (player.isPlayerSleeping()) {
|
||||||
player.onGround = packet.isOnGround();
|
if (distanceSq > 1.0D) {
|
||||||
double oldYDiff = yDiff;
|
// 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;
|
if (packetCount > 5) {
|
||||||
yDiff = packetY - player.posY;
|
LOGGER.debug("{} is sending move packets too frequently ({} packets since last tick)", player.getName(), packetCount);
|
||||||
if (yDiff > -0.5D || yDiff < 0.5D) { // TODO: But why?
|
packetCount = 1;
|
||||||
yDiff = 0.0D;
|
}
|
||||||
}
|
|
||||||
zDiff = packetZ - player.posZ;
|
|
||||||
distanceSq = xDiff * xDiff + yDiff * yDiff + zDiff * zDiff;
|
|
||||||
|
|
||||||
boolean movedWrongly = false;
|
// Fix: Disable isInvulnerableDimensionChange, not necessary if we don't revert position after teleport anymore
|
||||||
if (!player.isInvulnerableDimensionChange() && distanceSq > 0.0625D && !player.isPlayerSleeping() && !player.interactionManager.isCreative() && player.interactionManager.getGameType() != GameType.SPECTATOR) {
|
if (/*!player.isInvulnerableDimensionChange() && */!(player.getServerWorld().getGameRules().getBoolean("disableElytraMovementCheck") && player.isElytraFlying())) {
|
||||||
movedWrongly = true;
|
// Not really anti-cheat. Max speed would be 447 blocks/second with 5 packets/tick... Also, shouldn't
|
||||||
LOGGER.warn("{} moved wrongly!", player.getName());
|
// 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
|
if (distanceSq - motionSq > maxDistanceSqPerPacket * packetCount/* && !(server.isSinglePlayer() && server.getServerOwner().equals(player.getName()))*/) { // TODO: Single-player server owner check disabled for testing
|
||||||
//player.setPositionAndRotation(packetX, packetY, packetZ, packetYaw, packetPitch);
|
LOGGER.warn("{} moved too quickly! {},{},{}", player.getName(), xDiff, yDiff, zDiff);
|
||||||
//player.addMovementStat(player.posX - oldX, player.posY - oldY, player.posZ - oldZ);
|
setPlayerLocation(player.posX, player.posY, player.posZ, player.rotationYaw, player.rotationPitch);
|
||||||
player.addMovementStat(packetX - oldX, packetY - oldY, packetZ - oldZ);
|
return;
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
* <p>
|
||||||
|
* 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.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=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.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=Teleport Offset
|
||||||
dimdoors.general.teleportOffset.tooltip=Distance in blocks to teleport the player in front of the dimensional door.
|
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
|
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=Rift Size
|
||||||
dimdoors.graphics.riftSize.tooltip=Multiplier affecting how large rifts should be rendered, 1 being the default 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.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.
|
||||||
|
|
|
@ -109,8 +109,8 @@ dimdoors.general=Options Généralles
|
||||||
dimdoors.general.tooltip=Options de configuration généralles pour le mod
|
dimdoors.general.tooltip=Options de configuration généralles pour le mod
|
||||||
dimdoors.general.baseDimensionID=Numéro de dimension de base
|
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.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=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.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=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.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
|
dimdoors.world=Options de génération de monde
|
||||||
|
|
Loading…
Add table
Reference in a new issue