diff --git a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java index 3070e743d..0938fa7c0 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java @@ -65,4 +65,23 @@ public class FileUtils { return file; } + + public static void writeFile(File file, char[] data) throws IOException { + if (!file.exists()) { + file.createNewFile(); + } + + FileOutputStream fos = new FileOutputStream(file); + + for (char c : data) { + fos.write(c); + } + + fos.flush(); + fos.close(); + } + + public static void writeFile(String name, char[] data) throws IOException { + writeFile(new File(name), data); + } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java new file mode 100644 index 000000000..55540ea39 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java @@ -0,0 +1,235 @@ +package org.geysermc.connector.utils; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Getter; +import org.geysermc.connector.GeyserConnector; + +import java.io.*; +import java.time.OffsetDateTime; +import java.util.*; + +public class LocaleUtils { + + public static final Map> LOCALE_MAPPINGS = new HashMap<>(); + + static { + /* Load the language mappings */ + InputStream stream = Toolbox.getResource("mappings/locales.json"); + JsonNode locales; + try { + locales = Toolbox.JSON_MAPPER.readTree(stream); + } catch (Exception e) { + throw new AssertionError("Unable to load Java locale list", e); + } + + File localesFolder = new File("locales/"); + + if (!localesFolder.exists()) { + GeyserConnector.getInstance().getLogger().info("Locales not cached, downloading... (this may take some time depending on your internet connection)"); + ObjectMapper mapper = new ObjectMapper(); + try { + VersionManifest versionManifest = mapper.readValue(WebUtils.getBody("https://launchermeta.mojang.com/mc/game/version_manifest.json"), VersionManifest.class); + String latestInfoURL = ""; + for (Version version : versionManifest.getVersions()) { + if (version.getId().equals(versionManifest.getLatestVersion().getRelease())) { + latestInfoURL = version.getUrl(); + break; + } + } + + if (latestInfoURL.isEmpty()) { + throw new Exception("Unable to get latest Minecraft version"); + } + + VersionInfo versionInfo = mapper.readValue(WebUtils.getBody(latestInfoURL), VersionInfo.class); + JsonNode assets = mapper.readTree(WebUtils.getBody(versionInfo.getAssetIndex().getUrl())).get("objects"); + + localesFolder.mkdir(); + + for (JsonNode localeNode : locales.get("locales")) { + String currentLocale = localeNode.asText(); + + if (currentLocale.equals("en_us")) { continue; } + + GeyserConnector.getInstance().getLogger().info("Downloading locale: " + currentLocale); + Asset asset = mapper.treeToValue(assets.get("minecraft/lang/" + currentLocale + ".json"), Asset.class); + String hash = asset.getHash(); + FileUtils.writeFile("locales/" + currentLocale + ".json", WebUtils.getBody("http://resources.download.minecraft.net/" + hash.substring(0, 2) + "/" + hash).toCharArray()); + } + } catch (Exception e) { + GeyserConnector.getInstance().getLogger().info("Failed to load locales: " + (!e.getMessage().isEmpty() ? e.getMessage() : e.getStackTrace())); + } + } + + if (localesFolder.exists()) { + for (JsonNode localeNode : locales.get("locales")) { + String currentLocale = localeNode.asText(); + loadLocale(currentLocale); + } + } + } + + public static void downloadAndLoadLocale(String locale) { + downloadLocale(locale); + loadLocale(locale); + } + + private static void downloadLocale(String locale) { + + } + + private static void loadLocale(String locale) { + File localeFile = new File("locales/" + locale + ".json"); + + // Create the en_us locale + if (!localeFile.exists() && locale.equals("en_us")) { + try { + InputStreamReader isReader = new InputStreamReader(Toolbox.getResource("mappings/lang/en_us.json")); + BufferedReader reader = new BufferedReader(isReader); + StringBuffer sb = new StringBuffer(); + String str; + while((str = reader.readLine())!= null){ + sb.append(str); + } + + FileUtils.writeFile(localeFile, sb.toString().toCharArray()); + } catch (Exception e) { + throw new AssertionError("Unable to load en_us locale!", e); + } + } + + // Load the locale + if (localeFile.exists()) { + // Read the localefile + InputStream localeStream; + try { + localeStream = new FileInputStream(localeFile); + } catch (FileNotFoundException e) { + throw new AssertionError("Unable to load locale: " + locale + " (" + e.getMessage() + ")"); + } + + // Parse the file as json + JsonNode locale; + try { + locale = Toolbox.JSON_MAPPER.readTree(localeStream); + } catch (Exception e) { + throw new AssertionError("Unable to load Java lang map for " + locale, e); + } + + // Parse all the locale fields + Iterator> localeIterator = locale.fields(); + Map langMap = new HashMap<>(); + while (localeIterator.hasNext()) { + Map.Entry entry = localeIterator.next(); + langMap.put(entry.getKey(), entry.getValue().asText()); + } + + // Insert the locale into the mappings + LOCALE_MAPPINGS.put(locale.toLowerCase(), langMap); + } else { + GeyserConnector.getInstance().getLogger().warning("Missing locale file: " + locale); + } + } + + public static String getLocaleString(String messageText, String locale) { + Map localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(locale.toLowerCase()); + if (localeStrings == null) + localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(GeyserConnector.getInstance().getConfig().getDefaultLocale()); + + return localeStrings.getOrDefault(messageText, messageText); + } + + public static void init() { + // no-op + } +} + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +class VersionManifest { + @JsonProperty("latest") + private LatestVersion latestVersion; + + @JsonProperty("versions") + private List versions; +} + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +class LatestVersion { + @JsonProperty("release") + private String release; + + @JsonProperty("snapshot") + private String snapshot; +} + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +class Version { + @JsonProperty("id") + private String id; + + @JsonProperty("type") + private String type; + + @JsonProperty("url") + private String url; + + @JsonProperty("time") + private String time; + + @JsonProperty("releaseTime") + private String releaseTime; +} + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +class VersionInfo { + @JsonProperty("id") + private String id; + + @JsonProperty("type") + private String type; + + @JsonProperty("time") + private String time; + + @JsonProperty("releaseTime") + private String releaseTime; + + @JsonProperty("assetIndex") + private AssetIndex assetIndex; +} + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +class AssetIndex { + @JsonProperty("id") + private String id; + + @JsonProperty("sha1") + private String sha1; + + @JsonProperty("size") + private int size; + + @JsonProperty("totalSize") + private int totalSize; + + @JsonProperty("url") + private String url; +} + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +class Asset { + @JsonProperty("hash") + private String hash; + + @JsonProperty("size") + private int size; +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java b/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java index ef2a661da..6e78321c6 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java @@ -64,7 +64,7 @@ public class MessageUtils { List furtherParams = getTranslationParams(translation.getTranslationParams()); if (locale != null) { - strings.add(insertParams(getLocaleString(translation.getTranslationKey(), locale), furtherParams)); + strings.add(insertParams(LocaleUtils.getLocaleString(translation.getTranslationKey(), locale), furtherParams)); }else{ strings.addAll(furtherParams); } @@ -101,7 +101,7 @@ public class MessageUtils { String messageText = message.getText(); if (locale != null) { - messageText = getLocaleString(messageText, locale); + messageText = LocaleUtils.getLocaleString(messageText, locale); } StringBuilder builder = new StringBuilder(messageText); @@ -119,14 +119,6 @@ public class MessageUtils { return getTranslatedBedrockMessage(message, null); } - private static String getLocaleString(String messageText, String locale) { - Map localeStrings = Toolbox.LOCALE_MAPPINGS.get(locale.toLowerCase()); - if (localeStrings == null) - localeStrings = Toolbox.LOCALE_MAPPINGS.get(GeyserConnector.getInstance().getConfig().getDefaultLocale()); - - return localeStrings.getOrDefault(messageText, messageText); - } - public static String getBedrockMessage(Message message) { return getTranslatedBedrockMessage(message); } diff --git a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java index 674a8bf7b..9c1e10f6c 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java +++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java @@ -106,35 +106,8 @@ public class Toolbox { itemIndex++; } - /* Load the language mappings */ - stream = Toolbox.getResource("mappings/locales.json"); - JsonNode locales; - try { - locales = Toolbox.JSON_MAPPER.readTree(stream); - } catch (Exception e) { - throw new AssertionError("Unable to load Java locale list", e); - } - - for (JsonNode localeNode : locales.get("locales")) { - String currentLocale = localeNode.asText(); - - InputStream localeStream = Toolbox.getResource("mappings/lang/" + currentLocale + ".json"); - JsonNode locale; - try { - locale = Toolbox.JSON_MAPPER.readTree(localeStream); - } catch (Exception e) { - throw new AssertionError("Unable to load Java lang map for " + currentLocale, e); - } - - Iterator> localeIterator = locale.fields(); - Map langMap = new HashMap<>(); - while (localeIterator.hasNext()) { - Map.Entry entry = localeIterator.next(); - langMap.put(entry.getKey(), entry.getValue().asText()); - } - - LOCALE_MAPPINGS.put(currentLocale.toLowerCase(), langMap); - } + // Load the locale data + LocaleUtils.init(); } public static InputStream getResource(String resource) { diff --git a/connector/src/main/java/org/geysermc/connector/utils/WebUtils.java b/connector/src/main/java/org/geysermc/connector/utils/WebUtils.java new file mode 100644 index 000000000..9a6556be9 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/WebUtils.java @@ -0,0 +1,39 @@ +package org.geysermc.connector.utils; + +import org.geysermc.connector.GeyserConnector; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +public class WebUtils { + + public static String getBody(String reqURL) { + URL url = null; + try { + url = new URL(reqURL); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + con.setRequestMethod("GET"); + + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer content = new StringBuffer(); + + while ((inputLine = in.readLine()) != null) { + content.append(inputLine); + content.append("\n"); + } + + in.close(); + con.disconnect(); + + return content.toString(); + } catch (Exception e) { + return e.getMessage(); + } + } +} diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index c1745b639..1c45d021c 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit c1745b639500e8a9434c2239074acc36784f7c40 +Subproject commit 1c45d021c69da23fdb68d1196365cad4f75b5d90