From 72bd485e67ee001b122ab79dd24ef4be0a8e660a Mon Sep 17 00:00:00 2001 From: ORelio Date: Fri, 27 Nov 2015 17:16:33 +0100 Subject: [PATCH] Add basic location handling - Retrieve player location from the server - Send back player location from the server - Requires that a specific setting is enabled - Should allow items to be picked up by the player - May also trigger some anti chead plugins --- MinecraftClient/ChatBot.cs | 2 +- MinecraftClient/ChatBots/AutoRelog.cs | 6 +- MinecraftClient/Mapping/Location.cs | 159 ++++++++++++++++++ MinecraftClient/McTcpClient.cs | 60 ++++++- MinecraftClient/MinecraftClient.csproj | 1 + MinecraftClient/Program.cs | 8 +- .../Protocol/Handlers/Protocol16.cs | 6 + .../Protocol/Handlers/Protocol18.cs | 66 ++++++++ MinecraftClient/Protocol/IMinecraftCom.cs | 16 +- .../Protocol/IMinecraftComHandler.cs | 9 + MinecraftClient/Settings.cs | 3 + 11 files changed, 319 insertions(+), 17 deletions(-) create mode 100644 MinecraftClient/Mapping/Location.cs diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs index 38eb3a18..a9349d72 100644 --- a/MinecraftClient/ChatBot.cs +++ b/MinecraftClient/ChatBot.cs @@ -458,7 +458,7 @@ namespace MinecraftClient protected void ReconnectToTheServer(int ExtraAttempts = 3) { - McTcpClient.AttemptsLeft = ExtraAttempts; + McTcpClient.ReconnectionAttemptsLeft = ExtraAttempts; Program.Restart(); } diff --git a/MinecraftClient/ChatBots/AutoRelog.cs b/MinecraftClient/ChatBots/AutoRelog.cs index d98e30d5..2c53f585 100644 --- a/MinecraftClient/ChatBots/AutoRelog.cs +++ b/MinecraftClient/ChatBots/AutoRelog.cs @@ -25,14 +25,14 @@ namespace MinecraftClient.ChatBots { attempts = retries; if (attempts == -1) { attempts = int.MaxValue; } - McTcpClient.AttemptsLeft = attempts; + McTcpClient.ReconnectionAttemptsLeft = attempts; delay = DelayBeforeRelog; if (delay < 1) { delay = 1; } } public override void Initialize() { - McTcpClient.AttemptsLeft = attempts; + McTcpClient.ReconnectionAttemptsLeft = attempts; if (System.IO.File.Exists(Settings.AutoRelog_KickMessagesFile)) { dictionary = System.IO.File.ReadAllLines(Settings.AutoRelog_KickMessagesFile); @@ -55,7 +55,7 @@ namespace MinecraftClient.ChatBots { LogToConsole("Waiting " + delay + " seconds before reconnecting..."); System.Threading.Thread.Sleep(delay * 1000); - McTcpClient.AttemptsLeft = attempts; + McTcpClient.ReconnectionAttemptsLeft = attempts; ReconnectToTheServer(); return true; } diff --git a/MinecraftClient/Mapping/Location.cs b/MinecraftClient/Mapping/Location.cs new file mode 100644 index 00000000..22145ad5 --- /dev/null +++ b/MinecraftClient/Mapping/Location.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Mapping +{ + /// + /// Represents a location into a Minecraft world + /// + public struct Location + { + /// + /// The X Coordinate + /// + public double X; + + /// + /// The Y Coordinate (vertical) + /// + public double Y; + + /// + /// The Z coordinate + /// + public double Z; + + /// + /// Get location with zeroed coordinates + /// + public static Location Zero + { + get + { + return new Location(0, 0, 0); + } + } + + /// + /// Create a new location + /// + public Location(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + + /// + /// Compare two locations. Locations are equals if the integer part of their coordinates are equals. + /// + /// Object to compare to + /// TRUE if the locations are equals + public override bool Equals(object obj) + { + if (obj == null) + return false; + if (obj is Location) + { + return ((int)this.X) == ((int)((Location)obj).X) + && ((int)this.Y) == ((int)((Location)obj).Y) + && ((int)this.Z) == ((int)((Location)obj).Z); + } + return false; + } + + /// + /// Get a representation of the location as unsigned long + /// + /// + /// A modulo will be applied if the location is outside the following ranges: + /// X: -33,554,432 to +33,554,431 + /// Y: -2,048 to +2,047 + /// Z: -33,554,432 to +33,554,431 + /// + /// Location representation as ulong + + public ulong GetLongRepresentation() + { + return ((((ulong)X) & 0x3FFFFFF) << 38) | ((((ulong)Y) & 0xFFF) << 26) | (((ulong)Z) & 0x3FFFFFF); + } + + /// + /// Get a location from an unsigned long. + /// + /// Location represented by the ulong + + public static Location FromLongRepresentation(ulong location) + { + return new Location(location >> 38, (location >> 26) & 0xFFF, location << 38 >> 38); + } + + /// + /// Compare two locations. Locations are equals if the integer part of their coordinates are equals. + /// + /// First location to compare + /// Second location to compare + /// TRUE if the locations are equals + public static bool operator == (Location loc1, Location loc2) + { + if (loc1 == null && loc2 == null) + return true; + if (loc1 == null || loc2 == null) + return false; + return loc1.Equals(loc2); + } + + /// + /// Compare two locations. Locations are not equals if the integer part of their coordinates are not equals. + /// + /// First location to compare + /// Second location to compare + /// TRUE if the locations are equals + public static bool operator != (Location loc1, Location loc2) + { + if (loc1 == null && loc2 == null) + return true; + if (loc1 == null || loc2 == null) + return false; + return !loc1.Equals(loc2); + } + + /// + /// Sums two locations and returns the result. + /// + /// + /// Thrown if one of the provided location is null + /// + /// First location to sum + /// Second location to sum + /// Sum of the two locations + public static Location operator + (Location loc1, Location loc2) + { + return new Location + ( + loc1.X + loc2.X, + loc1.Y + loc2.Y, + loc1.Z + loc2.Z + ); + } + + /// + /// DO NOT USE. Defined to comply with C# requirements requiring a GetHashCode() when overriding Equals() or == + /// + /// + /// A modulo will be applied if the location is outside the following ranges: + /// X: -4096 to +4095 + /// Y: -32 to +31 + /// Z: -4096 to +4095 + /// + /// A simplified version of the location + public override int GetHashCode() + { + return (((int)X) & ~((~0) << 13)) << 19 + | (((int)Y) & ~((~0) << 13)) << 13 + | (((int)Z) & ~((~0) << 06)) << 00; + } + } +} diff --git a/MinecraftClient/McTcpClient.cs b/MinecraftClient/McTcpClient.cs index e1e5c6bd..66e8bc8a 100644 --- a/MinecraftClient/McTcpClient.cs +++ b/MinecraftClient/McTcpClient.cs @@ -9,6 +9,7 @@ using System.Net; using MinecraftClient.Protocol; using MinecraftClient.Proxy; using MinecraftClient.Protocol.Handlers.Forge; +using MinecraftClient.Mapping; namespace MinecraftClient { @@ -18,16 +19,20 @@ namespace MinecraftClient public class McTcpClient : IMinecraftComHandler { - private static List cmd_names = new List(); - private static Dictionary cmds = new Dictionary(); - private List bots = new List(); + public static int ReconnectionAttemptsLeft = 0; + + private static readonly List cmd_names = new List(); + private static readonly Dictionary cmds = new Dictionary(); private readonly Dictionary onlinePlayers = new Dictionary(); - private static List scripts_on_hold = new List(); + + private readonly List bots = new List(); + private static readonly List scripts_on_hold = new List(); public void BotLoad(ChatBot b) { b.SetHandler(this); bots.Add(b); b.Initialize(); Settings.SingleCommand = ""; } public void BotUnLoad(ChatBot b) { bots.RemoveAll(item => object.ReferenceEquals(item, b)); } public void BotClear() { bots.Clear(); } - public static int AttemptsLeft = 0; + private Location location; + private int updateTicks = 0; private string host; private int port; @@ -40,6 +45,7 @@ namespace MinecraftClient public string GetUsername() { return username; } public string GetUserUUID() { return uuid; } public string GetSessionID() { return sessionid; } + public Location GetCurrentLocation() { return location; } TcpClient client; IMinecraftCom handler; @@ -162,10 +168,10 @@ namespace MinecraftClient if (retry) { - if (AttemptsLeft > 0) + if (ReconnectionAttemptsLeft > 0) { - ConsoleIO.WriteLogLine("Waiting 5 seconds (" + AttemptsLeft + " attempts left)..."); - Thread.Sleep(5000); AttemptsLeft--; Program.Restart(); + ConsoleIO.WriteLogLine("Waiting 5 seconds (" + ReconnectionAttemptsLeft + " attempts left)..."); + Thread.Sleep(5000); ReconnectionAttemptsLeft--; Program.Restart(); } else if (!singlecommand && Settings.interactiveMode) { @@ -330,6 +336,34 @@ namespace MinecraftClient handler.SendBrandInfo(Settings.BrandInfo.Trim()); } + /// + /// Called when the server sends a new player location, + /// or if a ChatBot whishes to update the player's location. + /// + /// The new location + /// If true, the location is relative to the current location + + public void UpdateLocation(Location location, bool relative) + { + if (relative) + { + this.location += location; + } + else this.location = location; + } + + /// + /// Called when the server sends a new player location, + /// or if a ChatBot whishes to update the player's location. + /// + /// The new location + /// If true, the location is relative to the current location + + public void UpdateLocation(Location location) + { + UpdateLocation(location, false); + } + /// /// Received some text from the server /// @@ -409,6 +443,16 @@ namespace MinecraftClient else throw; //ThreadAbortException should not be caught } } + + if (Settings.TerrainAndMovements) + { + if (updateTicks >= 10) + { + handler.SendLocationUpdate(location, true); //TODO handle onGround once terrain data is available + updateTicks = 0; + } + updateTicks++; + } } /// diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 9c09a5e1..b9a92e7e 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -152,6 +152,7 @@ + diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index 9e447531..12916186 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -19,7 +19,11 @@ namespace MinecraftClient { private static McTcpClient Client; public static string[] startupargs; + public const string Version = "1.8.2"; + public const string MCLowestVersion = "1.4.6"; + public const string MCHighestVersion = "1.8.8"; + private static Thread offlinePrompt = null; private static bool useMcVersionOnce = false; @@ -29,7 +33,7 @@ namespace MinecraftClient static void Main(string[] args) { - Console.WriteLine("Console Client for MC 1.4.6 to 1.8.8 - v" + Version + " - By ORelio & Contributors"); + Console.WriteLine("Console Client for MC {0} to {1} - v{2} - By ORelio & Contributors", MCLowestVersion, MCHighestVersion, Version); //Basic Input/Output ? if (args.Length >= 1 && args[args.Length - 1] == "BasicIO") @@ -137,6 +141,8 @@ namespace MinecraftClient ConsoleIcon.setPlayerIconAsync(Settings.Username); Console.WriteLine("Success. (session ID: " + sessionID + ')'); + + //ProtocolHandler.RealmsListWorlds(Settings.Username, UUID, sessionID); //TODO REMOVE if (Settings.ServerIP == "") { diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index 89cedbe6..07ee63dd 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -7,6 +7,7 @@ using System.Threading; using MinecraftClient.Crypto; using MinecraftClient.Proxy; using System.Security.Cryptography; +using MinecraftClient.Mapping; namespace MinecraftClient.Protocol.Handlers { @@ -615,6 +616,11 @@ namespace MinecraftClient.Protocol.Handlers return false; //Only supported since MC 1.7 } + public bool SendLocationUpdate(Location location, bool onGround) + { + return false; //Currently not implemented + } + /// /// Send a plugin channel packet to the server. /// diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index a9631206..ddb8771b 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -8,6 +8,7 @@ using MinecraftClient.Crypto; using MinecraftClient.Proxy; using System.Security.Cryptography; using MinecraftClient.Protocol.Handlers.Forge; +using MinecraftClient.Mapping; namespace MinecraftClient.Protocol.Handlers { @@ -162,6 +163,23 @@ namespace MinecraftClient.Protocol.Handlers catch (IndexOutOfRangeException) { /* No message type */ } handler.OnTextReceived(ChatParser.ParseText(message)); break; + case 0x08: + if (Settings.TerrainAndMovements) + { + double x = readNextDouble(ref packetData); + double y = readNextDouble(ref packetData); + double z = readNextDouble(ref packetData); + + byte locMask = readNextByte(ref packetData); + Location location = handler.GetCurrentLocation(); + + 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); + } + break; case 0x38: //Player List update if (protocolversion >= MC18Version) { @@ -509,6 +527,18 @@ namespace MinecraftClient.Protocol.Handlers return readData(len, ref cache); } + /// + /// Read a double from a cache of bytes and remove it from the cache + /// + /// The double value + + private static double readNextDouble(ref byte[] cache) + { + byte[] rawValue = readData(8, ref cache); + Array.Reverse(rawValue); //Endianness + return BitConverter.ToDouble(rawValue, 0); + } + /// /// Read an integer from the network /// @@ -602,6 +632,19 @@ namespace MinecraftClient.Protocol.Handlers return bytes.ToArray(); } + /// + /// Get byte array representing a double + /// + /// Array to process + /// Array ready to send + + private byte[] getDouble(double number) + { + byte[] theDouble = BitConverter.GetBytes(number); + Array.Reverse(theDouble); //Endianness + return theDouble; + } + /// /// Get byte array with length information prepended to it /// @@ -931,6 +974,29 @@ namespace MinecraftClient.Protocol.Handlers return SendPluginChannelPacket("MC|Brand", getString(brandInfo)); } + /// + /// Send a location update to the server + /// + /// The new location of the player + /// True if the player is on the ground + /// True if the location update was successfully sent + + public bool SendLocationUpdate(Location location, bool onGround) + { + if (Settings.TerrainAndMovements) + { + try + { + SendPacket(0x04, concatBytes( + getDouble(location.X), getDouble(location.X), getDouble(location.X), + new byte[] { onGround ? (byte)1 : (byte)0 })); + return true; + } + catch (SocketException) { return false; } + } + else return false; + } + /// /// Send a plugin channel packet (0x17) to the server, compression and encryption will be handled automatically /// diff --git a/MinecraftClient/Protocol/IMinecraftCom.cs b/MinecraftClient/Protocol/IMinecraftCom.cs index 218f49d4..927258fd 100644 --- a/MinecraftClient/Protocol/IMinecraftCom.cs +++ b/MinecraftClient/Protocol/IMinecraftCom.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using MinecraftClient.Crypto; +using MinecraftClient.Mapping; namespace MinecraftClient.Protocol { @@ -45,7 +46,7 @@ namespace MinecraftClient.Protocol bool SendRespawnPacket(); /// - /// Tell the server what client is being used to connect to the server + /// Inform the server of the client being used to connect /// /// Client string describing the client /// True if brand info was successfully sent @@ -53,10 +54,17 @@ namespace MinecraftClient.Protocol bool SendBrandInfo(string brandInfo); /// - /// Send a plugin channel packet to the server. - /// - /// http://dinnerbone.com/blog/2012/01/13/minecraft-plugin-channels-messaging/ + /// Send a location update telling that we moved to that location /// + /// The new location + /// True if packet was successfully sent + + bool SendLocationUpdate(Location location, bool onGround); + + /// + /// Send a plugin channel packet to the server. + /// + /// /// Channel to send packet on /// packet Data /// True if message was successfully sent diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index 606e80ea..5376e31a 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using MinecraftClient.Mapping; namespace MinecraftClient.Protocol { @@ -22,6 +23,7 @@ namespace MinecraftClient.Protocol string GetUserUUID(); string GetSessionID(); string[] GetOnlinePlayers(); + Location GetCurrentLocation(); /// /// Called when a server was successfully joined @@ -50,6 +52,13 @@ namespace MinecraftClient.Protocol void OnPlayerLeave(Guid uuid); + /// + /// Called when the server sets the new location for the player + /// + /// New location of the player + + void UpdateLocation(Location location); + /// /// This method is called when the connection has been lost /// diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index c0119ff1..a0c6fc5d 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -54,6 +54,7 @@ namespace MinecraftClient public static string BrandInfo = MCCBrandInfo; public static bool DisplaySystemMessages = true; public static bool DisplayXPBarMessages = true; + public static bool TerrainAndMovements = false; //AntiAFK Settings public static bool AntiAFK_Enabled = false; @@ -178,6 +179,7 @@ namespace MinecraftClient case "scriptcache": CacheScripts = str2bool(argValue); break; case "showsystemmessages": DisplaySystemMessages = str2bool(argValue); break; case "showxpbarmessages": DisplayXPBarMessages = str2bool(argValue); break; + case "handleterrainandmovements": TerrainAndMovements = str2bool(argValue); break; case "botowners": Bots_Owners.Clear(); @@ -407,6 +409,7 @@ namespace MinecraftClient + "chatbotlogfile= #leave empty for no logfile\r\n" + "showsystemmessages=true #system messages for server ops\r\n" + "showxpbarmessages=true #messages displayed above xp bar\r\n" + + "handleterrainandmovements=false #requires more ram and cpu\r\n" + "accountlist=accounts.txt\r\n" + "serverlist=servers.txt\r\n" + "playerheadicon=true\r\n"