From ce8f21c2682a056a652c7a024f7e5869ea759dca Mon Sep 17 00:00:00 2001 From: ineanto Date: Sat, 23 Dec 2023 00:00:24 +0100 Subject: [PATCH] feat(i18n): custom locale migration --- CHANGELOG.log | 20 ++++-- build.gradle.kts | 2 +- .../java/xyz/ineanto/nicko/NickoBukkit.java | 58 +++++++-------- .../appearance/random/RandomNameFetcher.java | 2 +- .../xyz/ineanto/nicko/i18n/CustomLocale.java | 55 ++++++-------- .../nicko/migration/CustomLocaleMigrator.java | 32 ++++++--- src/main/resources/{names.csv => names.txt} | 2 +- .../test/config/ConfigurationVersionTest.java | 4 +- .../nicko/test/i18n/ItemTranslationTest.java | 1 - .../nicko/test/migration/MigrationTest.java | 71 +++++++++++++++++++ src/test/resources/en.yml | 4 ++ 11 files changed, 165 insertions(+), 86 deletions(-) rename src/main/resources/{names.csv => names.txt} (99%) create mode 100644 src/test/java/xyz/ineanto/nicko/test/migration/MigrationTest.java create mode 100644 src/test/resources/en.yml diff --git a/CHANGELOG.log b/CHANGELOG.log index fd7fe3c..1585289 100644 --- a/CHANGELOG.log +++ b/CHANGELOG.log @@ -1,13 +1,25 @@ -1.0.9-RC1: Update n°6 (20/12/23) +1.1.0-RC1: Update n°6 (XX/12/23) + [BREAKING] + - The language system has been updated to use the Adventure API (https://docs.advntr.dev/index.html). This results in custom locale now breaking + Nicko upon usage of legacy color codes (e.g., "§6Nicko"). Your custom locale will be backed up upon starting this version and you will be able to + use the new default English locale to help you make your locale compatible with the new formatting. + [FEATURES] - - + - Players can now choose to get a random appearance via a list of more than 400 usernames and skins associated. + - Players can now toggle a setting to automatically get a random appearance upon joining. + - Introduced a version string inside Nicko's language files to plan future updates to the file. (see [BREAKING]) + + (Note: the random skin functionality is still work-in-progress and might break or not work at all because of + the lack of time that I have to test all the usernames and skins associated.) [FIXES] - - bStats metrics should be properly sent now(?) + - Various optimizations and improvements. + - Internal refactoring + - bStats metrics are not minified anymore. 1.0.8-RC1: Update n°5 (19/12/23) [FEATURES] - - Introduced a version string inside Nicko's configuration to plan future updated to the file. Your previous configuration file will automatically be migrated to this current version (with the backup of your old one included!) + - Introduced a version string inside Nicko's configuration to plan future updates to the file. Your previous configuration file will automatically be migrated to this current version (with the backup of your old one included!) - Persistence and cache will now fallback to local alternatives when unreachable. - Player check GUI has been updated to better reflect the current state of player's disguises. - Developers can now listen to the PlayerDisguiseEvent and cancel the disguise process. diff --git a/build.gradle.kts b/build.gradle.kts index 39403a9..1f1ba0f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ plugins { } group = "xyz.ineanto" -version = "1.1.1-RC1" +version = "1.1.0-RC1" val shadowImplementation: Configuration by configurations.creating configurations["implementation"].extendsFrom(shadowImplementation) diff --git a/src/main/java/xyz/ineanto/nicko/NickoBukkit.java b/src/main/java/xyz/ineanto/nicko/NickoBukkit.java index 2fb4a49..2ec8bc6 100644 --- a/src/main/java/xyz/ineanto/nicko/NickoBukkit.java +++ b/src/main/java/xyz/ineanto/nicko/NickoBukkit.java @@ -12,11 +12,10 @@ import xyz.ineanto.nicko.config.Configuration; import xyz.ineanto.nicko.config.ConfigurationManager; import xyz.ineanto.nicko.event.PlayerJoinListener; import xyz.ineanto.nicko.event.PlayerQuitListener; -import xyz.ineanto.nicko.i18n.Locale; import xyz.ineanto.nicko.i18n.CustomLocale; +import xyz.ineanto.nicko.i18n.Locale; import xyz.ineanto.nicko.migration.ConfigurationMigrator; import xyz.ineanto.nicko.migration.CustomLocaleMigrator; -import xyz.ineanto.nicko.migration.Migrator; import xyz.ineanto.nicko.mojang.MojangAPI; import xyz.ineanto.nicko.placeholder.NickoExpansion; import xyz.ineanto.nicko.storage.PlayerDataStore; @@ -27,9 +26,7 @@ import xyz.xenondevs.invui.gui.structure.Structure; import xyz.xenondevs.invui.item.builder.ItemBuilder; import xyz.xenondevs.invui.item.impl.SimpleItem; -import java.io.FileNotFoundException; import java.io.IOException; -import java.util.List; public class NickoBukkit extends JavaPlugin { private static NickoBukkit plugin; @@ -65,15 +62,7 @@ public class NickoBukkit extends JavaPlugin { configurationManager = new ConfigurationManager(getDataFolder()); configurationManager.saveDefaultConfig(); - mojangAPI = new MojangAPI(); dataStore = new PlayerDataStore(mojangAPI, getNickoConfig()); - nameStore = new PlayerNameStore(); - nameFetcher = new RandomNameFetcher(this); - - if (!Bukkit.getOnlineMode()) { - getLogger().warning("Nicko has not been tested using offline mode!"); - getLogger().warning("Issues regarding Nicko being used in offline mode will be ignored for now."); - } if (!MinecraftVersion.WILD_UPDATE.atOrAbove()) { getLogger().severe("This version (" + MinecraftVersion.getCurrentVersion().getVersion() + ") is not supported by Nicko!"); @@ -81,6 +70,17 @@ public class NickoBukkit extends JavaPlugin { Bukkit.getPluginManager().disablePlugin(this); } + if (!Bukkit.getOnlineMode()) { + getLogger().warning("Nicko has not been tested using offline mode!"); + getLogger().warning("Issues regarding Nicko being used in offline mode will be ignored for now."); + } + + try { + Class.forName("io.papermc.paper.threadedregions.RegionizedServerInitEvent"); + getLogger().warning("Nicko has not been tested against Folia and might not work at all!"); + getLogger().warning("Issues regarding Nicko on Folia will be ignored for now."); + } catch (ClassNotFoundException ignored) { } + getLogger().info("Loading persistence..."); if (!dataStore.getStorage().getProvider().init()) { getLogger().severe("Couldn't connect to distant persistence, falling back on local persistence."); @@ -98,28 +98,20 @@ public class NickoBukkit extends JavaPlugin { } if (!unitTesting) { - final List migrations = List.of( - new ConfigurationMigrator(this), - new CustomLocaleMigrator(this) - ); - migrations.forEach(Migrator::migrate); + nameStore = new PlayerNameStore(); + mojangAPI = new MojangAPI(); + nameFetcher = new RandomNameFetcher(this); - try { - Class.forName("io.papermc.paper.threadedregions.RegionizedServerInitEvent"); - getLogger().warning("Nicko has not been tested against Folia and might not work at all!"); - getLogger().warning("Issues regarding Nicko on Folia will be ignored for now."); - } catch (ClassNotFoundException ignored) { } + new ConfigurationMigrator(this).migrate(); if (configuration.isCustomLocale()) { try { - customLocale = new CustomLocale(this); - if (customLocale.dumpIntoFile(Locale.ENGLISH)) { - getLogger().info("Successfully loaded custom language file."); - } else { - getLogger().severe("Failed to load custom language file!"); - } - } catch (FileNotFoundException e) { - throw new RuntimeException(e); + CustomLocale.dumpIntoFile(Locale.ENGLISH); + customLocale = new CustomLocale(); + new CustomLocaleMigrator(this, customLocale).migrate(); + getLogger().info("Successfully loaded the custom locale."); + } catch (IOException e) { + getLogger().severe("Failed to load the custom locale!"); } } @@ -147,7 +139,6 @@ public class NickoBukkit extends JavaPlugin { @Override public void onDisable() { if (!getDataStore().getStorage().isError()) { - nameStore.clearStoredNames(); Bukkit.getOnlinePlayers().forEach(player -> dataStore.saveData(player)); if (!dataStore.getStorage().getProvider().close()) { getLogger().severe("Failed to close persistence!"); @@ -156,7 +147,10 @@ public class NickoBukkit extends JavaPlugin { } } - if (!unitTesting) metrics.shutdown(); + if (!unitTesting) { + nameStore.clearStoredNames(); + metrics.shutdown(); + } getLogger().info("Nicko (Bukkit) has been disabled."); } diff --git a/src/main/java/xyz/ineanto/nicko/appearance/random/RandomNameFetcher.java b/src/main/java/xyz/ineanto/nicko/appearance/random/RandomNameFetcher.java index b6778a4..7549203 100644 --- a/src/main/java/xyz/ineanto/nicko/appearance/random/RandomNameFetcher.java +++ b/src/main/java/xyz/ineanto/nicko/appearance/random/RandomNameFetcher.java @@ -19,7 +19,7 @@ public class RandomNameFetcher { } public String getRandomUsername() { - final InputStream resource = instance.getResource("names.csv"); + final InputStream resource = instance.getResource("names.txt"); final List> records = new ArrayList<>(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource))) { String line; diff --git a/src/main/java/xyz/ineanto/nicko/i18n/CustomLocale.java b/src/main/java/xyz/ineanto/nicko/i18n/CustomLocale.java index a70e504..f172d27 100644 --- a/src/main/java/xyz/ineanto/nicko/i18n/CustomLocale.java +++ b/src/main/java/xyz/ineanto/nicko/i18n/CustomLocale.java @@ -3,55 +3,46 @@ package xyz.ineanto.nicko.i18n; import com.github.jsixface.YamlConfig; import xyz.ineanto.nicko.NickoBukkit; import xyz.ineanto.nicko.version.Version; -import xyz.xenondevs.invui.util.IOUtils; import java.io.*; -import java.time.Instant; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; +import java.nio.file.Files; import java.util.logging.Logger; public class CustomLocale { - private final Logger logger = Logger.getLogger("CustomLocale"); - private final File directory = new File(NickoBukkit.getInstance().getDataFolder() + "/lang/"); + private static final Logger logger = Logger.getLogger("CustomLocale"); + private static final File directory = new File(NickoBukkit.getInstance().getDataFolder(), "/locale/"); + private static final File file = new File(directory, "locale.yml"); - private final File file; - private final File backupFile; - private final NickoBukkit instance; private final String version; private final Version versionObject; - private final BufferedInputStream inputStream; - private final BufferedOutputStream outputStream; private final YamlConfig yamlFile; - public CustomLocale(NickoBukkit instance) throws FileNotFoundException { - this.instance = instance; - this.file = new File(directory, "lang.yml"); - final String date = Instant.now() - .atZone(ZoneId.systemDefault()) - .format(DateTimeFormatter.ofPattern("dd-MM-yyyy")); - this.backupFile = new File(directory, "lang.old-" + date + ".yml"); - this.inputStream = new BufferedInputStream(new FileInputStream(file)); - this.outputStream = new BufferedOutputStream(new FileOutputStream(file)); - this.yamlFile = new YamlConfig(inputStream); + public CustomLocale(InputStream input) throws IOException { + this.yamlFile = new YamlConfig(input); this.version = yamlFile.getString("version"); this.versionObject = Version.fromString(version); } - public boolean dumpIntoFile(Locale locale) { - if (locale == Locale.CUSTOM) return true; - if (file.exists()) return true; + public CustomLocale() throws IOException { + this.yamlFile = new YamlConfig(new FileInputStream(file)); + this.version = yamlFile.getString("version"); + this.versionObject = Version.fromString(version); + } + public static void dumpIntoFile(Locale locale) throws IOException { + if (locale == Locale.CUSTOM) return; + if (file.exists()) return; + if (!directory.exists()) directory.mkdirs(); + + final String localeFileName = locale.getCode() + ".yml"; try { - if (directory.mkdirs()) { - if (file.createNewFile()) { - IOUtils.copy(inputStream, outputStream, 8192); - } + final InputStream resource = NickoBukkit.getInstance().getResource(localeFileName); + if (resource != null) { + Files.copy(resource, file.toPath()); + resource.close(); } - return true; } catch (IOException e) { logger.severe("Unable to dump Locale: " + locale.getCode() + "!"); - return false; } } @@ -67,8 +58,8 @@ public class CustomLocale { return yamlFile; } - public File getBackupFile() { - return backupFile; + public File getDirectory() { + return directory; } public File getFile() { diff --git a/src/main/java/xyz/ineanto/nicko/migration/CustomLocaleMigrator.java b/src/main/java/xyz/ineanto/nicko/migration/CustomLocaleMigrator.java index 2e9193b..54279ea 100644 --- a/src/main/java/xyz/ineanto/nicko/migration/CustomLocaleMigrator.java +++ b/src/main/java/xyz/ineanto/nicko/migration/CustomLocaleMigrator.java @@ -4,31 +4,41 @@ import xyz.ineanto.nicko.NickoBukkit; import xyz.ineanto.nicko.i18n.CustomLocale; import xyz.ineanto.nicko.i18n.Locale; +import java.io.File; import java.io.IOException; import java.nio.file.Files; -import java.nio.file.StandardCopyOption; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; public class CustomLocaleMigrator implements Migrator { private final NickoBukkit instance; + private final CustomLocale customLocale; - public CustomLocaleMigrator(NickoBukkit instance) { + public CustomLocaleMigrator(NickoBukkit instance, CustomLocale customLocale) { this.instance = instance; + this.customLocale = customLocale; } @Override public void migrate() { - final CustomLocale customLanguageFile = instance.getCustomLocale(); - // Migrate custom locale (1.1.0-RC1) - if (customLanguageFile.getVersionObject() == null - || customLanguageFile.getVersion().isEmpty() - || customLanguageFile.getVersionObject().compareTo(Locale.VERSION) != 0) { - instance.getLogger().info("Migrating custom locale to match the current version..."); + if (customLocale.getVersionObject() == null + || customLocale.getVersion().isEmpty() + || customLocale.getVersionObject().compareTo(Locale.VERSION) != 0) { + instance.getLogger().info("Migrating the custom locale (" + customLocale.getVersion() + ") to match the current version (" + Locale.VERSION + ")..."); + + final String date = Instant.now().atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("dd-MM-yyyy")); + final File backupFile = new File(customLocale.getDirectory(), "locale-" + date + ".yml"); + try { - Files.copy(customLanguageFile.getFile().toPath(), customLanguageFile.getBackupFile().toPath(), StandardCopyOption.ATOMIC_MOVE); - customLanguageFile.dumpIntoFile(Locale.ENGLISH); + Files.copy(customLocale.getFile().toPath(), backupFile.toPath()); + if (customLocale.getFile().delete()) { + customLocale.dumpIntoFile(Locale.ENGLISH); + } + instance.getLogger().info("Successfully migrated the custom locale."); } catch (IOException e) { - instance.getLogger().severe("Failed to migrate your custom locale!"); + instance.getLogger().severe("Failed to migrate the custom locale!"); } } } diff --git a/src/main/resources/names.csv b/src/main/resources/names.txt similarity index 99% rename from src/main/resources/names.csv rename to src/main/resources/names.txt index 4eae88f..0d54e92 100644 --- a/src/main/resources/names.csv +++ b/src/main/resources/names.txt @@ -487,4 +487,4 @@ The42OWeedGOD CooperBee _MikuMiku_ althume -Tr3bba93 +Tr3bba93 \ No newline at end of file diff --git a/src/test/java/xyz/ineanto/nicko/test/config/ConfigurationVersionTest.java b/src/test/java/xyz/ineanto/nicko/test/config/ConfigurationVersionTest.java index a1b55ba..3535d7d 100644 --- a/src/test/java/xyz/ineanto/nicko/test/config/ConfigurationVersionTest.java +++ b/src/test/java/xyz/ineanto/nicko/test/config/ConfigurationVersionTest.java @@ -12,13 +12,11 @@ import xyz.ineanto.nicko.config.DefaultDataSources; import static org.junit.jupiter.api.Assertions.assertEquals; public class ConfigurationVersionTest { - private static NickoBukkit plugin; - @BeforeAll public static void setup() { MockBukkit.mock(); final Configuration configuration = Configuration.DEFAULT; - plugin = MockBukkit.load(NickoBukkit.class, configuration); + MockBukkit.load(NickoBukkit.class, configuration); } @Test diff --git a/src/test/java/xyz/ineanto/nicko/test/i18n/ItemTranslationTest.java b/src/test/java/xyz/ineanto/nicko/test/i18n/ItemTranslationTest.java index 56b768c..b9a722e 100644 --- a/src/test/java/xyz/ineanto/nicko/test/i18n/ItemTranslationTest.java +++ b/src/test/java/xyz/ineanto/nicko/test/i18n/ItemTranslationTest.java @@ -43,7 +43,6 @@ public class ItemTranslationTest { test.lore().forEach(System.out::println); final Translation translation = i18n.translate(I18NDict.GUI.Admin.Cache.STATISTICS, "1", "1"); - translation.lore().forEach(System.out::println); assertFalse(translation.lore().isEmpty()); assertEquals("Nombre de requêtes: 1", translation.lore().get(0)); assertEquals("Nb. de skin dans le cache: 1", translation.lore().get(1)); diff --git a/src/test/java/xyz/ineanto/nicko/test/migration/MigrationTest.java b/src/test/java/xyz/ineanto/nicko/test/migration/MigrationTest.java new file mode 100644 index 0000000..61ba6b3 --- /dev/null +++ b/src/test/java/xyz/ineanto/nicko/test/migration/MigrationTest.java @@ -0,0 +1,71 @@ +package xyz.ineanto.nicko.test.migration; + +import be.seeseemelk.mockbukkit.MockBukkit; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import xyz.ineanto.nicko.NickoBukkit; +import xyz.ineanto.nicko.config.Configuration; +import xyz.ineanto.nicko.config.DefaultDataSources; +import xyz.ineanto.nicko.i18n.CustomLocale; +import xyz.ineanto.nicko.migration.CustomLocaleMigrator; + +import java.io.*; +import java.nio.charset.StandardCharsets; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MigrationTest { + private static NickoBukkit plugin; + + private static File folder; + private static File localeFile; + + @BeforeAll + public static void setup() throws IOException { + MockBukkit.mock(); + final Configuration configuration = new Configuration(Configuration.VERSION.toString(), + DefaultDataSources.SQL_EMPTY, + DefaultDataSources.REDIS_EMPTY, + "§6Nicko §8§l| §r", + true); + plugin = MockBukkit.load(NickoBukkit.class, configuration); + folder = new File(plugin.getDataFolder(), "/locale/"); + localeFile = new File(folder, "locale.yml"); + folder.mkdirs(); + localeFile.createNewFile(); + } + + @Test + public void testLanguageFileMigration() throws IOException { + final String content = """ + # Nicko - Language File: + + # hello I'm the invalid version + version: "1.0.0" + """; + + BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(localeFile)); + outputStream.write(content.getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + + // Get wrong locale + final CustomLocale customLocaleBeforeMigration = new CustomLocale(new FileInputStream(localeFile)); + assertEquals(customLocaleBeforeMigration.getVersion(), "1.0.0"); + + // Migrate the wrong locale to the correct one + final CustomLocaleMigrator localeMigrator = new CustomLocaleMigrator(plugin, customLocaleBeforeMigration); + localeMigrator.migrate(); + + // Get the migrated locale + final CustomLocale customLocaleMigrated = new CustomLocale(new FileInputStream(localeFile)); + assertEquals(customLocaleMigrated.getVersion(), "1.1.0"); + } + + @AfterAll + public static void shutdown() { + MockBukkit.unmock(); + folder.delete(); + localeFile.delete(); + } +} diff --git a/src/test/resources/en.yml b/src/test/resources/en.yml new file mode 100644 index 0000000..7b83c58 --- /dev/null +++ b/src/test/resources/en.yml @@ -0,0 +1,4 @@ +# Nicko ${version} - Language File: + +# hello I'm the good version +version: "1.1.0" \ No newline at end of file