forked from GeyserMC/Geyser
		
	Merge pull request #275 from rtm516/server-language-processing
Server language processing
This commit is contained in:
		
						commit
						e1d1f9ee8a
					
				
					 15 changed files with 526 additions and 17 deletions
				
			
		|  | @ -101,6 +101,11 @@ public class GeyserBukkitConfiguration implements IGeyserConfiguration { | ||||||
|         return config.getBoolean("allow-third-party-capes", true); |         return config.getBoolean("allow-third-party-capes", true); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public String getDefaultLocale() { | ||||||
|  |         return config.getString("default-locale", "en_us"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public Path getFloodgateKeyFile() { |     public Path getFloodgateKeyFile() { | ||||||
|         return Paths.get(dataFolder.toString(), config.getString("floodgate-key-file", "public-key.pem")); |         return Paths.get(dataFolder.toString(), config.getString("floodgate-key-file", "public-key.pem")); | ||||||
|  |  | ||||||
|  | @ -102,6 +102,11 @@ public class GeyserBungeeConfiguration implements IGeyserConfiguration { | ||||||
|         return config.getBoolean("allow-third-party-capes", true); |         return config.getBoolean("allow-third-party-capes", true); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public String getDefaultLocale() { | ||||||
|  |         return config.getString("default-locale", "en_us"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public Path getFloodgateKeyFile() { |     public Path getFloodgateKeyFile() { | ||||||
|         return Paths.get(dataFolder.toString(), config.getString("floodgate-key-file", "public-key.pem")); |         return Paths.get(dataFolder.toString(), config.getString("floodgate-key-file", "public-key.pem")); | ||||||
|  |  | ||||||
|  | @ -105,6 +105,11 @@ public class GeyserSpongeConfiguration implements IGeyserConfiguration { | ||||||
|         return node.getNode("allow-third-party-capes").getBoolean(true); |         return node.getNode("allow-third-party-capes").getBoolean(true); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public String getDefaultLocale() { | ||||||
|  |         return node.getNode("default-locale").getString("en_us"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public Path getFloodgateKeyFile() { |     public Path getFloodgateKeyFile() { | ||||||
|         return Paths.get(dataFolder.toString(), node.getNode("floodgate-key-file").getString("public-key.pem")); |         return Paths.get(dataFolder.toString(), node.getNode("floodgate-key-file").getString("public-key.pem")); | ||||||
|  |  | ||||||
|  | @ -63,6 +63,9 @@ public class GeyserConfiguration implements IGeyserConfiguration { | ||||||
|     @JsonProperty("allow-third-party-capes") |     @JsonProperty("allow-third-party-capes") | ||||||
|     private boolean allowThirdPartyCapes; |     private boolean allowThirdPartyCapes; | ||||||
| 
 | 
 | ||||||
|  |     @JsonProperty("default-locale") | ||||||
|  |     private String defaultLocale; | ||||||
|  | 
 | ||||||
|     private MetricsInfo metrics; |     private MetricsInfo metrics; | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|  |  | ||||||
|  | @ -63,6 +63,9 @@ public class GeyserVelocityConfiguration implements IGeyserConfiguration { | ||||||
|     @JsonProperty("allow-third-party-capes") |     @JsonProperty("allow-third-party-capes") | ||||||
|     private boolean allowThirdPartyCapes; |     private boolean allowThirdPartyCapes; | ||||||
| 
 | 
 | ||||||
|  |     @JsonProperty("default-locale") | ||||||
|  |     private String defaultLocale; | ||||||
|  | 
 | ||||||
|     private MetricsInfo metrics; |     private MetricsInfo metrics; | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|  |  | ||||||
|  | @ -46,6 +46,8 @@ public interface IGeyserConfiguration { | ||||||
| 
 | 
 | ||||||
|     boolean isAllowThirdPartyCapes(); |     boolean isAllowThirdPartyCapes(); | ||||||
| 
 | 
 | ||||||
|  |     String getDefaultLocale(); | ||||||
|  | 
 | ||||||
|     Path getFloodgateKeyFile(); |     Path getFloodgateKeyFile(); | ||||||
| 
 | 
 | ||||||
|     IMetricsInfo getMetrics(); |     IMetricsInfo getMetrics(); | ||||||
|  |  | ||||||
|  | @ -65,6 +65,7 @@ import org.geysermc.connector.network.session.cache.*; | ||||||
| import org.geysermc.connector.network.translators.Registry; | import org.geysermc.connector.network.translators.Registry; | ||||||
| import org.geysermc.connector.network.translators.block.BlockTranslator; | import org.geysermc.connector.network.translators.block.BlockTranslator; | ||||||
| import org.geysermc.connector.utils.ChunkUtils; | import org.geysermc.connector.utils.ChunkUtils; | ||||||
|  | import org.geysermc.connector.utils.LocaleUtils; | ||||||
| import org.geysermc.connector.utils.Toolbox; | import org.geysermc.connector.utils.Toolbox; | ||||||
| import org.geysermc.floodgate.util.BedrockData; | import org.geysermc.floodgate.util.BedrockData; | ||||||
| import org.geysermc.floodgate.util.EncryptionUtil; | import org.geysermc.floodgate.util.EncryptionUtil; | ||||||
|  | @ -252,6 +253,17 @@ public class GeyserSession implements CommandSender { | ||||||
|                         connector.getLogger().info(authData.getName() + " (logged in as: " + protocol.getProfile().getName() + ")" + " has connected to remote java server on address " + remoteServer.getAddress()); |                         connector.getLogger().info(authData.getName() + " (logged in as: " + protocol.getProfile().getName() + ")" + " has connected to remote java server on address " + remoteServer.getAddress()); | ||||||
|                         playerEntity.setUuid(protocol.getProfile().getId()); |                         playerEntity.setUuid(protocol.getProfile().getId()); | ||||||
|                         playerEntity.setUsername(protocol.getProfile().getName()); |                         playerEntity.setUsername(protocol.getProfile().getName()); | ||||||
|  | 
 | ||||||
|  |                         String locale = clientData.getLanguageCode(); | ||||||
|  | 
 | ||||||
|  |                         // Let the user know there locale may take some time to download | ||||||
|  |                         // as it has to be extracted from a JAR | ||||||
|  |                         if (locale.toLowerCase().equals("en_us") && !LocaleUtils.LOCALE_MAPPINGS.containsKey("en_us")) { | ||||||
|  |                             sendMessage("Downloading your locale (en_us) this may take some time"); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         // Download and load the language for the player | ||||||
|  |                         LocaleUtils.downloadAndLoadLocale(locale); | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     @Override |                     @Override | ||||||
|  |  | ||||||
|  | @ -34,6 +34,8 @@ import com.github.steveice10.mc.protocol.data.message.TranslationMessage; | ||||||
| import com.github.steveice10.mc.protocol.packet.ingame.server.ServerChatPacket; | import com.github.steveice10.mc.protocol.packet.ingame.server.ServerChatPacket; | ||||||
| import com.nukkitx.protocol.bedrock.packet.TextPacket; | import com.nukkitx.protocol.bedrock.packet.TextPacket; | ||||||
| 
 | 
 | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
| @Translator(packet = ServerChatPacket.class) | @Translator(packet = ServerChatPacket.class) | ||||||
| public class JavaChatTranslator extends PacketTranslator<ServerChatPacket> { | public class JavaChatTranslator extends PacketTranslator<ServerChatPacket> { | ||||||
| 
 | 
 | ||||||
|  | @ -58,14 +60,20 @@ public class JavaChatTranslator extends PacketTranslator<ServerChatPacket> { | ||||||
|                 break; |                 break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         String locale = session.getClientData().getLanguageCode(); | ||||||
|  | 
 | ||||||
|         if (packet.getMessage() instanceof TranslationMessage) { |         if (packet.getMessage() instanceof TranslationMessage) { | ||||||
|             textPacket.setType(TextPacket.Type.TRANSLATION); |             textPacket.setType(TextPacket.Type.TRANSLATION); | ||||||
|             textPacket.setNeedsTranslation(true); |             textPacket.setNeedsTranslation(true); | ||||||
|             textPacket.setParameters(MessageUtils.getTranslationParams(((TranslationMessage) packet.getMessage()).getTranslationParams())); | 
 | ||||||
|             textPacket.setMessage(MessageUtils.getBedrockMessage(packet.getMessage())); |             List<String> paramsTranslated = MessageUtils.getTranslationParams(((TranslationMessage) packet.getMessage()).getTranslationParams(), locale); | ||||||
|  |             textPacket.setParameters(paramsTranslated); | ||||||
|  | 
 | ||||||
|  |             textPacket.setMessage(MessageUtils.insertParams(MessageUtils.getTranslatedBedrockMessage(packet.getMessage(), locale, false), paramsTranslated)); | ||||||
|         } else { |         } else { | ||||||
|             textPacket.setNeedsTranslation(false); |             textPacket.setNeedsTranslation(false); | ||||||
|             textPacket.setMessage(MessageUtils.getBedrockMessage(packet.getMessage())); | 
 | ||||||
|  |             textPacket.setMessage(MessageUtils.getTranslatedBedrockMessage(packet.getMessage(), locale, false)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         session.getUpstream().sendPacket(textPacket); |         session.getUpstream().sendPacket(textPacket); | ||||||
|  |  | ||||||
|  | @ -65,4 +65,23 @@ public class FileUtils { | ||||||
| 
 | 
 | ||||||
|         return file; |         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); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,320 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  * | ||||||
|  |  * @author GeyserMC | ||||||
|  |  * @link https://github.com/GeyserMC/Geyser | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | 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.net.URL; | ||||||
|  | import java.nio.file.Files; | ||||||
|  | import java.nio.file.Paths; | ||||||
|  | import java.nio.file.StandardCopyOption; | ||||||
|  | import java.time.OffsetDateTime; | ||||||
|  | import java.util.*; | ||||||
|  | import java.util.zip.ZipFile; | ||||||
|  | 
 | ||||||
|  | public class LocaleUtils { | ||||||
|  | 
 | ||||||
|  |     public static final Map<String, Map<String, String>> LOCALE_MAPPINGS = new HashMap<>(); | ||||||
|  | 
 | ||||||
|  |     private static final Map<String, Asset> ASSET_MAP = new HashMap<>(); | ||||||
|  | 
 | ||||||
|  |     private static final String DEFAULT_LOCALE = (GeyserConnector.getInstance().getConfig().getDefaultLocale() != null ? GeyserConnector.getInstance().getConfig().getDefaultLocale() : "en_us"); | ||||||
|  | 
 | ||||||
|  |     private static String smallestURL = ""; | ||||||
|  | 
 | ||||||
|  |     static { | ||||||
|  |         // Create the locales folder | ||||||
|  |         File localesFolder = new File("locales/"); | ||||||
|  |         localesFolder.mkdir(); | ||||||
|  | 
 | ||||||
|  |         // Download the latest asset list and cache it | ||||||
|  |         generateAssetCache(); | ||||||
|  |         downloadAndLoadLocale(DEFAULT_LOCALE); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private static void generateAssetCache() { | ||||||
|  |         try { | ||||||
|  |             VersionManifest versionManifest = Toolbox.JSON_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 = Toolbox.JSON_MAPPER.readValue(WebUtils.getBody(latestInfoURL), VersionInfo.class); | ||||||
|  | 
 | ||||||
|  |             int currentSize = Integer.MAX_VALUE; | ||||||
|  |             for (VersionDownload download : versionInfo.getDownloads().values()) { | ||||||
|  |                 if (download.getUrl().endsWith(".jar") && download.getSize() < currentSize) { | ||||||
|  |                     smallestURL = download.getUrl(); | ||||||
|  |                     currentSize = download.getSize(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             JsonNode assets = Toolbox.JSON_MAPPER.readTree(WebUtils.getBody(versionInfo.getAssetIndex().getUrl())).get("objects"); | ||||||
|  | 
 | ||||||
|  |             Iterator<Map.Entry<String, JsonNode>> assetIterator = assets.fields(); | ||||||
|  |             while (assetIterator.hasNext()) { | ||||||
|  |                 Map.Entry<String, JsonNode> entry = assetIterator.next(); | ||||||
|  |                 Asset asset = Toolbox.JSON_MAPPER.treeToValue(entry.getValue(), Asset.class); | ||||||
|  |                 ASSET_MAP.put(entry.getKey(), asset); | ||||||
|  |             } | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             GeyserConnector.getInstance().getLogger().info("Failed to load locale asset cache: " + (!e.getMessage().isEmpty() ? e.getMessage() : e.getStackTrace())); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static void downloadAndLoadLocale(String locale) { | ||||||
|  |         locale = locale.toLowerCase(); | ||||||
|  |         if (!ASSET_MAP.containsKey("minecraft/lang/" + locale + ".json") && !locale.equals("en_us")) { | ||||||
|  |             GeyserConnector.getInstance().getLogger().warning("Invalid locale requested to download and load: " + locale); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         GeyserConnector.getInstance().getLogger().debug("Downloading and loading locale: " + locale); | ||||||
|  | 
 | ||||||
|  |         downloadLocale(locale); | ||||||
|  |         loadLocale(locale); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private static void downloadLocale(String locale) { | ||||||
|  |         File localeFile = new File("locales/" + locale + ".json"); | ||||||
|  | 
 | ||||||
|  |         if (localeFile.exists()) { | ||||||
|  |             GeyserConnector.getInstance().getLogger().debug("Locale already downloaded: " + locale); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Create the en_us locale | ||||||
|  |         if (locale.equals("en_us")) { | ||||||
|  |             downloadEN_US(localeFile); | ||||||
|  | 
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Get the hash and download the locale | ||||||
|  |         String hash = ASSET_MAP.get("minecraft/lang/" + locale + ".json").getHash(); | ||||||
|  |         WebUtils.downloadFile("http://resources.download.minecraft.net/" + hash.substring(0, 2) + "/" + hash, "locales/" + locale + ".json"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private static void loadLocale(String locale) { | ||||||
|  |         File localeFile = new File("locales/" + locale + ".json"); | ||||||
|  | 
 | ||||||
|  |         // 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 localeObj; | ||||||
|  |             try { | ||||||
|  |                 localeObj = 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<Map.Entry<String, JsonNode>> localeIterator = localeObj.fields(); | ||||||
|  |             Map<String, String> langMap = new HashMap<>(); | ||||||
|  |             while (localeIterator.hasNext()) { | ||||||
|  |                 Map.Entry<String, JsonNode> 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); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private static void downloadEN_US(File localeFile) { | ||||||
|  |         try { | ||||||
|  |             // Let the user know we are downloading the JAR | ||||||
|  |             GeyserConnector.getInstance().getLogger().info("Downloading Minecraft JAR to extract en_us locale, please wait... (this may take some time depending on the speed of your internet connection)"); | ||||||
|  |             GeyserConnector.getInstance().getLogger().debug("Download URL: " + smallestURL); | ||||||
|  | 
 | ||||||
|  |             // Download the smallest JAR (client or server) | ||||||
|  |             WebUtils.downloadFile(smallestURL, "tmp_locale.jar"); | ||||||
|  | 
 | ||||||
|  |             // Load in the JAR as a zip and extract the file | ||||||
|  |             ZipFile localeJar = new ZipFile("tmp_locale.jar"); | ||||||
|  |             InputStream inputStream = localeJar.getInputStream(localeJar.getEntry("assets/minecraft/lang/en_us.json")); | ||||||
|  |             FileOutputStream outputStream = new FileOutputStream(localeFile); | ||||||
|  | 
 | ||||||
|  |             // Write the file to the locale dir | ||||||
|  |             int data = inputStream.read(); | ||||||
|  |             while(data != -1){ | ||||||
|  |                 outputStream.write(data); | ||||||
|  |                 data = inputStream.read(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Flush all changes to disk and cleanup | ||||||
|  |             outputStream.flush(); | ||||||
|  |             outputStream.close(); | ||||||
|  | 
 | ||||||
|  |             inputStream.close(); | ||||||
|  |             localeJar.close(); | ||||||
|  | 
 | ||||||
|  |             // Delete the nolonger needed client/server jar | ||||||
|  |             Files.delete(Paths.get("tmp_locale.jar")); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             throw new AssertionError("Unable to download and extract en_us locale!", e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static String getLocaleString(String messageText, String locale) { | ||||||
|  |         Map<String, String> localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(locale.toLowerCase()); | ||||||
|  |         if (localeStrings == null) | ||||||
|  |             localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(DEFAULT_LOCALE); | ||||||
|  | 
 | ||||||
|  |         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<Version> 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; | ||||||
|  | 
 | ||||||
|  |     @JsonProperty("downloads") | ||||||
|  |     private Map<String, VersionDownload> downloads; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @JsonIgnoreProperties(ignoreUnknown = true) | ||||||
|  | @Getter | ||||||
|  | class VersionDownload { | ||||||
|  |     @JsonProperty("sha1") | ||||||
|  |     private String sha1; | ||||||
|  | 
 | ||||||
|  |     @JsonProperty("size") | ||||||
|  |     private int size; | ||||||
|  | 
 | ||||||
|  |     @JsonProperty("url") | ||||||
|  |     private String url; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @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; | ||||||
|  | } | ||||||
|  | @ -35,21 +35,24 @@ import com.google.gson.JsonElement; | ||||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||||
| import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||||
| import com.google.gson.JsonPrimitive; | import com.google.gson.JsonPrimitive; | ||||||
|  | import org.geysermc.connector.GeyserConnector; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.*; | ||||||
| import java.util.Collections; | import java.util.regex.Matcher; | ||||||
| import java.util.List; | import java.util.regex.Pattern; | ||||||
| 
 | 
 | ||||||
| public class MessageUtils { | public class MessageUtils { | ||||||
| 
 | 
 | ||||||
|     public static List<String> getTranslationParams(Message[] messages) { |     public static List<String> getTranslationParams(Message[] messages, String locale) { | ||||||
|         List<String> strings = new ArrayList<>(); |         List<String> strings = new ArrayList<>(); | ||||||
|         for (Message message : messages) { |         for (Message message : messages) { | ||||||
|             if (message instanceof TranslationMessage) { |             if (message instanceof TranslationMessage) { | ||||||
|                 TranslationMessage translation = (TranslationMessage) message; |                 TranslationMessage translation = (TranslationMessage) message; | ||||||
| 
 | 
 | ||||||
|  |                 if (locale == null) { | ||||||
|                     String builder = "%" + translation.getTranslationKey(); |                     String builder = "%" + translation.getTranslationKey(); | ||||||
|                     strings.add(builder); |                     strings.add(builder); | ||||||
|  |                 } | ||||||
| 
 | 
 | ||||||
|                 if (translation.getTranslationKey().equals("commands.gamemode.success.other")) { |                 if (translation.getTranslationKey().equals("commands.gamemode.success.other")) { | ||||||
|                     strings.add(""); |                     strings.add(""); | ||||||
|  | @ -59,11 +62,16 @@ public class MessageUtils { | ||||||
|                     strings.add(" - no permission or invalid command!"); |                     strings.add(" - no permission or invalid command!"); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 strings.addAll(getTranslationParams(translation.getTranslationParams())); |                 List<String> furtherParams = getTranslationParams(translation.getTranslationParams()); | ||||||
|  |                 if (locale != null) { | ||||||
|  |                     strings.add(insertParams(LocaleUtils.getLocaleString(translation.getTranslationKey(), locale), furtherParams)); | ||||||
|  |                 }else{ | ||||||
|  |                     strings.addAll(furtherParams); | ||||||
|  |                 } | ||||||
|             } else { |             } else { | ||||||
|                 String builder = getFormat(message.getStyle().getFormats()) + |                 String builder = getFormat(message.getStyle().getFormats()) + | ||||||
|                         getColor(message.getStyle().getColor()) + |                         getColor(message.getStyle().getColor()); | ||||||
|                         getBedrockMessage(message); |                 builder += getTranslatedBedrockMessage(message, locale, false); | ||||||
|                 strings.add(builder); |                 strings.add(builder); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -71,29 +79,63 @@ public class MessageUtils { | ||||||
|         return strings; |         return strings; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public static List<String> getTranslationParams(Message[] messages) { | ||||||
|  |         return getTranslationParams(messages, null); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public static String getTranslationText(TranslationMessage message) { |     public static String getTranslationText(TranslationMessage message) { | ||||||
|         return getFormat(message.getStyle().getFormats()) + getColor(message.getStyle().getColor()) |         return getFormat(message.getStyle().getFormats()) + getColor(message.getStyle().getColor()) | ||||||
|                 + "%" + message.getTranslationKey(); |                 + "%" + message.getTranslationKey(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static String getBedrockMessage(Message message) { |     public static String getTranslatedBedrockMessage(Message message, String locale, boolean shouldTranslate) { | ||||||
|         JsonParser parser = new JsonParser(); |         JsonParser parser = new JsonParser(); | ||||||
|         if (isMessage(message.getText())) { |         if (isMessage(message.getText())) { | ||||||
|             JsonObject object = parser.parse(message.getText()).getAsJsonObject(); |             JsonObject object = parser.parse(message.getText()).getAsJsonObject(); | ||||||
|             message = Message.fromJson(formatJson(object)); |             message = Message.fromJson(formatJson(object)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         StringBuilder builder = new StringBuilder(message.getText()); |         String messageText = message.getText(); | ||||||
|  |         if (locale != null && shouldTranslate) { | ||||||
|  |             messageText = LocaleUtils.getLocaleString(messageText, locale); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         StringBuilder builder = new StringBuilder(messageText); | ||||||
|         for (Message msg : message.getExtra()) { |         for (Message msg : message.getExtra()) { | ||||||
|             builder.append(getFormat(msg.getStyle().getFormats())); |             builder.append(getFormat(msg.getStyle().getFormats())); | ||||||
|             builder.append(getColor(msg.getStyle().getColor())); |             builder.append(getColor(msg.getStyle().getColor())); | ||||||
|             if (!(msg.getText() == null)) { |             if (!(msg.getText() == null)) { | ||||||
|                 builder.append(getBedrockMessage(msg)); |                 boolean isTranslationMessage = (msg instanceof TranslationMessage); | ||||||
|  |                 builder.append(getTranslatedBedrockMessage(msg, locale, isTranslationMessage)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         return builder.toString(); |         return builder.toString(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public static String getBedrockMessage(Message message) { | ||||||
|  |         return getTranslatedBedrockMessage(message, null, false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static String insertParams(String message, List<String> params) { | ||||||
|  |         String newMessage = message; | ||||||
|  | 
 | ||||||
|  |         Pattern p = Pattern.compile("%([1-9])\\$s"); | ||||||
|  |         Matcher m = p.matcher(message); | ||||||
|  |         while (m.find()) { | ||||||
|  |             try { | ||||||
|  |                 newMessage = newMessage.replaceFirst("%" + m.group(1) + "\\$s" , params.get(Integer.parseInt(m.group(1)) - 1)); | ||||||
|  |             } catch (Exception e) { | ||||||
|  |                 // Couldnt find the param to replace | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for (String text : params) { | ||||||
|  |             newMessage = newMessage.replaceFirst("%s", text); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return newMessage; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private static String getColor(ChatColor color) { |     private static String getColor(ChatColor color) { | ||||||
|         String base = "\u00a7"; |         String base = "\u00a7"; | ||||||
|         switch (color) { |         switch (color) { | ||||||
|  |  | ||||||
|  | @ -40,7 +40,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; | ||||||
| import org.geysermc.connector.GeyserConnector; | import org.geysermc.connector.GeyserConnector; | ||||||
| import org.geysermc.connector.network.translators.item.ItemEntry; | import org.geysermc.connector.network.translators.item.ItemEntry; | ||||||
| 
 | 
 | ||||||
| import java.io.InputStream; | import java.io.*; | ||||||
| import java.util.*; | import java.util.*; | ||||||
| 
 | 
 | ||||||
| public class Toolbox { | public class Toolbox { | ||||||
|  | @ -52,6 +52,8 @@ public class Toolbox { | ||||||
| 
 | 
 | ||||||
|     public static final Int2ObjectMap<ItemEntry> ITEM_ENTRIES = new Int2ObjectOpenHashMap<>(); |     public static final Int2ObjectMap<ItemEntry> ITEM_ENTRIES = new Int2ObjectOpenHashMap<>(); | ||||||
| 
 | 
 | ||||||
|  |     public static final Map<String, Map<String, String>> LOCALE_MAPPINGS = new HashMap<>(); | ||||||
|  | 
 | ||||||
|     static { |     static { | ||||||
|         /* Load biomes */ |         /* Load biomes */ | ||||||
|         InputStream biomestream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/biome_definitions.dat"); |         InputStream biomestream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/biome_definitions.dat"); | ||||||
|  | @ -103,6 +105,9 @@ public class Toolbox { | ||||||
|                     entry.getValue().get("bedrock_id").intValue(), entry.getValue().get("bedrock_data").intValue())); |                     entry.getValue().get("bedrock_id").intValue(), entry.getValue().get("bedrock_data").intValue())); | ||||||
|             itemIndex++; |             itemIndex++; | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         // Load the locale data | ||||||
|  |         LocaleUtils.init(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static InputStream getResource(String resource) { |     public static InputStream getResource(String resource) { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,77 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  * | ||||||
|  |  * @author GeyserMC | ||||||
|  |  * @link https://github.com/GeyserMC/Geyser | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package org.geysermc.connector.utils; | ||||||
|  | 
 | ||||||
|  | import org.geysermc.connector.GeyserConnector; | ||||||
|  | 
 | ||||||
|  | import java.io.BufferedReader; | ||||||
|  | import java.io.InputStream; | ||||||
|  | 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; | ||||||
|  | import java.nio.file.Files; | ||||||
|  | import java.nio.file.Paths; | ||||||
|  | import java.nio.file.StandardCopyOption; | ||||||
|  | 
 | ||||||
|  | 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(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static void downloadFile(String reqURL, String fileLocation) { | ||||||
|  |         try { | ||||||
|  |             InputStream in = new URL(reqURL).openStream(); | ||||||
|  |             Files.copy(in, Paths.get(fileLocation), StandardCopyOption.REPLACE_EXISTING); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             throw new AssertionError("Unable to download and save file: " + fileLocation + " (" + reqURL + ")", e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -57,6 +57,9 @@ general-thread-pool: 32 | ||||||
| # OptiFine capes, LabyMod capes, 5Zig capes and MinecraftCapes | # OptiFine capes, LabyMod capes, 5Zig capes and MinecraftCapes | ||||||
| allow-third-party-capes: true | allow-third-party-capes: true | ||||||
| 
 | 
 | ||||||
|  | # The default locale if we dont have the one the client requested | ||||||
|  | default-locale: en_us | ||||||
|  | 
 | ||||||
| # bStats is a stat tracker that is entirely anonymous and tracks only basic information | # bStats is a stat tracker that is entirely anonymous and tracks only basic information | ||||||
| # about Geyser, such as how many people are online, how many servers are using Geyser, | # about Geyser, such as how many people are online, how many servers are using Geyser, | ||||||
| # what OS is being used, etc. You can learn more about bStats here: https://bstats.org/. | # what OS is being used, etc. You can learn more about bStats here: https://bstats.org/. | ||||||
|  |  | ||||||
|  | @ -1 +1 @@ | ||||||
| Subproject commit 278c73449aeeb4064c7513a68f98a49a5f463f0a | Subproject commit efc9db6b7d51bdf145230933ac23b321ac1c132d | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue