mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
I ocasionally see untranslated messages; this should resolve all of them. The changes to Settings.cs, the core changes, just update the index file used and the normal hash. The changes in ChatParser.cs primarilly fix the name: 1.7.4 used 'lang/en-GB.lang' but 1.7.10 and 1.8 use 'minecraft/lang/en-GB.lang' and 'realms/lang/en-GB.lang' (and realms comes first), meaning that the wrong language file is selected. The name is updated to make sure the right file is used. I also corrected the indentation in that block.
267 lines
12 KiB
C#
267 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
namespace MinecraftClient.Protocol.Handlers
|
|
{
|
|
/// <summary>
|
|
/// This class parses JSON chat data from MC 1.6+ and returns the appropriate string to be printed.
|
|
/// </summary>
|
|
|
|
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)
|
|
{
|
|
return JSONData2String(Json.ParseJson(json), "");
|
|
}
|
|
|
|
/// <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)
|
|
{
|
|
switch (colorname.ToLower())
|
|
{
|
|
/* 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";
|
|
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";
|
|
|
|
//Language file in a subfolder, depending on the language setting
|
|
if (!System.IO.Directory.Exists("lang"))
|
|
System.IO.Directory.CreateDirectory("lang");
|
|
|
|
string Language_File = "lang" + (Program.isUsingMono ? '/' : '\\') + Settings.Language + ".lang";
|
|
|
|
//File not found? Try downloading language file from Mojang's servers?
|
|
if (!System.IO.File.Exists(Language_File))
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.DarkGray;
|
|
ConsoleIO.WriteLine("Downloading '" + Settings.Language + ".lang' from Mojang servers...");
|
|
try
|
|
{
|
|
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 + '\'');
|
|
}
|
|
catch
|
|
{
|
|
ConsoleIO.WriteLine("Failed to download the file.");
|
|
}
|
|
Console.ForegroundColor = ConsoleColor.Gray;
|
|
}
|
|
|
|
//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;
|
|
ConsoleIO.WriteLineFormatted("§8Defaulting to en_GB.lang from your Minecraft directory.");
|
|
}
|
|
|
|
//Load the external dictionnary of translation rules or display an error message
|
|
if (System.IO.File.Exists(Language_File))
|
|
{
|
|
string[] translations = System.IO.File.ReadAllLines(Language_File);
|
|
foreach (string line in translations)
|
|
{
|
|
if (line.Length > 0)
|
|
{
|
|
string[] splitted = line.Split('=');
|
|
if (splitted.Length == 2)
|
|
{
|
|
TranslationRules[splitted[0]] = splitted[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
ConsoleIO.WriteLineFormatted("§8Translations file loaded.");
|
|
}
|
|
else //No external dictionnary found.
|
|
{
|
|
ConsoleIO.WriteLineFormatted("§8Translations file not found: \"" + Language_File + "\""
|
|
+ "\nSome messages won't be properly printed without this file.");
|
|
}
|
|
}
|
|
|
|
/// <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))
|
|
{
|
|
int using_idx = 0;
|
|
string rule = TranslationRules[rulename];
|
|
StringBuilder result = new StringBuilder();
|
|
for (int i = 0; i < rule.Length; i++)
|
|
{
|
|
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]);
|
|
}
|
|
return result.ToString();
|
|
}
|
|
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>
|
|
/// <param name="colorcode">Allow parent color code to affect child elements (set to "" for function init)</param>
|
|
/// <returns>returns the Minecraft-formatted string</returns>
|
|
|
|
private static string JSONData2String(Json.JSONData data, string colorcode)
|
|
{
|
|
string extra_result = "";
|
|
switch (data.Type)
|
|
{
|
|
case Json.JSONData.DataType.Object:
|
|
if (data.Properties.ContainsKey("color"))
|
|
{
|
|
colorcode = color2tag(JSONData2String(data.Properties["color"], ""));
|
|
}
|
|
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";
|
|
}
|
|
if (data.Properties.ContainsKey("text"))
|
|
{
|
|
return colorcode + JSONData2String(data.Properties["text"], colorcode) + extra_result;
|
|
}
|
|
else if (data.Properties.ContainsKey("translate"))
|
|
{
|
|
List<string> using_data = new List<string>();
|
|
if (data.Properties.ContainsKey("using") && !data.Properties.ContainsKey("with"))
|
|
data.Properties["with"] = data.Properties["using"];
|
|
if (data.Properties.ContainsKey("with"))
|
|
{
|
|
Json.JSONData[] array = data.Properties["with"].DataArray.ToArray();
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
using_data.Add(JSONData2String(array[i], colorcode));
|
|
}
|
|
}
|
|
return colorcode + TranslateString(JSONData2String(data.Properties["translate"], ""), using_data) + extra_result;
|
|
}
|
|
else return extra_result;
|
|
|
|
case Json.JSONData.DataType.Array:
|
|
string result = "";
|
|
foreach (Json.JSONData item in data.DataArray)
|
|
{
|
|
result += JSONData2String(item, colorcode);
|
|
}
|
|
return result;
|
|
|
|
case Json.JSONData.DataType.String:
|
|
return colorcode + data.StringValue;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
/// <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;
|
|
}
|
|
}
|
|
}
|