From a344ac4101af99d3dd9a4e48c3a12a6c8e989c28 Mon Sep 17 00:00:00 2001 From: ORelio Date: Fri, 10 Mar 2017 23:40:02 +0100 Subject: [PATCH] Implement more realisic fall to ground Should help moving around in servers using anti-cheat plugins. See #195 --- MinecraftClient/Mapping/Movement.cs | 62 ++++++++++++++----- MinecraftClient/McTcpClient.cs | 21 ++++--- .../Protocol/Handlers/Protocol16.cs | 2 +- .../Protocol/Handlers/Protocol18.cs | 23 +++++-- MinecraftClient/Protocol/IMinecraftCom.cs | 4 +- .../Protocol/IMinecraftComHandler.cs | 3 +- 6 files changed, 85 insertions(+), 30 deletions(-) diff --git a/MinecraftClient/Mapping/Movement.cs b/MinecraftClient/Mapping/Movement.cs index a12af234..8590899b 100644 --- a/MinecraftClient/Mapping/Movement.cs +++ b/MinecraftClient/Mapping/Movement.cs @@ -17,15 +17,25 @@ namespace MinecraftClient.Mapping /// /// World the player is currently located in /// Location the player is currently at + /// Current vertical motion speed /// Updated location after applying gravity - public static Location HandleGravity(World world, Location location) + public static Location HandleGravity(World world, Location location, ref double motionY) { Location onFoots = new Location(location.X, Math.Floor(location.Y), location.Z); Location belowFoots = Move(location, Direction.Down); + if (location.Y > Math.Truncate(location.Y) + 0.0001) + { + belowFoots = location; + belowFoots.Y = Math.Truncate(location.Y); + } if (!IsOnGround(world, location) && !IsSwimming(world, location)) - location = Move2Steps(location, belowFoots).Dequeue(); + { + while (!IsOnGround(world, belowFoots) && belowFoots.Y >= 1) + belowFoots = Move(belowFoots, Direction.Down); + location = Move2Steps(location, belowFoots, ref motionY, true).Dequeue(); + } else if (!(world.GetBlock(onFoots).Type.IsSolid())) - location = Move2Steps(location, onFoots).Dequeue(); + location = Move2Steps(location, onFoots, ref motionY, true).Dequeue(); return location; } @@ -64,25 +74,46 @@ namespace MinecraftClient.Mapping /// /// Start location /// Destination location + /// Current vertical motion speed + /// Specify if performing falling steps /// Amount of steps by block /// A list of locations corresponding to the requested steps - public static Queue Move2Steps(Location start, Location goal, int stepsByBlock = 8) + public static Queue Move2Steps(Location start, Location goal, ref double motionY, bool falling = false, int stepsByBlock = 8) { if (stepsByBlock <= 0) stepsByBlock = 1; - double totalStepsDouble = start.Distance(goal) * stepsByBlock; - int totalSteps = (int)Math.Ceiling(totalStepsDouble); - Location step = (goal - start) / totalSteps; - - if (totalStepsDouble >= 1) + if (falling) { - Queue movementSteps = new Queue(); - for (int i = 1; i <= totalSteps; i++) - movementSteps.Enqueue(start + step * i); - return movementSteps; + //Use MC-Like falling algorithm + double Y = start.Y; + Queue fallSteps = new Queue(); + fallSteps.Enqueue(start); + double motionPrev = motionY; + motionY -= 0.08D; + motionY *= 0.9800000190734863D; + Y += motionY; + if (Y < goal.Y) + return new Queue(new[] { goal }); + else return new Queue(new[] { new Location(start.X, Y, start.Z) }); + } + else + { + //Regular MCC moving algorithm + motionY = 0; //Reset motion speed + double totalStepsDouble = start.Distance(goal) * stepsByBlock; + int totalSteps = (int)Math.Ceiling(totalStepsDouble); + Location step = (goal - start) / totalSteps; + + if (totalStepsDouble >= 1) + { + Queue movementSteps = new Queue(); + for (int i = 1; i <= totalSteps; i++) + movementSteps.Enqueue(start + step * i); + return movementSteps; + } + else return new Queue(new[] { goal }); } - else return new Queue(new[] { goal }); } /// @@ -163,7 +194,8 @@ namespace MinecraftClient.Mapping /// True if the specified location is on the ground public static bool IsOnGround(World world, Location location) { - return world.GetBlock(Move(location, Direction.Down)).Type.IsSolid(); + return world.GetBlock(Move(location, Direction.Down)).Type.IsSolid() + && (location.Y <= Math.Truncate(location.Y) + 0.0001); } /// diff --git a/MinecraftClient/McTcpClient.cs b/MinecraftClient/McTcpClient.cs index f5a0b291..8374c2a3 100644 --- a/MinecraftClient/McTcpClient.cs +++ b/MinecraftClient/McTcpClient.cs @@ -37,6 +37,8 @@ namespace MinecraftClient private Queue steps; private Queue path; private Location location; + private byte[] yawpitch; + private double motionY; private string host; private int port; @@ -410,8 +412,9 @@ namespace MinecraftClient /// /// The new location /// If true, the location is relative to the current location - public void UpdateLocation(Location location) + public void UpdateLocation(Location location, byte[] yawpitch) { + this.yawpitch = yawpitch; UpdateLocation(location, false); } @@ -520,13 +523,17 @@ namespace MinecraftClient { for (int i = 0; i < 2; i++) //Needs to run at 20 tps; MCC runs at 10 tps { - if (steps != null && steps.Count > 0) - location = steps.Dequeue(); - else if (path != null && path.Count > 0) - steps = Movement.Move2Steps(location, path.Dequeue()); - else location = Movement.HandleGravity(world, location); - handler.SendLocationUpdate(location, Movement.IsOnGround(world, location)); + if (yawpitch == null) + { + if (steps != null && steps.Count > 0) + location = steps.Dequeue(); + else if (path != null && path.Count > 0) + steps = Movement.Move2Steps(location, path.Dequeue(), ref motionY); + else location = Movement.HandleGravity(world, location, ref motionY); + } + handler.SendLocationUpdate(location, Movement.IsOnGround(world, location), yawpitch); } + yawpitch = null; //First 2 updates must be player position AND look, and player must not move (to conform with vanilla) } } } diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index d79a271e..3e3a5d87 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -637,7 +637,7 @@ namespace MinecraftClient.Protocol.Handlers return false; //Currently not implemented } - public bool SendLocationUpdate(Location location, bool onGround) + public bool SendLocationUpdate(Location location, bool onGround, byte[] yawpitch) { return false; //Currently not implemented } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 0f8cee2d..a3e569e4 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -273,7 +273,7 @@ namespace MinecraftClient.Protocol.Handlers double x = readNextDouble(packetData); double y = readNextDouble(packetData); double z = readNextDouble(packetData); - readData(8, packetData); //Ignore look + byte[] yawpitch = readData(8, packetData); byte locMask = readNextByte(packetData); if (protocolversion >= MC18Version) @@ -282,9 +282,9 @@ namespace MinecraftClient.Protocol.Handlers location.X = (locMask & 1 << 0) != 0 ? location.X + x : x; location.Y = (locMask & 1 << 1) != 0 ? location.Y + y : y; location.Z = (locMask & 1 << 2) != 0 ? location.Z + z : z; - handler.UpdateLocation(location); + handler.UpdateLocation(location, yawpitch); } - else handler.UpdateLocation(new Location(x, y, z)); + else handler.UpdateLocation(new Location(x, y, z), yawpitch); if (protocolversion >= MC19Version) { @@ -1513,20 +1513,33 @@ namespace MinecraftClient.Protocol.Handlers /// /// The new location of the player /// True if the player is on the ground + /// Yaw and pitch (optional and currently not parsed) /// True if the location update was successfully sent - public bool SendLocationUpdate(Location location, bool onGround) + public bool SendLocationUpdate(Location location, bool onGround, byte[] yawpitch = null) { if (Settings.TerrainAndMovements) { + int packetId; + if (yawpitch != null && yawpitch.Length == 8) + { + packetId = protocolversion >= MC19Version ? 0x0D : 0x06; + } + else + { + yawpitch = new byte[0]; + packetId = protocolversion >= MC19Version ? 0x0C : 0x04; + } + try { - SendPacket(protocolversion >= MC19Version ? 0x0C : 0x04, concatBytes( + SendPacket(packetId, concatBytes( getDouble(location.X), getDouble(location.Y), protocolversion < MC18Version ? getDouble(location.Y + 1.62) : new byte[0], getDouble(location.Z), + yawpitch, new byte[] { onGround ? (byte)1 : (byte)0 })); return true; } diff --git a/MinecraftClient/Protocol/IMinecraftCom.cs b/MinecraftClient/Protocol/IMinecraftCom.cs index d8fa3f61..2b9b5502 100644 --- a/MinecraftClient/Protocol/IMinecraftCom.cs +++ b/MinecraftClient/Protocol/IMinecraftCom.cs @@ -71,8 +71,10 @@ namespace MinecraftClient.Protocol /// Send a location update telling that we moved to that location /// /// The new location + /// True if the player is on the ground + /// Yaw and pitch (optional and currently not parsed) /// True if packet was successfully sent - bool SendLocationUpdate(Location location, bool onGround); + bool SendLocationUpdate(Location location, bool onGround, byte[] yawpitch); /// /// Send a plugin channel packet to the server. diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index fb6b29fc..32870335 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -55,7 +55,8 @@ namespace MinecraftClient.Protocol /// Called when the server sets the new location for the player /// /// New location of the player - void UpdateLocation(Location location); + /// Yaw and pitch (optional and currently not parsed) + void UpdateLocation(Location location, byte[] yawpitch); /// /// This method is called when the connection has been lost