From bcea10c9db08a0a5939534030f29767e6783ee40 Mon Sep 17 00:00:00 2001 From: ineanto Date: Sun, 18 May 2025 22:15:34 +0200 Subject: [PATCH] fix: data save bug --- CHANGELOG.log | 13 +++-- build.gradle.kts | 2 +- .../ineanto/nicko/appearance/Appearance.java | 8 ++++ .../ineanto/nicko/command/NickoCommand.java | 4 +- .../ineanto/nicko/profile/NickoProfile.java | 48 +++++++++++-------- .../nicko/storage/json/JSONStorage.java | 9 ++-- .../storage/json/JSONStorageProvider.java | 7 +-- .../nicko/storage/mariadb/MariaDBStorage.java | 13 +++-- .../nicko/storage/mysql/MySQLStorage.java | 13 +++-- 9 files changed, 73 insertions(+), 44 deletions(-) create mode 100644 src/main/java/xyz/ineanto/nicko/appearance/Appearance.java diff --git a/CHANGELOG.log b/CHANGELOG.log index 6056af3..ec6be1c 100644 --- a/CHANGELOG.log +++ b/CHANGELOG.log @@ -4,18 +4,23 @@ 1.2.0-RC1: Update n°12 (XX/XX/25) [FEATURES] - - Updated to support up to Minecraft 1.21.4. + - Updated to support Minecraft 1.21.5. + - Added a sub-command (/nicko about) to get information about Nicko. - Modernized the messages and added various sound effects upon interacting with the plugin. - - Made GUIs names cleaner. + - Cleaned up GUI titles. - [FIXES] - - Cleaned up the codebase to prepare for future updates. + [FIXES] - Fixed an oversight preventing the configuration from properly being migrated. + - Fixed a rare bug that could prevent data from being saved. - Fixed the placeholder item in the skin cache invalidation not being translated. + - Fixed a oversight about a column name using SQL storage. [LANGUAGE] - Moved the prefix to the language file. + [OTHER] + - Cleaned up the codebase to prepare for future updates. + 1.1.7-RC1: Hotfix n°5 (04/05/24) [OTHER] - Restored download link again on spigotmc.org diff --git a/build.gradle.kts b/build.gradle.kts index b7b8751..eb6dc02 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -44,7 +44,7 @@ dependencies { implementation("com.mysql:mysql-connector-j:9.2.0") implementation("org.mariadb.jdbc:mariadb-java-client:3.5.2") implementation("redis.clients:jedis:5.2.0") - implementation("com.google.code.gson:gson:2.10.1") + implementation("com.google.code.gson:gson:2.13.1") } tasks { diff --git a/src/main/java/xyz/ineanto/nicko/appearance/Appearance.java b/src/main/java/xyz/ineanto/nicko/appearance/Appearance.java new file mode 100644 index 0000000..9c40462 --- /dev/null +++ b/src/main/java/xyz/ineanto/nicko/appearance/Appearance.java @@ -0,0 +1,8 @@ +package xyz.ineanto.nicko.appearance; + +import javax.annotation.Nullable; + +public record Appearance( + @Nullable String name, + @Nullable String skin +) {} diff --git a/src/main/java/xyz/ineanto/nicko/command/NickoCommand.java b/src/main/java/xyz/ineanto/nicko/command/NickoCommand.java index 1063c4b..7b18427 100644 --- a/src/main/java/xyz/ineanto/nicko/command/NickoCommand.java +++ b/src/main/java/xyz/ineanto/nicko/command/NickoCommand.java @@ -30,13 +30,13 @@ public class NickoCommand implements BasicCommand { if (args.length >= 1 && args[0].equalsIgnoreCase("about")) { final Component firstAboutMessage = MiniMessage.miniMessage().deserialize( - " v", + " (© Ineanto 2023-2025) v ", Placeholder.component("prefix", playerLanguage.getPrefixComponent()), Placeholder.unparsed("version", Nicko.getInstance().getPluginMeta().getVersion()) ); final Component secondAboutMessage = MiniMessage.miniMessage().deserialize( - "Configuration v, I18N v", + "Configuration v, I18N ", Placeholder.component("prefix", playerLanguage.getPrefixComponent()), Placeholder.unparsed("configversion", Configuration.VERSION.toString()), Placeholder.unparsed("i18nversion", Language.VERSION.toString()) diff --git a/src/main/java/xyz/ineanto/nicko/profile/NickoProfile.java b/src/main/java/xyz/ineanto/nicko/profile/NickoProfile.java index bc75f78..139cff9 100644 --- a/src/main/java/xyz/ineanto/nicko/profile/NickoProfile.java +++ b/src/main/java/xyz/ineanto/nicko/profile/NickoProfile.java @@ -2,28 +2,36 @@ package xyz.ineanto.nicko.profile; import org.bukkit.entity.Player; import xyz.ineanto.nicko.Nicko; +import xyz.ineanto.nicko.appearance.Appearance; import xyz.ineanto.nicko.language.Language; import xyz.ineanto.nicko.storage.PlayerDataStore; +import java.util.Collections; +import java.util.List; import java.util.Optional; import java.util.UUID; public class NickoProfile implements Cloneable { - public static final NickoProfile EMPTY_PROFILE = new NickoProfile(null, null, Language.ENGLISH, true); + public static final NickoProfile EMPTY_PROFILE = new NickoProfile( + new Appearance(null, null), + Language.ENGLISH, + true, + Collections.emptyList() + ); private static final Nicko instance = Nicko.getInstance(); private static final PlayerDataStore dataStore = instance.getDataStore(); - private String name; - private String skin; + private Appearance appearance; private Language language; private boolean randomSkin; + private transient List favorites; - public NickoProfile(String name, String skin, Language language, boolean randomSkin) { - this.name = name; - this.skin = skin; + public NickoProfile(Appearance appearance, Language language, boolean randomSkin, List favorites) { + this.appearance = appearance; this.language = language; this.randomSkin = randomSkin; + this.favorites = favorites; } public static Optional get(Player player) { @@ -35,23 +43,31 @@ public class NickoProfile implements Cloneable { } public boolean hasData() { - return name != null || skin != null; + return appearance.name() != null || appearance.skin() != null; } public String getName() { - return name; + return appearance.name(); } public void setName(String name) { - this.name = name; + this.appearance = new Appearance(name, appearance.skin() == null ? null : appearance.skin()); } public String getSkin() { - return skin; + return appearance.skin(); } public void setSkin(String skin) { - this.skin = skin; + this.appearance = new Appearance(appearance.name() == null ? null : appearance.name(), skin); + } + + public List getFavorites() { + return favorites; + } + + public void setFavorites(List favorites) { + this.favorites = favorites; } public Language getLocale() { @@ -70,16 +86,6 @@ public class NickoProfile implements Cloneable { this.randomSkin = randomSkin; } - @Override - public String toString() { - return "NickoProfile{" + - "name='" + name + '\'' + - ", skin='" + skin + '\'' + - ", locale=" + language + - ", randomSkin=" + randomSkin + - '}'; - } - @Override public NickoProfile clone() { Object o; diff --git a/src/main/java/xyz/ineanto/nicko/storage/json/JSONStorage.java b/src/main/java/xyz/ineanto/nicko/storage/json/JSONStorage.java index 7e804be..cf7a15c 100644 --- a/src/main/java/xyz/ineanto/nicko/storage/json/JSONStorage.java +++ b/src/main/java/xyz/ineanto/nicko/storage/json/JSONStorage.java @@ -46,6 +46,7 @@ public class JSONStorage extends Storage { } } catch (IOException e) { logger.warning("Could not create file."); + e.printStackTrace(); return ActionResult.error(); } @@ -54,14 +55,12 @@ public class JSONStorage extends Storage { @Override public boolean isStored(UUID uuid) { - final File directory = new File(Nicko.getInstance().getDataFolder() + "/players/"); final File file = new File(directory, uuid.toString() + ".json"); return file.exists(); } @Override public Optional retrieve(UUID uuid) { - final File directory = new File(Nicko.getInstance().getDataFolder() + "/players/"); final File file = new File(directory, uuid.toString() + ".json"); try (FileReader fileReader = new FileReader(file)) { try (BufferedReader reader = new BufferedReader(fileReader)) { @@ -75,7 +74,6 @@ public class JSONStorage extends Storage { @Override public ActionResult delete(UUID uuid) { - final File directory = new File(Nicko.getInstance().getDataFolder() + "/players/"); final File file = new File(directory, uuid.toString() + ".json"); if (file.delete() || !file.exists()) { return ActionResult.ok(); @@ -84,6 +82,11 @@ public class JSONStorage extends Storage { } private boolean checkFileExists(File file) throws IOException { + // Additional check if the folder gets deleted while the plugin is running. + if (!file.getParentFile().exists()) { + file.getParentFile().mkdirs(); + } + if (!file.exists()) { return file.createNewFile(); } diff --git a/src/main/java/xyz/ineanto/nicko/storage/json/JSONStorageProvider.java b/src/main/java/xyz/ineanto/nicko/storage/json/JSONStorageProvider.java index c2976f3..e360b6f 100644 --- a/src/main/java/xyz/ineanto/nicko/storage/json/JSONStorageProvider.java +++ b/src/main/java/xyz/ineanto/nicko/storage/json/JSONStorageProvider.java @@ -12,11 +12,12 @@ public class JSONStorageProvider implements StorageProvider { } @Override - public boolean init() - { + public boolean init() { return directory.exists() || directory.mkdirs(); } @Override - public boolean close() { return true; } + public boolean close() { + return true; + } } diff --git a/src/main/java/xyz/ineanto/nicko/storage/mariadb/MariaDBStorage.java b/src/main/java/xyz/ineanto/nicko/storage/mariadb/MariaDBStorage.java index f2590a1..051ff03 100644 --- a/src/main/java/xyz/ineanto/nicko/storage/mariadb/MariaDBStorage.java +++ b/src/main/java/xyz/ineanto/nicko/storage/mariadb/MariaDBStorage.java @@ -1,6 +1,7 @@ package xyz.ineanto.nicko.storage.mariadb; import xyz.ineanto.nicko.appearance.ActionResult; +import xyz.ineanto.nicko.appearance.Appearance; import xyz.ineanto.nicko.config.Configuration; import xyz.ineanto.nicko.language.Language; import xyz.ineanto.nicko.profile.NickoProfile; @@ -10,6 +11,7 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Collections; import java.util.Optional; import java.util.UUID; import java.util.logging.Logger; @@ -83,15 +85,16 @@ public class MariaDBStorage extends Storage { String name = ""; String skin = ""; String locale = ""; - boolean bungeecord = false; + boolean randomSkin = false; while (resultSet.next()) { name = resultSet.getString("name"); skin = resultSet.getString("skin"); locale = resultSet.getString("locale"); - bungeecord = resultSet.getBoolean("bungeecord"); + randomSkin = resultSet.getBoolean("randomskin"); } - final NickoProfile profile = new NickoProfile(name, skin, Language.fromCode(locale), bungeecord); + // TODO (Ineanto, 17/05/2025): Retrieve favorites + final NickoProfile profile = new NickoProfile(new Appearance(name, skin), Language.fromCode(locale), randomSkin, Collections.emptyList()); return Optional.of(profile); } catch (SQLException e) { logger.warning("Couldn't fetch profile: " + e.getMessage()); @@ -117,7 +120,7 @@ public class MariaDBStorage extends Storage { } private PreparedStatement getInsertStatement(Connection connection, UUID uuid, NickoProfile profile) throws SQLException { - final String sql = "INSERT IGNORE INTO nicko.DATA (`uuid`, `name`, `skin`, `locale`, `bungeecord`) VALUES (?, ?, ?, ?, ?)"; + final String sql = "INSERT IGNORE INTO nicko.DATA (`uuid`, `name`, `skin`, `locale`, `randomskin`) VALUES (?, ?, ?, ?, ?)"; final PreparedStatement statement = connection.prepareStatement(sql); statement.setString(1, uuid.toString()); statement.setString(2, profile.getName() == null ? null : profile.getName()); @@ -128,7 +131,7 @@ public class MariaDBStorage extends Storage { } private PreparedStatement getUpdateStatement(Connection connection, UUID uuid, NickoProfile profile) throws SQLException { - final String sql = "UPDATE nicko.DATA SET name = ?, skin = ?, locale = ?, bungeecord = ? WHERE uuid = ?"; + final String sql = "UPDATE nicko.DATA SET name = ?, skin = ?, locale = ?, randomskin = ? WHERE uuid = ?"; final PreparedStatement statement = connection.prepareStatement(sql); statement.setString(1, profile.getName() == null ? null : profile.getName()); statement.setString(2, profile.getSkin() == null ? null : profile.getSkin()); diff --git a/src/main/java/xyz/ineanto/nicko/storage/mysql/MySQLStorage.java b/src/main/java/xyz/ineanto/nicko/storage/mysql/MySQLStorage.java index bd3d22a..c1f0313 100644 --- a/src/main/java/xyz/ineanto/nicko/storage/mysql/MySQLStorage.java +++ b/src/main/java/xyz/ineanto/nicko/storage/mysql/MySQLStorage.java @@ -1,6 +1,7 @@ package xyz.ineanto.nicko.storage.mysql; import xyz.ineanto.nicko.appearance.ActionResult; +import xyz.ineanto.nicko.appearance.Appearance; import xyz.ineanto.nicko.config.Configuration; import xyz.ineanto.nicko.language.Language; import xyz.ineanto.nicko.profile.NickoProfile; @@ -10,6 +11,7 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Collections; import java.util.Optional; import java.util.UUID; import java.util.logging.Logger; @@ -83,15 +85,16 @@ public class MySQLStorage extends Storage { String name = ""; String skin = ""; String locale = ""; - boolean bungeecord = false; + boolean randomSkin = false; while (resultSet.next()) { name = resultSet.getString("name"); skin = resultSet.getString("skin"); locale = resultSet.getString("locale"); - bungeecord = resultSet.getBoolean("bungeecord"); + randomSkin = resultSet.getBoolean("randomskin"); } - final NickoProfile profile = new NickoProfile(name, skin, Language.fromCode(locale), bungeecord); + // TODO (Ineanto, 17/05/2025): Retrieve favorites + final NickoProfile profile = new NickoProfile(new Appearance(name, skin), Language.fromCode(locale), randomSkin, Collections.emptyList()); return Optional.of(profile); } catch (SQLException e) { logger.warning("Couldn't fetch profile: " + e.getMessage()); @@ -117,7 +120,7 @@ public class MySQLStorage extends Storage { } private PreparedStatement getInsertStatement(Connection connection, UUID uuid, NickoProfile profile) throws SQLException { - final String sql = "INSERT IGNORE INTO nicko.DATA (`uuid`, `name`, `skin`, `locale`, `bungeecord`) VALUES (?, ?, ?, ?, ?)"; + final String sql = "INSERT IGNORE INTO nicko.DATA (`uuid`, `name`, `skin`, `locale`, `randomskin`) VALUES (?, ?, ?, ?, ?)"; final PreparedStatement statement = connection.prepareStatement(sql); statement.setString(1, uuid.toString()); statement.setString(2, profile.getName() == null ? null : profile.getName()); @@ -128,7 +131,7 @@ public class MySQLStorage extends Storage { } private PreparedStatement getUpdateStatement(Connection connection, UUID uuid, NickoProfile profile) throws SQLException { - final String sql = "UPDATE nicko.DATA SET name = ?, skin = ?, locale = ?, bungeecord = ? WHERE uuid = ?"; + final String sql = "UPDATE nicko.DATA SET name = ?, skin = ?, locale = ?, randomskin = ? WHERE uuid = ?"; final PreparedStatement statement = connection.prepareStatement(sql); statement.setString(1, profile.getName() == null ? null : profile.getName()); statement.setString(2, profile.getSkin() == null ? null : profile.getSkin());