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…
Reference in a new issue