2013-07-18 09:27:19 +02:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
|
2014-05-31 01:59:03 +02:00
|
|
|
|
namespace MinecraftClient.Protocol.Handlers
|
2013-07-18 09:27:19 +02:00
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This class parses JSON chat data from MC 1.6+ and returns the appropriate string to be printed.
|
|
|
|
|
|
/// </summary>
|
2013-07-20 11:01:49 +10:00
|
|
|
|
|
2013-07-18 09:27:19 +02:00
|
|
|
|
static class ChatParser
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The main function to convert text from MC 1.6+ JSON to MC 1.5.2 formatted text
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="json">JSON serialized text</param>
|
|
|
|
|
|
/// <returns>Returns the translated text</returns>
|
|
|
|
|
|
|
|
|
|
|
|
public static string ParseText(string json)
|
|
|
|
|
|
{
|
2015-06-19 19:29:23 +02:00
|
|
|
|
return JSONData2String(Json.ParseJson(json), "");
|
2013-07-18 09:27:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Get the classic color tag corresponding to a color name
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="colorname">Color Name</param>
|
|
|
|
|
|
/// <returns>Color code</returns>
|
|
|
|
|
|
|
|
|
|
|
|
private static string color2tag(string colorname)
|
|
|
|
|
|
{
|
2013-07-20 11:01:49 +10:00
|
|
|
|
switch (colorname.ToLower())
|
2013-07-18 09:27:19 +02:00
|
|
|
|
{
|
2014-06-03 13:05:53 +02:00
|
|
|
|
/* MC 1.7+ Name MC 1.6 Name Classic tag */
|
|
|
|
|
|
case "black": /* Blank if same */ return "§0";
|
|
|
|
|
|
case "dark_blue": return "§1";
|
|
|
|
|
|
case "dark_green": return "§2";
|
|
|
|
|
|
case "dark_aqua": case "dark_cyan": return "§3";
|
|
|
|
|
|
case "dark_red": return "§4";
|
|
|
|
|
|
case "dark_purple": case "dark_magenta": return "§5";
|
|
|
|
|
|
case "gold": case "dark_yellow": return "§6";
|
|
|
|
|
|
case "gray": return "§7";
|
|
|
|
|
|
case "dark_gray": return "§8";
|
|
|
|
|
|
case "blue": return "§9";
|
|
|
|
|
|
case "green": return "§a";
|
|
|
|
|
|
case "aqua": case "cyan": return "§b";
|
|
|
|
|
|
case "red": return "§c";
|
|
|
|
|
|
case "light_purple": case "magenta": return "§d";
|
|
|
|
|
|
case "yellow": return "§e";
|
|
|
|
|
|
case "white": return "§f";
|
2013-07-18 09:27:19 +02:00
|
|
|
|
default: return "";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Rules for text translation
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
|
|
|
|
private static bool init = false;
|
|
|
|
|
|
private static Dictionary<string, string> TranslationRules = new Dictionary<string, string>();
|
|
|
|
|
|
public static void InitTranslations() { if (!init) { InitRules(); init = true; } }
|
|
|
|
|
|
private static void InitRules()
|
|
|
|
|
|
{
|
|
|
|
|
|
//Small default dictionnary of translation rules
|
|
|
|
|
|
TranslationRules["chat.type.admin"] = "[%s: %s]";
|
|
|
|
|
|
TranslationRules["chat.type.announcement"] = "§d[%s] %s";
|
|
|
|
|
|
TranslationRules["chat.type.emote"] = " * %s %s";
|
|
|
|
|
|
TranslationRules["chat.type.text"] = "<%s> %s";
|
|
|
|
|
|
TranslationRules["multiplayer.player.joined"] = "§e%s joined the game.";
|
|
|
|
|
|
TranslationRules["multiplayer.player.left"] = "§e%s left the game.";
|
|
|
|
|
|
TranslationRules["commands.message.display.incoming"] = "§7%s whispers to you: %s";
|
|
|
|
|
|
TranslationRules["commands.message.display.outgoing"] = "§7You whisper to %s: %s";
|
|
|
|
|
|
|
2014-02-01 14:57:31 +01:00
|
|
|
|
//Language file in a subfolder, depending on the language setting
|
|
|
|
|
|
if (!System.IO.Directory.Exists("lang"))
|
|
|
|
|
|
System.IO.Directory.CreateDirectory("lang");
|
|
|
|
|
|
|
2014-06-27 13:26:27 +02:00
|
|
|
|
string Language_File = "lang" + (Program.isUsingMono ? '/' : '\\') + Settings.Language + ".lang";
|
2013-08-12 15:05:36 +02:00
|
|
|
|
|
2014-02-01 14:57:31 +01:00
|
|
|
|
//File not found? Try downloading language file from Mojang's servers?
|
|
|
|
|
|
if (!System.IO.File.Exists(Language_File))
|
2014-01-13 12:38:01 +01:00
|
|
|
|
{
|
|
|
|
|
|
Console.ForegroundColor = ConsoleColor.DarkGray;
|
2014-02-01 14:57:31 +01:00
|
|
|
|
ConsoleIO.WriteLine("Downloading '" + Settings.Language + ".lang' from Mojang servers...");
|
2014-01-13 12:38:01 +01:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2016-01-30 22:14:53 -08:00
|
|
|
|
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));
|
|
|
|
|
|
Console.WriteLine(hash);
|
|
|
|
|
|
ConsoleIO.WriteLine("Done. File saved as '" + Language_File + '\'');
|
2014-01-13 12:38:01 +01:00
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
ConsoleIO.WriteLine("Failed to download the file.");
|
|
|
|
|
|
}
|
|
|
|
|
|
Console.ForegroundColor = ConsoleColor.Gray;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-02-01 14:57:31 +01:00
|
|
|
|
//Download Failed? Defaulting to en_GB.lang if the game is installed
|
|
|
|
|
|
if (!System.IO.File.Exists(Language_File) //Try en_GB.lang
|
|
|
|
|
|
&& System.IO.File.Exists(Settings.TranslationsFile_FromMCDir))
|
|
|
|
|
|
{
|
|
|
|
|
|
Language_File = Settings.TranslationsFile_FromMCDir;
|
2014-06-11 20:40:25 +02:00
|
|
|
|
ConsoleIO.WriteLineFormatted("§8Defaulting to en_GB.lang from your Minecraft directory.");
|
2014-02-01 14:57:31 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-01-13 12:38:01 +01:00
|
|
|
|
//Load the external dictionnary of translation rules or display an error message
|
2014-02-01 14:57:31 +01:00
|
|
|
|
if (System.IO.File.Exists(Language_File))
|
2013-07-18 09:27:19 +02:00
|
|
|
|
{
|
2014-02-01 14:57:31 +01:00
|
|
|
|
string[] translations = System.IO.File.ReadAllLines(Language_File);
|
2013-07-18 09:27:19 +02:00
|
|
|
|
foreach (string line in translations)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (line.Length > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
string[] splitted = line.Split('=');
|
|
|
|
|
|
if (splitted.Length == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
TranslationRules[splitted[0]] = splitted[1];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-06-11 20:40:25 +02:00
|
|
|
|
ConsoleIO.WriteLineFormatted("§8Translations file loaded.");
|
2013-07-18 09:27:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
else //No external dictionnary found.
|
|
|
|
|
|
{
|
2014-05-31 01:59:03 +02:00
|
|
|
|
ConsoleIO.WriteLineFormatted("§8Translations file not found: \"" + Language_File + "\""
|
2014-06-11 20:40:25 +02:00
|
|
|
|
+ "\nSome messages won't be properly printed without this file.");
|
2013-07-18 09:27:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Format text using a specific formatting rule.
|
|
|
|
|
|
/// Example : * %s %s + ["ORelio", "is doing something"] = * ORelio is doing something
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="rulename">Name of the rule, chosen by the server</param>
|
|
|
|
|
|
/// <param name="using_data">Data to be used in the rule</param>
|
|
|
|
|
|
/// <returns>Returns the formatted text according to the given data</returns>
|
|
|
|
|
|
|
|
|
|
|
|
private static string TranslateString(string rulename, List<string> using_data)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!init) { InitRules(); init = true; }
|
|
|
|
|
|
if (TranslationRules.ContainsKey(rulename))
|
|
|
|
|
|
{
|
2015-05-17 21:45:00 +02:00
|
|
|
|
int using_idx = 0;
|
|
|
|
|
|
string rule = TranslationRules[rulename];
|
|
|
|
|
|
StringBuilder result = new StringBuilder();
|
|
|
|
|
|
for (int i = 0; i < rule.Length; i++)
|
2013-07-27 12:25:14 +02:00
|
|
|
|
{
|
2015-05-17 21:45:00 +02:00
|
|
|
|
if (rule[i] == '%' && i + 1 < rule.Length)
|
|
|
|
|
|
{
|
|
|
|
|
|
//Using string or int with %s or %d
|
|
|
|
|
|
if (rule[i + 1] == 's' || rule[i + 1] == 'd')
|
|
|
|
|
|
{
|
|
|
|
|
|
if (using_data.Count > using_idx)
|
|
|
|
|
|
{
|
|
|
|
|
|
result.Append(using_data[using_idx]);
|
|
|
|
|
|
using_idx++;
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//Using specified string or int with %1$s, %2$s...
|
|
|
|
|
|
else if (char.IsDigit(rule[i + 1])
|
|
|
|
|
|
&& i + 3 < rule.Length && rule[i + 2] == '$'
|
|
|
|
|
|
&& (rule[i + 3] == 's' || rule[i + 3] == 'd'))
|
|
|
|
|
|
{
|
|
|
|
|
|
int specified_idx = rule[i + 1] - '1';
|
|
|
|
|
|
if (using_data.Count > specified_idx)
|
|
|
|
|
|
{
|
|
|
|
|
|
result.Append(using_data[specified_idx]);
|
|
|
|
|
|
using_idx++;
|
|
|
|
|
|
i += 3;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
result.Append(rule[i]);
|
2013-07-18 09:27:19 +02:00
|
|
|
|
}
|
2015-05-17 21:45:00 +02:00
|
|
|
|
return result.ToString();
|
2013-07-18 09:27:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
else return "[" + rulename + "] " + String.Join(" ", using_data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Use a JSON Object to build the corresponding string
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="data">JSON object to convert</param>
|
2014-01-11 16:17:48 +01:00
|
|
|
|
/// <param name="colorcode">Allow parent color code to affect child elements (set to "" for function init)</param>
|
2013-07-18 09:27:19 +02:00
|
|
|
|
/// <returns>returns the Minecraft-formatted string</returns>
|
2015-06-19 19:29:23 +02:00
|
|
|
|
|
|
|
|
|
|
private static string JSONData2String(Json.JSONData data, string colorcode)
|
2013-07-18 09:27:19 +02:00
|
|
|
|
{
|
2014-01-08 23:58:49 +01:00
|
|
|
|
string extra_result = "";
|
2013-07-18 09:27:19 +02:00
|
|
|
|
switch (data.Type)
|
|
|
|
|
|
{
|
2015-06-19 19:29:23 +02:00
|
|
|
|
case Json.JSONData.DataType.Object:
|
2014-01-11 16:17:48 +01:00
|
|
|
|
if (data.Properties.ContainsKey("color"))
|
|
|
|
|
|
{
|
|
|
|
|
|
colorcode = color2tag(JSONData2String(data.Properties["color"], ""));
|
|
|
|
|
|
}
|
2014-01-08 23:58:49 +01:00
|
|
|
|
if (data.Properties.ContainsKey("extra"))
|
|
|
|
|
|
{
|
2015-06-19 19:29:23 +02:00
|
|
|
|
Json.JSONData[] extras = data.Properties["extra"].DataArray.ToArray();
|
|
|
|
|
|
foreach (Json.JSONData item in extras)
|
2014-01-11 16:17:48 +01:00
|
|
|
|
extra_result = extra_result + JSONData2String(item, colorcode) + "§r";
|
2013-07-18 09:27:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
if (data.Properties.ContainsKey("text"))
|
|
|
|
|
|
{
|
2014-01-11 16:17:48 +01:00
|
|
|
|
return colorcode + JSONData2String(data.Properties["text"], colorcode) + extra_result;
|
2013-07-18 09:27:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
else if (data.Properties.ContainsKey("translate"))
|
|
|
|
|
|
{
|
|
|
|
|
|
List<string> using_data = new List<string>();
|
2014-01-11 14:38:00 +01:00
|
|
|
|
if (data.Properties.ContainsKey("using") && !data.Properties.ContainsKey("with"))
|
|
|
|
|
|
data.Properties["with"] = data.Properties["using"];
|
|
|
|
|
|
if (data.Properties.ContainsKey("with"))
|
2013-07-18 09:27:19 +02:00
|
|
|
|
{
|
2015-06-19 19:29:23 +02:00
|
|
|
|
Json.JSONData[] array = data.Properties["with"].DataArray.ToArray();
|
2013-07-18 09:27:19 +02:00
|
|
|
|
for (int i = 0; i < array.Length; i++)
|
|
|
|
|
|
{
|
2014-01-11 16:17:48 +01:00
|
|
|
|
using_data.Add(JSONData2String(array[i], colorcode));
|
2013-07-18 09:27:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2014-01-11 16:17:48 +01:00
|
|
|
|
return colorcode + TranslateString(JSONData2String(data.Properties["translate"], ""), using_data) + extra_result;
|
2013-07-18 09:27:19 +02:00
|
|
|
|
}
|
2014-01-08 23:58:49 +01:00
|
|
|
|
else return extra_result;
|
2013-07-20 11:01:49 +10:00
|
|
|
|
|
2015-06-19 19:29:23 +02:00
|
|
|
|
case Json.JSONData.DataType.Array:
|
2013-07-18 09:27:19 +02:00
|
|
|
|
string result = "";
|
2015-06-19 19:29:23 +02:00
|
|
|
|
foreach (Json.JSONData item in data.DataArray)
|
2013-07-18 09:27:19 +02:00
|
|
|
|
{
|
2014-01-11 16:17:48 +01:00
|
|
|
|
result += JSONData2String(item, colorcode);
|
2013-07-18 09:27:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
2015-06-19 19:29:23 +02:00
|
|
|
|
case Json.JSONData.DataType.String:
|
2014-01-11 16:17:48 +01:00
|
|
|
|
return colorcode + data.StringValue;
|
2013-07-18 09:27:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return "";
|
|
|
|
|
|
}
|
2013-08-08 10:51:59 +02:00
|
|
|
|
|
2014-01-13 12:38:01 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Do a HTTP request to get a webpage or text data from a server file
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="url">URL of resource</param>
|
|
|
|
|
|
/// <returns>Returns resource data if success, otherwise a WebException is raised</returns>
|
|
|
|
|
|
|
|
|
|
|
|
private static string downloadString(string url)
|
|
|
|
|
|
{
|
|
|
|
|
|
System.Net.HttpWebRequest myRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
|
|
|
|
|
|
myRequest.Method = "GET";
|
|
|
|
|
|
System.Net.WebResponse myResponse = myRequest.GetResponse();
|
|
|
|
|
|
System.IO.StreamReader sr = new System.IO.StreamReader(myResponse.GetResponseStream(), System.Text.Encoding.UTF8);
|
|
|
|
|
|
string result = sr.ReadToEnd();
|
|
|
|
|
|
sr.Close();
|
|
|
|
|
|
myResponse.Close();
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
2013-07-18 09:27:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|