diff --git a/MinecraftClient/McTcpClient.cs b/MinecraftClient/McTcpClient.cs index efdef818..765c866d 100644 --- a/MinecraftClient/McTcpClient.cs +++ b/MinecraftClient/McTcpClient.cs @@ -436,9 +436,13 @@ namespace MinecraftClient /// Received some text from the server /// /// Text received - public void OnTextReceived(string text) + /// Links embedded in text + public void OnTextReceived(string text, IEnumerable links) { ConsoleIO.WriteLineFormatted(text, false); + if (Settings.DisplayChatLinks) + foreach (string link in links) + ConsoleIO.WriteLineFormatted("§8MCC: Link: " + link, false); for (int i = 0; i < bots.Count; i++) { try diff --git a/MinecraftClient/Protocol/Handlers/ChatParser.cs b/MinecraftClient/Protocol/Handlers/ChatParser.cs index 5c5b6c84..c7ae79dd 100644 --- a/MinecraftClient/Protocol/Handlers/ChatParser.cs +++ b/MinecraftClient/Protocol/Handlers/ChatParser.cs @@ -15,11 +15,11 @@ namespace MinecraftClient.Protocol.Handlers /// The main function to convert text from MC 1.6+ JSON to MC 1.5.2 formatted text /// /// JSON serialized text + /// Optional container for links from JSON serialized text /// Returns the translated text - - public static string ParseText(string json) + public static string ParseText(string json, List links = null) { - return JSONData2String(Json.ParseJson(json), ""); + return JSONData2String(Json.ParseJson(json), "", links); } /// @@ -27,8 +27,7 @@ namespace MinecraftClient.Protocol.Handlers /// /// Color Name /// Color code - - private static string color2tag(string colorname) + private static string Color2tag(string colorname) { switch (colorname.ToLower()) { @@ -54,12 +53,24 @@ namespace MinecraftClient.Protocol.Handlers } /// - /// Rules for text translation + /// Specify whether translation rules have been loaded /// + private static bool RulesInitialized = false; - private static bool init = false; + /// + /// Set of translation rules for formatting text + /// private static Dictionary TranslationRules = new Dictionary(); - public static void InitTranslations() { if (!init) { InitRules(); init = true; } } + + /// + /// Initialize translation rules. + /// Necessary for properly printing some chat messages. + /// + public static void InitTranslations() { if (!RulesInitialized) { InitRules(); RulesInitialized = true; } } + + /// + /// Internal rule initialization method. Looks for local rule file or download it from Mojang asset servers. + /// private static void InitRules() { //Small default dictionnary of translation rules @@ -85,11 +96,11 @@ namespace MinecraftClient.Protocol.Handlers ConsoleIO.WriteLine("Downloading '" + Settings.Language + ".lang' from Mojang servers..."); try { - string assets_index = downloadString(Settings.TranslationsFile_Website_Index); + string assets_index = DownloadString(Settings.TranslationsFile_Website_Index); string[] tmp = assets_index.Split(new string[] { "minecraft/lang/" + Settings.Language + ".lang" }, StringSplitOptions.None); tmp = tmp[1].Split(new string[] { "hash\": \"" }, StringSplitOptions.None); string hash = tmp[1].Split('"')[0]; //Translations file identifier on Mojang's servers - System.IO.File.WriteAllText(Language_File, downloadString(Settings.TranslationsFile_Website_Download + '/' + hash.Substring(0, 2) + '/' + hash)); + System.IO.File.WriteAllText(Language_File, DownloadString(Settings.TranslationsFile_Website_Download + '/' + hash.Substring(0, 2) + '/' + hash)); ConsoleIO.WriteLine("Done. File saved as '" + Language_File + '\''); } catch @@ -140,10 +151,9 @@ namespace MinecraftClient.Protocol.Handlers /// Name of the rule, chosen by the server /// Data to be used in the rule /// Returns the formatted text according to the given data - private static string TranslateString(string rulename, List using_data) { - if (!init) { InitRules(); init = true; } + if (!RulesInitialized) { InitRules(); RulesInitialized = true; } if (TranslationRules.ContainsKey(rulename)) { int using_idx = 0; @@ -192,9 +202,9 @@ namespace MinecraftClient.Protocol.Handlers /// /// JSON object to convert /// Allow parent color code to affect child elements (set to "" for function init) + /// Container for links from JSON serialized text /// returns the Minecraft-formatted string - - private static string JSONData2String(Json.JSONData data, string colorcode) + private static string JSONData2String(Json.JSONData data, string colorcode, List links) { string extra_result = ""; switch (data.Type) @@ -202,17 +212,28 @@ namespace MinecraftClient.Protocol.Handlers case Json.JSONData.DataType.Object: if (data.Properties.ContainsKey("color")) { - colorcode = color2tag(JSONData2String(data.Properties["color"], "")); + colorcode = Color2tag(JSONData2String(data.Properties["color"], "", links)); } + if (data.Properties.ContainsKey("clickEvent") && links != null) + { + Json.JSONData clickEvent = data.Properties["clickEvent"]; + if (clickEvent.Properties.ContainsKey("action") + && clickEvent.Properties.ContainsKey("value") + && clickEvent.Properties["action"].StringValue == "open_url" + && !String.IsNullOrEmpty(clickEvent.Properties["value"].StringValue)) + { + links.Add(clickEvent.Properties["value"].StringValue); + } + } if (data.Properties.ContainsKey("extra")) { Json.JSONData[] extras = data.Properties["extra"].DataArray.ToArray(); foreach (Json.JSONData item in extras) - extra_result = extra_result + JSONData2String(item, colorcode) + "§r"; + extra_result = extra_result + JSONData2String(item, colorcode, links) + "§r"; } if (data.Properties.ContainsKey("text")) { - return colorcode + JSONData2String(data.Properties["text"], colorcode) + extra_result; + return colorcode + JSONData2String(data.Properties["text"], colorcode, links) + extra_result; } else if (data.Properties.ContainsKey("translate")) { @@ -224,10 +245,10 @@ namespace MinecraftClient.Protocol.Handlers Json.JSONData[] array = data.Properties["with"].DataArray.ToArray(); for (int i = 0; i < array.Length; i++) { - using_data.Add(JSONData2String(array[i], colorcode)); + using_data.Add(JSONData2String(array[i], colorcode, links)); } } - return colorcode + TranslateString(JSONData2String(data.Properties["translate"], ""), using_data) + extra_result; + return colorcode + TranslateString(JSONData2String(data.Properties["translate"], "", links), using_data) + extra_result; } else return extra_result; @@ -235,7 +256,7 @@ namespace MinecraftClient.Protocol.Handlers string result = ""; foreach (Json.JSONData item in data.DataArray) { - result += JSONData2String(item, colorcode); + result += JSONData2String(item, colorcode, links); } return result; @@ -251,8 +272,7 @@ namespace MinecraftClient.Protocol.Handlers /// /// URL of resource /// Returns resource data if success, otherwise a WebException is raised - - private static string downloadString(string url) + private static string DownloadString(string url) { System.Net.HttpWebRequest myRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url); myRequest.Method = "GET"; diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index aa9a7e23..6710a60d 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -88,8 +88,9 @@ namespace MinecraftClient.Protocol.Handlers case 0x02: readData(1); readNextString(); readNextString(); readData(4); break; case 0x03: string message = readNextString(); - if (protocolversion >= 72) { message = ChatParser.ParseText(message); } - handler.OnTextReceived(message); break; + List links = new List(); + if (protocolversion >= 72) { message = ChatParser.ParseText(message, links); } + handler.OnTextReceived(message, links); break; case 0x04: readData(16); break; case 0x05: readData(6); readNextItemSlot(); break; case 0x06: readData(12); break; @@ -154,7 +155,7 @@ namespace MinecraftClient.Protocol.Handlers case 0x84: readData(11); nbr = readNextShort(); if (nbr > 0) { readData(nbr); } break; case 0x85: if (protocolversion >= 74) { readData(13); } break; case 0xC8: - if (readNextInt() == 2022) { handler.OnTextReceived("You are dead. Type /reco to respawn & reconnect."); } + if (readNextInt() == 2022) { ConsoleIO.WriteLineFormatted("§MCC: You are dead. Type /reco to respawn & reconnect."); } if (protocolversion >= 72) { readData(4); } else readData(1); break; case 0xC9: diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index dc411b30..8acc2810 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -257,7 +257,8 @@ namespace MinecraftClient.Protocol.Handlers break; } catch (ArgumentOutOfRangeException) { /* No message type */ } - handler.OnTextReceived(ChatParser.ParseText(message)); + List links = new List(); + handler.OnTextReceived(ChatParser.ParseText(message, links), links); break; case PacketIncomingType.Respawn: this.currentDimension = readNextInt(packetData); diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index 4f5e3df5..fb6b29fc 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -34,7 +34,9 @@ namespace MinecraftClient.Protocol /// /// This method is called when the protocol handler receives a chat message /// - void OnTextReceived(string text); + /// Text received from the server + /// Links embedded in text (for click events) + void OnTextReceived(string text, IEnumerable links); /// /// This method is called when a new player joins the game diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index bb5a4895..02fb62f5 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -88,6 +88,7 @@ namespace MinecraftClient public static string BrandInfo = MCCBrandInfo; public static bool DisplaySystemMessages = true; public static bool DisplayXPBarMessages = true; + public static bool DisplayChatLinks = true; public static bool TerrainAndMovements = false; public static string PrivateMsgsCmdName = "tell"; public static CacheType SessionCaching = CacheType.None; @@ -220,6 +221,7 @@ namespace MinecraftClient case "scriptcache": CacheScripts = str2bool(argValue); break; case "showsystemmessages": DisplaySystemMessages = str2bool(argValue); break; case "showxpbarmessages": DisplayXPBarMessages = str2bool(argValue); break; + case "showchatlinks": DisplayChatLinks = str2bool(argValue); break; case "terrainandmovements": TerrainAndMovements = str2bool(argValue); break; case "privatemsgscmdname": PrivateMsgsCmdName = argValue.ToLower().Trim(); break; case "botmessagedelay": botMessageDelay = TimeSpan.FromSeconds(str2int(argValue)); break; @@ -520,6 +522,7 @@ namespace MinecraftClient + "privatemsgscmdname=tell # Used by RemoteControl bot\r\n" + "showsystemmessages=true # System messages for server ops\r\n" + "showxpbarmessages=true # Messages displayed above xp bar\r\n" + + "showchatlinks=true # Show links embedded in chat messages\r\n" + "terrainandmovements=false # Uses more ram, cpu, bandwidth\r\n" + "sessioncache=memory # Use 'none', 'memory' or 'disk' (disk is experimental)\r\n" + "accountlist=accounts.txt\r\n"