diff --git a/build.gradle.kts b/build.gradle.kts index 2fda361..97d8151 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -70,6 +70,9 @@ tasks { processResources { from("src/main/resources") duplicatesStrategy = DuplicatesStrategy.EXCLUDE + filesMatching("config.yml") { + expand("version" to version) + } filesMatching("plugin.yml") { expand("version" to version) } diff --git a/src/main/java/xyz/ineanto/nicko/NickoBukkit.java b/src/main/java/xyz/ineanto/nicko/NickoBukkit.java index 94507d5..f73941c 100644 --- a/src/main/java/xyz/ineanto/nicko/NickoBukkit.java +++ b/src/main/java/xyz/ineanto/nicko/NickoBukkit.java @@ -24,6 +24,8 @@ import xyz.xenondevs.invui.item.builder.ItemBuilder; import xyz.xenondevs.invui.item.impl.SimpleItem; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; public class NickoBukkit extends JavaPlugin { private static NickoBukkit plugin; @@ -67,7 +69,6 @@ public class NickoBukkit extends JavaPlugin { 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!"); dataStore.getStorage().setError(true); @@ -91,6 +92,20 @@ public class NickoBukkit extends JavaPlugin { } if (!unitTesting) { + // Migrate configuration (1.0.8-RC1) + if (configuration.getVersion() == null + || configuration.getVersion().isEmpty() + || configuration.getVersionObject().compareTo(Configuration.VERSION) != 0) { + getLogger().info("Migrating configuration file from older version..."); + try { + Files.copy(configurationManager.getFile().toPath(), configurationManager.getBackupFile().toPath(), StandardCopyOption.REPLACE_EXISTING); + Files.delete(configurationManager.getFile().toPath()); + configurationManager.saveDefaultConfig(); + } catch (IOException e) { + getLogger().severe("Failed to migrate your configuration!"); + } + } + try { Class.forName("io.papermc.paper.threadedregions.RegionizedServerInitEvent"); getLogger().warning("Nicko has not been tested against Folia and might not work at all!"); @@ -139,7 +154,7 @@ public class NickoBukkit extends JavaPlugin { } } - if(!unitTesting) metrics.shutdown(); + if (!unitTesting) metrics.shutdown(); getLogger().info("Nicko (Bukkit) has been disabled."); } @@ -149,7 +164,10 @@ public class NickoBukkit extends JavaPlugin { public Configuration getNickoConfig() { try { - if (configuration == null) { return configuration = configurationManager.load(); } + if (configuration == null) { + configuration = configurationManager.load(); + getLogger().info("Configuration file loaded."); + } return configuration; } catch (IOException e) { getLogger().severe("Failed to load the configuration file!"); diff --git a/src/main/java/xyz/ineanto/nicko/config/Configuration.java b/src/main/java/xyz/ineanto/nicko/config/Configuration.java index 048c6d2..a4958ad 100644 --- a/src/main/java/xyz/ineanto/nicko/config/Configuration.java +++ b/src/main/java/xyz/ineanto/nicko/config/Configuration.java @@ -1,29 +1,48 @@ package xyz.ineanto.nicko.config; import com.fasterxml.jackson.annotation.JsonProperty; +import xyz.ineanto.nicko.version.Version; public class Configuration { + public static final Version VERSION = new Version(1, 0, 8); + public static final Configuration DEFAULT = new Configuration(VERSION.toString(), + DefaultDataSources.SQL_EMPTY, + DefaultDataSources.REDIS_EMPTY, + "§6Nicko §8§l| §r", + false); + + private final transient Version versionObject; + + @JsonProperty("version") + private final String version; @JsonProperty("sql") private final SQLDataSourceConfiguration sqlConfiguration; @JsonProperty("redis") private final DataSourceConfiguration redisConfiguration; + @JsonProperty("prefix") private final String prefix; + @JsonProperty("customLocale") private final Boolean customLocale; - public Configuration(SQLDataSourceConfiguration sqlConfiguration, DataSourceConfiguration redisConfiguration, String prefix, Boolean customLocale) { + public Configuration(@JsonProperty("version") String version, + @JsonProperty("sql") SQLDataSourceConfiguration sqlConfiguration, + @JsonProperty("redis") DataSourceConfiguration redisConfiguration, + @JsonProperty("prefix") String prefix, + @JsonProperty("customLocale") Boolean customLocale) { + this.version = version; + this.versionObject = Version.fromString(version); this.sqlConfiguration = sqlConfiguration; this.redisConfiguration = redisConfiguration; this.prefix = prefix; this.customLocale = customLocale; } - public Configuration() { - this( - new SQLDataSourceConfiguration(false, "", 3306, "", "", true), - new DataSourceConfiguration(false, "", 6379, "", ""), - "", - false - ); + public String getVersion() { + return version; + } + + public Version getVersionObject() { + return versionObject; } public SQLDataSourceConfiguration getSqlConfiguration() { diff --git a/src/main/java/xyz/ineanto/nicko/config/ConfigurationManager.java b/src/main/java/xyz/ineanto/nicko/config/ConfigurationManager.java index 0824ee5..566d55e 100644 --- a/src/main/java/xyz/ineanto/nicko/config/ConfigurationManager.java +++ b/src/main/java/xyz/ineanto/nicko/config/ConfigurationManager.java @@ -7,16 +7,25 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import java.io.*; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.logging.Logger; public class ConfigurationManager { private final Logger logger = Logger.getLogger("ConfigurationManager"); private final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); private final File file; + private final File backupFile; public ConfigurationManager(File directory) { + final String date = Instant.now() + .atZone(ZoneId.systemDefault()) + .format(DateTimeFormatter.ofPattern("dd-MM-yyyy")); this.file = new File(directory, "config.yml"); + this.backupFile = new File(directory, "config.old-" + date + ".yml"); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); } public void save(Configuration configuration) throws IOException { @@ -47,4 +56,12 @@ public class ConfigurationManager { return mapper.readValue(reader, Configuration.class); } } + + public File getFile() { + return file; + } + + public File getBackupFile() { + return backupFile; + } } diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/admin/check/PlayerInformationItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/admin/check/PlayerInformationItem.java index 8055016..1ba5b43 100644 --- a/src/main/java/xyz/ineanto/nicko/gui/items/admin/check/PlayerInformationItem.java +++ b/src/main/java/xyz/ineanto/nicko/gui/items/admin/check/PlayerInformationItem.java @@ -15,6 +15,7 @@ import xyz.ineanto.nicko.gui.items.common.choice.ChoiceCallback; import xyz.ineanto.nicko.i18n.I18N; import xyz.ineanto.nicko.i18n.I18NDict; import xyz.ineanto.nicko.profile.NickoProfile; +import xyz.xenondevs.invui.item.builder.AbstractItemBuilder; import xyz.xenondevs.invui.item.builder.ItemBuilder; import xyz.xenondevs.invui.item.builder.SkullBuilder; import xyz.xenondevs.invui.item.impl.AsyncItem; @@ -27,6 +28,7 @@ import java.util.Optional; public class PlayerInformationItem extends AsyncItem { private final Player target; private final NickoProfile profile; + private final I18N i18n; public PlayerInformationItem(I18N i18n, Player target) { super(new SuppliedItem(() -> { @@ -39,11 +41,19 @@ public class PlayerInformationItem extends AsyncItem { if (optionalProfile.isPresent()) { final NickoProfile profile = optionalProfile.get(); - return i18n.translateItem(skull, I18NDict.GUI.Admin.CHECK, + final AbstractItemBuilder headItem = i18n.translateItem(skull, I18NDict.GUI.Admin.CHECK, target.getName(), (profile.hasData() ? "§a✔" : "§c❌"), (profile.getName() == null ? "§7N/A" : profile.getName()), (profile.getSkin() == null ? "§7N/A" : profile.getSkin())); + + if (!profile.hasData()) { + // Remove the last 2 lines of the lore. + headItem.removeLoreLine(headItem.getLore().size() - 1); + headItem.removeLoreLine(headItem.getLore().size() - 1); + } + + return headItem; } } catch (MojangApiUtils.MojangApiException | IOException e) { NickoBukkit.getInstance().getLogger().severe("Unable to get head for specified UUID ( " + target.getUniqueId() + ")! (GUI/PlayerCheck)"); @@ -53,6 +63,7 @@ public class PlayerInformationItem extends AsyncItem { "§c§l?!?", "§7N/A", "§7N/A", "§7N/A" ); }); + this.i18n = i18n; this.target = target; this.profile = NickoBukkit.getInstance().getDataStore().getData(target.getUniqueId()).orElse(NickoProfile.EMPTY_PROFILE); } @@ -67,7 +78,6 @@ public class PlayerInformationItem extends AsyncItem { public void onConfirm() { final AppearanceManager appearanceManager = new AppearanceManager(target); appearanceManager.reset(); - final I18N i18n = new I18N(player); player.sendMessage(i18n.translate(I18NDict.Event.Admin.Check.REMOVE_SKIN, target.getName())); } diff --git a/src/main/java/xyz/ineanto/nicko/version/Version.java b/src/main/java/xyz/ineanto/nicko/version/Version.java new file mode 100644 index 0000000..b3a7f1a --- /dev/null +++ b/src/main/java/xyz/ineanto/nicko/version/Version.java @@ -0,0 +1,33 @@ +package xyz.ineanto.nicko.version; + +import org.jetbrains.annotations.NotNull; + +import java.util.Comparator; + +public record Version(int major, int minor, int patch) implements Comparable { + @Override + public int compareTo(@NotNull Version otherVersion) { + final Comparator comparator = Comparator + .comparingInt(Version::major) + .thenComparingInt(Version::minor) + .thenComparingInt(Version::patch); + return comparator.compare(this, otherVersion); + } + + @Override + public String toString() { + return major + "." + minor + "." + patch; + } + + public static Version fromString(String versionString) { + if (versionString == null || versionString.isEmpty()) { return new Version(0, 0, 0); } + final String[] split = versionString.split("\\."); + try { + return new Version(Integer.parseInt(split[0]), + Integer.parseInt(split[1]), + Integer.parseInt(split[2])); + } catch (NumberFormatException exception) { + return new Version(0, 0, 0); + } + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index ce98b0f..27cca69 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,7 +1,7 @@ -# Nicko ${project.version} - Config: +# Nicko ${version} - Config: -# Don't modify this. -# No, like... really. +# Specifies the configuration version. +# Do NOT modify this field. version: "1.0.8" ############ diff --git a/src/main/resources/en.yml b/src/main/resources/en.yml index 6216055..84e6d82 100644 --- a/src/main/resources/en.yml +++ b/src/main/resources/en.yml @@ -1,5 +1,3 @@ -version: "1.0.8" - error: generic: "An unknown error occurred." permission: "§cYou do not have the required permission." diff --git a/src/main/resources/fr.yml b/src/main/resources/fr.yml index 79e70a2..02783c6 100644 --- a/src/main/resources/fr.yml +++ b/src/main/resources/fr.yml @@ -1,5 +1,3 @@ -version: "1.0.8" - error: generic: "Une erreur inconnue c'est produite." permission: "§cVous ne possédez pas la permission." diff --git a/src/test/java/xyz/ineanto/nicko/test/NickoPluginTest.java b/src/test/java/xyz/ineanto/nicko/test/NickoPluginTest.java index e500995..8b848dd 100644 --- a/src/test/java/xyz/ineanto/nicko/test/NickoPluginTest.java +++ b/src/test/java/xyz/ineanto/nicko/test/NickoPluginTest.java @@ -7,7 +7,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import xyz.ineanto.nicko.NickoBukkit; import xyz.ineanto.nicko.config.Configuration; -import xyz.ineanto.nicko.config.DefaultDataSources; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -16,11 +15,7 @@ public class NickoPluginTest { @BeforeAll public static void setup() { - final Configuration config = new Configuration( - DefaultDataSources.MARIADB_EMPTY, - DefaultDataSources.REDIS_EMPTY, - "", - false); + final Configuration config = Configuration.DEFAULT; MockBukkit.mock(); plugin = MockBukkit.load(NickoBukkit.class, config); } diff --git a/src/test/java/xyz/ineanto/nicko/test/config/ConfigurationTest.java b/src/test/java/xyz/ineanto/nicko/test/config/ConfigurationTest.java index 76c798f..cd78d77 100644 --- a/src/test/java/xyz/ineanto/nicko/test/config/ConfigurationTest.java +++ b/src/test/java/xyz/ineanto/nicko/test/config/ConfigurationTest.java @@ -7,7 +7,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import xyz.ineanto.nicko.NickoBukkit; import xyz.ineanto.nicko.config.Configuration; -import xyz.ineanto.nicko.config.DefaultDataSources; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -17,11 +16,7 @@ public class ConfigurationTest { @BeforeAll public static void setup() { MockBukkit.mock(); - final Configuration config = new Configuration( - DefaultDataSources.SQL_EMPTY, - DefaultDataSources.REDIS_EMPTY, - "", - false); + final Configuration config = Configuration.DEFAULT; plugin = MockBukkit.load(NickoBukkit.class, config); } diff --git a/src/test/java/xyz/ineanto/nicko/test/config/ConfigurationVersionTest.java b/src/test/java/xyz/ineanto/nicko/test/config/ConfigurationVersionTest.java new file mode 100644 index 0000000..a1b55ba --- /dev/null +++ b/src/test/java/xyz/ineanto/nicko/test/config/ConfigurationVersionTest.java @@ -0,0 +1,72 @@ +package xyz.ineanto.nicko.test.config; + +import be.seeseemelk.mockbukkit.MockBukkit; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import xyz.ineanto.nicko.NickoBukkit; +import xyz.ineanto.nicko.config.Configuration; +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); + } + + @Test + @DisplayName("Compare configuration version") + public void compareConfigurationVersion() { + final Configuration configuration = new Configuration(Configuration.VERSION.toString(), + DefaultDataSources.SQL_EMPTY, + DefaultDataSources.REDIS_EMPTY, + "", + false); + assertEquals(configuration.getVersionObject().compareTo(Configuration.VERSION), 0); + } + + @Test + @DisplayName("Compare newer configuration version") + public void compareNewerConfigurationVersion() { + final Configuration configuration = new Configuration("24.1.0", + DefaultDataSources.SQL_EMPTY, + DefaultDataSources.REDIS_EMPTY, + "", + false); + assertEquals(configuration.getVersionObject().compareTo(Configuration.VERSION), 1); + } + + @Test + @DisplayName("Compare older configuration version") + public void compareOlderConfigurationVersion() { + final Configuration configuration = new Configuration("0.23.3", + DefaultDataSources.SQL_EMPTY, + DefaultDataSources.REDIS_EMPTY, + "", + false); + assertEquals(configuration.getVersionObject().compareTo(Configuration.VERSION), -1); + } + + @Test + @DisplayName("Compare unknown configuration version") + public void compareUnknownConfigurationVersion() { + final Configuration configuration = new Configuration(null, + DefaultDataSources.SQL_EMPTY, + DefaultDataSources.REDIS_EMPTY, + "", + false); + assertEquals(configuration.getVersionObject().compareTo(Configuration.VERSION), -1); + } + + @AfterAll + public static void shutdown() { + MockBukkit.unmock(); + } +} 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 6ee6905..50905c3 100644 --- a/src/test/java/xyz/ineanto/nicko/test/i18n/ItemTranslationTest.java +++ b/src/test/java/xyz/ineanto/nicko/test/i18n/ItemTranslationTest.java @@ -8,7 +8,6 @@ import org.junit.jupiter.api.DisplayName; 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.I18N; import xyz.ineanto.nicko.i18n.I18NDict; import xyz.ineanto.nicko.i18n.ItemTranslation; @@ -21,11 +20,7 @@ public class ItemTranslationTest { @BeforeAll public static void setup() { - final Configuration config = new Configuration( - DefaultDataSources.SQL_EMPTY, - DefaultDataSources.REDIS_EMPTY, - "", - false); + final Configuration config = Configuration.DEFAULT; MockBukkit.mock(); MockBukkit.load(NickoBukkit.class, config); } diff --git a/src/test/java/xyz/ineanto/nicko/test/i18n/TranslationTest.java b/src/test/java/xyz/ineanto/nicko/test/i18n/TranslationTest.java index 4f0c937..759c4bd 100644 --- a/src/test/java/xyz/ineanto/nicko/test/i18n/TranslationTest.java +++ b/src/test/java/xyz/ineanto/nicko/test/i18n/TranslationTest.java @@ -7,7 +7,6 @@ import org.junit.jupiter.api.DisplayName; 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.I18N; import xyz.ineanto.nicko.i18n.I18NDict; import xyz.ineanto.nicko.i18n.Locale; @@ -17,11 +16,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class TranslationTest { @BeforeAll public static void setup() { - final Configuration config = new Configuration( - DefaultDataSources.SQL_EMPTY, - DefaultDataSources.REDIS_EMPTY, - "", - false); + final Configuration config = Configuration.DEFAULT; MockBukkit.mock(); MockBukkit.load(NickoBukkit.class, config); } diff --git a/src/test/java/xyz/ineanto/nicko/test/storage/MapCacheTest.java b/src/test/java/xyz/ineanto/nicko/test/storage/MapCacheTest.java index f940ad9..d1dc888 100644 --- a/src/test/java/xyz/ineanto/nicko/test/storage/MapCacheTest.java +++ b/src/test/java/xyz/ineanto/nicko/test/storage/MapCacheTest.java @@ -3,14 +3,13 @@ package xyz.ineanto.nicko.test.storage; import be.seeseemelk.mockbukkit.MockBukkit; import be.seeseemelk.mockbukkit.ServerMock; import be.seeseemelk.mockbukkit.entity.PlayerMock; -import xyz.ineanto.nicko.NickoBukkit; -import xyz.ineanto.nicko.config.Configuration; -import xyz.ineanto.nicko.config.DefaultDataSources; -import xyz.ineanto.nicko.profile.NickoProfile; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import xyz.ineanto.nicko.NickoBukkit; +import xyz.ineanto.nicko.config.Configuration; +import xyz.ineanto.nicko.profile.NickoProfile; import java.util.Optional; @@ -22,11 +21,7 @@ public class MapCacheTest { @BeforeAll public static void setup() { - final Configuration config = new Configuration( - DefaultDataSources.SQL_EMPTY, - DefaultDataSources.REDIS_EMPTY, - "", - false); + final Configuration config = Configuration.DEFAULT; final ServerMock server = MockBukkit.mock(); plugin = MockBukkit.load(NickoBukkit.class, config); player = server.addPlayer(); diff --git a/src/test/java/xyz/ineanto/nicko/test/storage/RedisCacheTest.java b/src/test/java/xyz/ineanto/nicko/test/storage/RedisCacheTest.java index d2f79e3..e9b2859 100644 --- a/src/test/java/xyz/ineanto/nicko/test/storage/RedisCacheTest.java +++ b/src/test/java/xyz/ineanto/nicko/test/storage/RedisCacheTest.java @@ -24,6 +24,7 @@ public class RedisCacheTest { @BeforeAll public static void setup() { final Configuration config = new Configuration( + "", DefaultDataSources.SQL_EMPTY, new DataSourceConfiguration(true, "127.0.0.1", 6379, "", ""), "", diff --git a/src/test/java/xyz/ineanto/nicko/test/storage/SQLStorageTest.java b/src/test/java/xyz/ineanto/nicko/test/storage/SQLStorageTest.java index 555e6d0..774059e 100644 --- a/src/test/java/xyz/ineanto/nicko/test/storage/SQLStorageTest.java +++ b/src/test/java/xyz/ineanto/nicko/test/storage/SQLStorageTest.java @@ -24,6 +24,7 @@ public class SQLStorageTest { @BeforeAll public static void setup() { final Configuration config = new Configuration( + "", new SQLDataSourceConfiguration(true, "127.0.0.1", 3306, "root", "12345", true), DefaultDataSources.REDIS_EMPTY, "",