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"