From 9a98a9d46f93ccfd91c720d956cd8dd45b440e2c Mon Sep 17 00:00:00 2001 From: ORelio Date: Mon, 22 Aug 2016 19:09:43 +0200 Subject: [PATCH] Store extended player info, list display names - Add 1.10.1 and 1.10.2 in supported version list - Store both player name and player display names - List command will sort players by player name - List command will now display by display name - Ability to use /list raw to display by real name Suggestion by Johngreen123 --- MinecraftClient/Commands/List.cs | 11 ++- MinecraftClient/McTcpClient.cs | 92 +++++++++---------- MinecraftClient/MinecraftClient.csproj | 1 + MinecraftClient/Program.cs | 10 +- .../Protocol/Handlers/Protocol16.cs | 2 +- .../Protocol/Handlers/Protocol18.cs | 7 +- .../Protocol/IMinecraftComHandler.cs | 17 +--- MinecraftClient/Protocol/PlayerInfo.cs | 86 +++++++++++++++++ MinecraftClient/Protocol/ProtocolHandler.cs | 13 ++- MinecraftClient/config/README.txt | 2 +- 10 files changed, 156 insertions(+), 85 deletions(-) create mode 100644 MinecraftClient/Protocol/PlayerInfo.cs diff --git a/MinecraftClient/Commands/List.cs b/MinecraftClient/Commands/List.cs index da9368e0..f660e039 100644 --- a/MinecraftClient/Commands/List.cs +++ b/MinecraftClient/Commands/List.cs @@ -8,11 +8,18 @@ namespace MinecraftClient.Commands public class List : Command { public override string CMDName { get { return "list"; } } - public override string CMDDesc { get { return "list: get the player list."; } } + public override string CMDDesc { get { return "list [raw]: get the player list."; } } public override string Run(McTcpClient handler, string command) { - return "PlayerList: " + String.Join(", ", handler.GetOnlinePlayers()); + bool rawNames = getArg(command).ToLower() == "raw"; + return "PlayerList: " + + String.Join(", ", + handler.GetOnlinePlayers() + .OrderBy(player => player.Name) + .Select(player => rawNames + ? player.Name + : player.DisplayName)); } } } diff --git a/MinecraftClient/McTcpClient.cs b/MinecraftClient/McTcpClient.cs index f50baecd..fb26c08a 100644 --- a/MinecraftClient/McTcpClient.cs +++ b/MinecraftClient/McTcpClient.cs @@ -23,31 +23,10 @@ namespace MinecraftClient private static readonly List cmd_names = new List(); private static readonly Dictionary cmds = new Dictionary(); - private readonly Dictionary onlinePlayers = new Dictionary(); + private readonly Dictionary onlinePlayers = new Dictionary(); 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(); - if (this.handler != null) - { - b.AfterGameJoined(); - } - Settings.SingleCommand = ""; - } - public void BotUnLoad(ChatBot b) { - bots.RemoveAll(item => object.ReferenceEquals(item, b)); - - // ToList is needed to avoid an InvalidOperationException from modfiying the list while it's being iterated upon. - var botRegistrations = registeredBotPluginChannels.Where(entry => entry.Value.Contains(b)).ToList(); - foreach (var entry in botRegistrations) - { - UnregisterPluginChannel(entry.Key, b); - } - } - public void BotClear() { bots.Clear(); } private readonly Dictionary> registeredBotPluginChannels = new Dictionary>(); private readonly List registeredServerPluginChannels = new List(); @@ -86,7 +65,6 @@ namespace MinecraftClient /// The server IP /// The server port to use /// Minecraft protocol version to use - public McTcpClient(string username, string uuid, string sessionID, int protocolversion, ForgeInfo forgeInfo, string server_ip, ushort port) { StartClient(username, uuid, sessionID, server_ip, port, protocolversion, forgeInfo, false, ""); @@ -102,7 +80,6 @@ namespace MinecraftClient /// The server port to use /// Minecraft protocol version to use /// The text or command to send. - public McTcpClient(string username, string uuid, string sessionID, string server_ip, ushort port, int protocolversion, ForgeInfo forgeInfo, string command) { StartClient(username, uuid, sessionID, server_ip, port, protocolversion, forgeInfo, true, command); @@ -119,7 +96,6 @@ namespace MinecraftClient /// The player's UUID for online-mode authentication /// If set to true, the client will send a single command and then disconnect from the server /// The text or command to send. Will only be sent if singlecommand is set to true. - private void StartClient(string user, string uuid, string sessionID, string server_ip, ushort port, int protocolversion, ForgeInfo forgeInfo, bool singlecommand, string command) { bool retry = false; @@ -209,7 +185,6 @@ namespace MinecraftClient /// /// Allows the user to send chat messages, commands, and to leave the server. /// - private void CommandPrompt() { try @@ -267,7 +242,6 @@ namespace MinecraftClient /// Set to true if command was sent by the user using the command prompt /// May contain a confirmation or error message after processing the command, or "" otherwise. /// TRUE if the command was indeed an internal MCC command - public bool PerformInternalCommand(string command, ref string response_msg) { /* Load commands from the 'Commands' namespace */ @@ -330,7 +304,6 @@ namespace MinecraftClient /// /// Disconnect the client from the server /// - public void Disconnect() { foreach (ChatBot bot in bots) @@ -352,10 +325,47 @@ namespace MinecraftClient client.Close(); } + /// + /// Load a new bot + /// + public void BotLoad(ChatBot b) + { + b.SetHandler(this); + bots.Add(b); + b.Initialize(); + if (this.handler != null) + { + b.AfterGameJoined(); + } + Settings.SingleCommand = ""; + } + + /// + /// Unload a bot + /// + public void BotUnLoad(ChatBot b) + { + bots.RemoveAll(item => object.ReferenceEquals(item, b)); + + // ToList is needed to avoid an InvalidOperationException from modfiying the list while it's being iterated upon. + var botRegistrations = registeredBotPluginChannels.Where(entry => entry.Value.Contains(b)).ToList(); + foreach (var entry in botRegistrations) + { + UnregisterPluginChannel(entry.Key, b); + } + } + + /// + /// Clear bots + /// + public void BotClear() + { + bots.Clear(); + } + /// /// Called when a server was successfully joined /// - public void OnGameJoined() { if (!String.IsNullOrWhiteSpace(Settings.BrandInfo)) @@ -370,7 +380,6 @@ namespace MinecraftClient /// /// The new location /// If true, the location is relative to the current location - public void UpdateLocation(Location location, bool relative) { lock (locationLock) @@ -390,7 +399,6 @@ namespace MinecraftClient /// /// The new location /// If true, the location is relative to the current location - public void UpdateLocation(Location location) { UpdateLocation(location, false); @@ -417,7 +425,6 @@ namespace MinecraftClient /// Received some text from the server /// /// Text received - public void OnTextReceived(string text) { ConsoleIO.WriteLineFormatted(text, false); @@ -441,7 +448,6 @@ namespace MinecraftClient /// /// When connection has been lost /// - public void OnConnectionLost(ChatBot.DisconnectReason reason, string message) { bool will_restart = false; @@ -474,7 +480,6 @@ namespace MinecraftClient /// /// Called ~10 times per second by the protocol handler /// - public void OnUpdate() { foreach (var bot in bots.ToArray()) @@ -516,7 +521,6 @@ namespace MinecraftClient /// /// Text to send to the server /// True if the text was sent with no error - public bool SendText(string text) { if (text.Length > 100) //Message is too long? @@ -547,7 +551,6 @@ namespace MinecraftClient /// Allow to respawn after death /// /// True if packet successfully sent - public bool SendRespawnPacket() { return handler.SendRespawnPacket(); @@ -557,17 +560,16 @@ namespace MinecraftClient /// Triggered when a new player joins the game /// /// UUID of the player - /// Name of the player - - public void OnPlayerJoin(Guid uuid, string name) + /// Info about this player + public void OnPlayerJoin(PlayerInfo info) { //Ignore TabListPlus placeholders - if (name.StartsWith("0000tab#")) + if (info.Name.StartsWith("0000tab#")) return; lock (onlinePlayers) { - onlinePlayers[uuid] = name; + onlinePlayers[info.UUID] = info; } } @@ -575,7 +577,6 @@ namespace MinecraftClient /// Triggered when a player has left the game /// /// UUID of the player - public void OnPlayerLeave(Guid uuid) { lock (onlinePlayers) @@ -588,8 +589,7 @@ namespace MinecraftClient /// Get a set of online player names /// /// Online player names - - public string[] GetOnlinePlayers() + public PlayerInfo[] GetOnlinePlayers() { lock (onlinePlayers) { @@ -602,7 +602,6 @@ namespace MinecraftClient /// /// The channel to register. /// The bot to register the channel for. - public void RegisterPluginChannel(string channel, ChatBot bot) { if (registeredBotPluginChannels.ContainsKey(channel)) @@ -623,7 +622,6 @@ namespace MinecraftClient /// /// The channel to unregister. /// The bot to unregister the channel for. - public void UnregisterPluginChannel(string channel, ChatBot bot) { if (registeredBotPluginChannels.ContainsKey(channel)) @@ -646,7 +644,6 @@ namespace MinecraftClient /// The payload for the packet. /// Whether the packet should be sent even if the server or the client hasn't registered it yet. /// Whether the packet was sent: true if it was sent, false if there was a connection error or it wasn't registered. - public bool SendPluginChannelMessage(string channel, byte[] data, bool sendEvenIfNotRegistered = false) { if (!sendEvenIfNotRegistered) @@ -668,7 +665,6 @@ namespace MinecraftClient /// /// The channel the message was sent on /// The data from the channel - public void OnPluginChannelMessage(string channel, byte[] data) { if (channel == "REGISTER") diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 35a9ff85..d3934de4 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -148,6 +148,7 @@ + diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index 4517542d..1e3b38f8 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -15,7 +15,6 @@ namespace MinecraftClient /// Allows to connect to any Minecraft server, send and receive text, automated scripts. /// This source code is released under the CDDL 1.0 License. /// - static class Program { private static McTcpClient Client; @@ -23,7 +22,7 @@ namespace MinecraftClient public const string Version = "1.10.0 DEV"; public const string MCLowestVersion = "1.4.6"; - public const string MCHighestVersion = "1.10.0"; + public const string MCHighestVersion = "1.10.2"; private static Thread offlinePrompt = null; private static bool useMcVersionOnce = false; @@ -31,7 +30,6 @@ namespace MinecraftClient /// /// The main entry point of Minecraft Console Client /// - static void Main(string[] args) { Console.WriteLine("Console Client for MC {0} to {1} - v{2} - By ORelio & Contributors", MCLowestVersion, MCHighestVersion, Version); @@ -129,7 +127,6 @@ namespace MinecraftClient /// /// Start a new Client /// - private static void InitializeClient() { SessionToken session = new SessionToken(); @@ -271,7 +268,6 @@ namespace MinecraftClient /// /// Disconnect the current client from the server and restart it /// - public static void Restart() { new Thread(new ThreadStart(delegate @@ -286,7 +282,6 @@ namespace MinecraftClient /// /// Disconnect the current client from the server and exit the app /// - public static void Exit() { new Thread(new ThreadStart(delegate @@ -305,7 +300,6 @@ namespace MinecraftClient /// Error message to display and optionally pass to AutoRelog bot /// Specify if the error is related to an incompatible or unkown server version /// If set, the error message will be processed by the AutoRelog bot - public static void HandleFailure(string errorMessage = null, bool versionError = false, ChatBots.AutoRelog.DisconnectReason? disconnectReason = null) { if (!String.IsNullOrEmpty(errorMessage)) @@ -387,7 +381,6 @@ namespace MinecraftClient /// /// Detect if the user is running Minecraft Console Client through Mono /// - public static bool isUsingMono { get @@ -402,7 +395,6 @@ namespace MinecraftClient /// Namespace to process /// Assembly to use. Default is Assembly.GetExecutingAssembly() /// - public static Type[] GetTypesInNamespace(string nameSpace, Assembly assembly = null) { if (assembly == null) { assembly = Assembly.GetExecutingAssembly(); } diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index c5f002c3..77e21dfc 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -160,7 +160,7 @@ namespace MinecraftClient.Protocol.Handlers case 0xC9: string name = readNextString(); bool online = readNextByte() != 0x00; readData(2); Guid FakeUUID = new Guid(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray()); - if (online) { handler.OnPlayerJoin(FakeUUID, name); } else { handler.OnPlayerLeave(FakeUUID); } + if (online) { handler.OnPlayerJoin(new PlayerInfo(FakeUUID, name)); } else { handler.OnPlayerLeave(FakeUUID); } break; case 0xCA: if (protocolversion >= 72) { readData(9); } else readData(3); break; case 0xCB: autocomplete_result = readNextString(); autocomplete_received = true; break; diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 4c057896..888dc813 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -437,9 +437,10 @@ namespace MinecraftClient.Protocol.Handlers } readNextVarInt(packetData); readNextVarInt(packetData); + string displayName = name; if (readNextBool(packetData)) - readNextString(packetData); - handler.OnPlayerJoin(uuid, name); + displayName = readNextString(packetData); + handler.OnPlayerJoin(new PlayerInfo(uuid, name, displayName)); break; case 0x01: //Update gamemode case 0x02: //Update latency @@ -465,7 +466,7 @@ namespace MinecraftClient.Protocol.Handlers short ping = readNextShort(packetData); Guid FakeUUID = new Guid(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray()); if (online) - handler.OnPlayerJoin(FakeUUID, name); + handler.OnPlayerJoin(new PlayerInfo(FakeUUID, name)); else handler.OnPlayerLeave(FakeUUID); } break; diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index dae4b516..fd0f9e63 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -22,55 +22,48 @@ namespace MinecraftClient.Protocol string GetUsername(); string GetUserUUID(); string GetSessionID(); - string[] GetOnlinePlayers(); + PlayerInfo[] GetOnlinePlayers(); Location GetCurrentLocation(); World GetWorld(); /// /// Called when a server was successfully joined /// - void OnGameJoined(); /// /// This method is called when the protocol handler receives a chat message /// - void OnTextReceived(string text); /// /// This method is called when a new player joins the game /// /// UUID of the player - /// Name of the player - - void OnPlayerJoin(Guid uuid, string name); + /// Info about this player + void OnPlayerJoin(PlayerInfo info); /// /// This method is called when a player has left the game /// /// UUID of the player - 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 /// - void OnConnectionLost(ChatBot.DisconnectReason reason, string message); /// /// Called ~10 times per second (10 ticks per second) /// Useful for updating bots in other parts of the program /// - void OnUpdate(); /// @@ -78,7 +71,6 @@ namespace MinecraftClient.Protocol /// /// The channel to register. /// The bot to register the channel for. - void RegisterPluginChannel(string channel, ChatBot bot); /// @@ -86,7 +78,6 @@ namespace MinecraftClient.Protocol /// /// The channel to unregister. /// The bot to unregister the channel for. - void UnregisterPluginChannel(string channel, ChatBot bot); /// @@ -97,7 +88,6 @@ namespace MinecraftClient.Protocol /// The payload for the packet. /// Whether the packet should be sent even if the server or the client hasn't registered it yet. /// Whether the packet was sent: true if it was sent, false if there was a connection error or it wasn't registered. - bool SendPluginChannelMessage(string channel, byte[] data, bool sendEvenIfNotRegistered = false); /// @@ -105,7 +95,6 @@ namespace MinecraftClient.Protocol /// /// The channel the message was sent on /// The data from the channel - void OnPluginChannelMessage(string channel, byte[] data); } } diff --git a/MinecraftClient/Protocol/PlayerInfo.cs b/MinecraftClient/Protocol/PlayerInfo.cs new file mode 100644 index 00000000..86a506a3 --- /dev/null +++ b/MinecraftClient/Protocol/PlayerInfo.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Protocol +{ + /// + /// Information about a player (player tab list item) + /// + public struct PlayerInfo : IComparable + { + private Guid _uuid; + private string _name; + private string _displayName; + + public Guid UUID { get { return _uuid; } } + public string Name { get { return _name; } } + public string DisplayName { get { return _displayName; } } + + /// + /// Create a new PlayerInfo structure + /// + /// Player Id + /// Player Name + public PlayerInfo(Guid uuid, string name) + { + _uuid = uuid; + _name = name; + _displayName = name; + } + + /// + /// Create a new PlayerInfo structure + /// + /// Player Id + /// Player Name + /// Player Display Name + public PlayerInfo(Guid uuid, string name, string displayName) + : this(uuid, name) + { + _displayName = displayName; + } + + /// + /// String representation of the player + /// + /// Player display name + public override string ToString() + { + return DisplayName; + } + + /// + /// Compare a player to another player + /// + /// Other player + /// TRUE if same player Id + public override bool Equals(object obj) + { + if (obj is PlayerInfo) + return UUID.Equals(((PlayerInfo)obj).UUID); + return base.Equals(obj); + } + + /// + /// Basic hash function for player, from Player Id + /// + /// Interger hash + /// Required when overriding Equals() + public override int GetHashCode() + { + return UUID.GetHashCode(); + } + + /// + /// Allows sorting players by name + /// + /// Other player to compare to + /// Comparition with the player's name + int IComparable.CompareTo(PlayerInfo obj) + { + return Name.CompareTo(obj.Name); + } + } +} diff --git a/MinecraftClient/Protocol/ProtocolHandler.cs b/MinecraftClient/Protocol/ProtocolHandler.cs index fdd2c12a..67f74d2f 100644 --- a/MinecraftClient/Protocol/ProtocolHandler.cs +++ b/MinecraftClient/Protocol/ProtocolHandler.cs @@ -96,6 +96,7 @@ namespace MinecraftClient.Protocol return 60; case "1.5.2": return 61; + case "1.6": case "1.6.0": return 72; case "1.6.1": @@ -114,6 +115,7 @@ namespace MinecraftClient.Protocol case "1.7.9": case "1.7.10": return 5; + case "1.8": case "1.8.0": case "1.8.1": case "1.8.2": @@ -125,6 +127,7 @@ namespace MinecraftClient.Protocol case "1.8.8": case "1.8.9": return 47; + case "1.9": case "1.9.0": return 107; case "1.9.1": @@ -134,7 +137,10 @@ namespace MinecraftClient.Protocol case "1.9.3": case "1.9.4": return 110; + case "1.10": case "1.10.0": + case "1.10.1": + case "1.10.2": return 210; default: return 0; @@ -234,7 +240,6 @@ namespace MinecraftClient.Protocol /// Will contain the cached access token previously returned by Minecraft.net /// Will contain the cached client token created on login /// Returns the status of the token (Valid, Invalid, etc.) - /// public static LoginResult GetTokenValidation(SessionToken session) { try @@ -269,7 +274,6 @@ namespace MinecraftClient.Protocol /// Will contain the client token generated before sending to Minecraft.net /// Will contain the player's PlayerID, needed for multiplayer /// Returns the status of the new token request (Success, Failure, etc.) - /// public static LoginResult GetNewToken(SessionToken currentsession, out SessionToken newsession) { newsession = new SessionToken(); @@ -319,7 +323,6 @@ namespace MinecraftClient.Protocol /// Session ID /// Server ID /// TRUE if session was successfully checked - public static bool SessionCheck(string uuid, string accesstoken, string serverhash) { try @@ -348,7 +351,6 @@ namespace MinecraftClient.Protocol /// Cookies for making the request /// Request result /// HTTP Status code - private static int doHTTPSGet(string host, string endpoint, string cookies, ref string result) { List http_request = new List(); @@ -372,7 +374,6 @@ namespace MinecraftClient.Protocol /// Request payload /// Request result /// HTTP Status code - private static int doHTTPSPost(string host, string endpoint, string request, ref string result) { List http_request = new List(); @@ -395,7 +396,6 @@ namespace MinecraftClient.Protocol /// Host to connect to /// Request result /// HTTP Status code - private static int doHTTPSRequest(List headers, string host, ref string result) { string postResult = null; @@ -425,7 +425,6 @@ namespace MinecraftClient.Protocol /// /// Source text /// Encoded text - private static string jsonEncode(string text) { StringBuilder result = new StringBuilder(); diff --git a/MinecraftClient/config/README.txt b/MinecraftClient/config/README.txt index f2c7035d..14f7a131 100644 --- a/MinecraftClient/config/README.txt +++ b/MinecraftClient/config/README.txt @@ -63,7 +63,7 @@ In scripts and remote control, no slash is needed to perform the command. - send : send a message or a command to the server - respawn : Use this to respawn if you are dead (like clicking "respawn" ingame) - log : display some text in the console (useful for scripts) - - list : list players logged in to the server (uses tab list info sent by server) + - list [raw] : list players logged in to the server (raw = force show real names) - set varname=value : set a value which can be used as %varname% in further commands - wait