diff --git a/core/src/main/java/net/artelnatif/nicko/bukkit/event/PlayerQuitListener.java b/core/src/main/java/net/artelnatif/nicko/bukkit/event/PlayerQuitListener.java index 0794fdb..f713f00 100644 --- a/core/src/main/java/net/artelnatif/nicko/bukkit/event/PlayerQuitListener.java +++ b/core/src/main/java/net/artelnatif/nicko/bukkit/event/PlayerQuitListener.java @@ -1,6 +1,7 @@ package net.artelnatif.nicko.bukkit.event; import net.artelnatif.nicko.bukkit.NickoBukkit; +import net.artelnatif.nicko.disguise.ActionResult; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -10,6 +11,9 @@ public class PlayerQuitListener implements Listener { @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { final Player player = event.getPlayer(); - NickoBukkit.getInstance().getNicko().getDataStore().saveData(player); + final ActionResult result = NickoBukkit.getInstance().getNicko().getDataStore().saveData(player); + if (result.isError()) { + NickoBukkit.getInstance().getLogger().warning("Failed to save data for " + player.getName()); + } } } diff --git a/core/src/main/java/net/artelnatif/nicko/bukkit/gui/admin/cache/CacheDetailledGUI.java b/core/src/main/java/net/artelnatif/nicko/bukkit/gui/admin/cache/CacheDetailledGUI.java index 0726a3f..1849cde 100644 --- a/core/src/main/java/net/artelnatif/nicko/bukkit/gui/admin/cache/CacheDetailledGUI.java +++ b/core/src/main/java/net/artelnatif/nicko/bukkit/gui/admin/cache/CacheDetailledGUI.java @@ -30,7 +30,7 @@ public class CacheDetailledGUI { final List loadedSkins = skins.entrySet().stream() .filter(entry -> entry.getValue().isPresent()) .map(Map.Entry::getKey) - .toList(); + .collect(Collectors.toList()); final List items = loadedSkins.stream() .map(SkinPlaceholder::new) diff --git a/core/src/main/java/net/artelnatif/nicko/bukkit/i18n/I18N.java b/core/src/main/java/net/artelnatif/nicko/bukkit/i18n/I18N.java index 093f965..bf81fc3 100644 --- a/core/src/main/java/net/artelnatif/nicko/bukkit/i18n/I18N.java +++ b/core/src/main/java/net/artelnatif/nicko/bukkit/i18n/I18N.java @@ -16,7 +16,7 @@ public class I18N { final NickoBukkit instance = NickoBukkit.getInstance(); try { final Optional profile = instance.getNicko().getDataStore().getData(player.getUniqueId()); - return profile.isEmpty() ? Locale.FALLBACK_LOCALE : profile.get().getLocale(); + return !profile.isPresent() ? Locale.FALLBACK_LOCALE : profile.get().getLocale(); } catch (IllegalArgumentException exception) { instance.getLogger().severe("Invalid locale provided by " + player.getName() + ", defaulting to " + Locale.FALLBACK_LOCALE.getCode() + "."); return Locale.FALLBACK_LOCALE; diff --git a/core/src/main/java/net/artelnatif/nicko/bukkit/i18n/Locale.java b/core/src/main/java/net/artelnatif/nicko/bukkit/i18n/Locale.java index 9894aa7..5402b0f 100644 --- a/core/src/main/java/net/artelnatif/nicko/bukkit/i18n/Locale.java +++ b/core/src/main/java/net/artelnatif/nicko/bukkit/i18n/Locale.java @@ -5,7 +5,7 @@ import java.io.Serializable; public enum Locale implements Serializable { ENGLISH("en", "English"), FRENCH("fr", "Français"), - CUSTOM("custom", "Server Custom"); + CUSTOM("cm", "Server Custom"); public static final Locale FALLBACK_LOCALE = ENGLISH; @@ -17,6 +17,13 @@ public enum Locale implements Serializable { this.name = name; } + public static Locale fromCode(String code) { + for (Locale value : values()) { + if (code.equals(value.code)) return value; + } + return ENGLISH; + } + public String getCode() { return code; } diff --git a/core/src/main/java/net/artelnatif/nicko/disguise/ActionResult.java b/core/src/main/java/net/artelnatif/nicko/disguise/ActionResult.java index a5eded8..60101c9 100644 --- a/core/src/main/java/net/artelnatif/nicko/disguise/ActionResult.java +++ b/core/src/main/java/net/artelnatif/nicko/disguise/ActionResult.java @@ -7,16 +7,18 @@ public class ActionResult { private boolean error = false; private R result; - public ActionResult(I18NDict errorMessage) { - this.error = true; - this.errorMessage = errorMessage; - } - public ActionResult() { this.errorMessage = null; } - public void setResult(R result) { + public ActionResult(I18NDict errorMessage) { + this.errorMessage = errorMessage; + this.error = true; + this.result = null; + } + + public ActionResult(R result) { + this.errorMessage = null; this.result = result; } diff --git a/core/src/main/java/net/artelnatif/nicko/impl/Internals.java b/core/src/main/java/net/artelnatif/nicko/impl/Internals.java index 4f716d7..ffdc4b3 100644 --- a/core/src/main/java/net/artelnatif/nicko/impl/Internals.java +++ b/core/src/main/java/net/artelnatif/nicko/impl/Internals.java @@ -26,13 +26,7 @@ public interface Internals { final Optional uuid = mojang.getUUID(profile.getSkin()); if (uuid.isPresent()) { skin = (reset ? mojang.getSkinWithoutCaching(uuid.get()) : mojang.getSkin(uuid.get())); - if (skin.isEmpty()) { - return new ActionResult<>(I18NDict.Error.SKIN_FAIL_MOJANG); - } - - final ActionResult actionResult = new ActionResult<>(); - actionResult.setResult(skin.get()); - return actionResult; + return skin.map(ActionResult::new).orElseGet(() -> new ActionResult<>(I18NDict.Error.SKIN_FAIL_MOJANG)); } return new ActionResult<>(I18NDict.Error.NAME_FAIL_MOJANG); } catch (ExecutionException e) { diff --git a/core/src/main/java/net/artelnatif/nicko/impl/InternalsProvider.java b/core/src/main/java/net/artelnatif/nicko/impl/InternalsProvider.java index f66428d..66c45f8 100644 --- a/core/src/main/java/net/artelnatif/nicko/impl/InternalsProvider.java +++ b/core/src/main/java/net/artelnatif/nicko/impl/InternalsProvider.java @@ -1,7 +1,9 @@ package net.artelnatif.nicko.impl; +import net.artelnatif.nicko.bukkit.NickoBukkit; import org.bukkit.Bukkit; +import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; public class InternalsProvider { @@ -12,7 +14,15 @@ public class InternalsProvider { final String packageName = Internals.class.getPackage().getName(); final String bukkitVersion = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; final String fullClassName = packageName + "." + bukkitVersion; - internals = (Internals) Class.forName(fullClassName).getConstructors()[0].newInstance(); + final Class clazz = Class.forName(fullClassName); + internals = (Internals) clazz.getConstructors()[0].newInstance(); + for (Annotation annotation : clazz.getDeclaredAnnotations()) { + if (annotation instanceof Unstable) { + NickoBukkit.getInstance().getLogger().warning("Version " + Bukkit.getVersion() + " has been marked as unstable."); + NickoBukkit.getInstance().getLogger().warning("Nicko might not work at all until the version has been tested extensively."); + NickoBukkit.getInstance().getLogger().warning("Proceed with caution!"); + } + } } catch (InvocationTargetException | ClassNotFoundException | InstantiationException | IllegalAccessException | ClassCastException exception) { internals = null; diff --git a/core/src/main/java/net/artelnatif/nicko/impl/Unstable.java b/core/src/main/java/net/artelnatif/nicko/impl/Unstable.java new file mode 100644 index 0000000..ef36369 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/impl/Unstable.java @@ -0,0 +1,7 @@ +package net.artelnatif.nicko.impl; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +public @interface Unstable { } diff --git a/core/src/main/java/net/artelnatif/nicko/storage/PlayerDataStore.java b/core/src/main/java/net/artelnatif/nicko/storage/PlayerDataStore.java index 233f1bf..270179b 100644 --- a/core/src/main/java/net/artelnatif/nicko/storage/PlayerDataStore.java +++ b/core/src/main/java/net/artelnatif/nicko/storage/PlayerDataStore.java @@ -1,6 +1,8 @@ package net.artelnatif.nicko.storage; import net.artelnatif.nicko.Nicko; +import net.artelnatif.nicko.bukkit.i18n.I18NDict; +import net.artelnatif.nicko.disguise.ActionResult; import net.artelnatif.nicko.disguise.NickoProfile; import net.artelnatif.nicko.mojang.MojangUtils; import net.artelnatif.nicko.storage.json.JSONStorage; @@ -76,12 +78,13 @@ public class PlayerDataStore { } } - public void saveData(Player player) { - if (storage.isError()) { return; } - if (!profiles.containsKey(player.getUniqueId())) { return; } + public ActionResult saveData(Player player) { + if (storage.isError()) { return new ActionResult<>(I18NDict.Error.UNEXPECTED_ERROR); } + if (!profiles.containsKey(player.getUniqueId())) { return new ActionResult<>(I18NDict.Error.UNEXPECTED_ERROR); } - storage.store(player.getUniqueId(), profiles.get(player.getUniqueId())); + final ActionResult store = storage.store(player.getUniqueId(), profiles.get(player.getUniqueId())); profiles.remove(player.getUniqueId()); + return store; } public Storage getStorage() { diff --git a/core/src/main/java/net/artelnatif/nicko/storage/sql/SQLStorage.java b/core/src/main/java/net/artelnatif/nicko/storage/sql/SQLStorage.java index 177df31..800de11 100644 --- a/core/src/main/java/net/artelnatif/nicko/storage/sql/SQLStorage.java +++ b/core/src/main/java/net/artelnatif/nicko/storage/sql/SQLStorage.java @@ -1,18 +1,17 @@ package net.artelnatif.nicko.storage.sql; -import com.google.common.io.ByteStreams; import net.artelnatif.nicko.Nicko; import net.artelnatif.nicko.bukkit.i18n.I18NDict; +import net.artelnatif.nicko.bukkit.i18n.Locale; import net.artelnatif.nicko.disguise.ActionResult; import net.artelnatif.nicko.disguise.NickoProfile; import net.artelnatif.nicko.storage.Storage; import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; import java.nio.ByteBuffer; import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.util.Optional; import java.util.UUID; @@ -40,13 +39,14 @@ public class SQLStorage extends Storage { if (connection == null) return new ActionResult<>(I18NDict.Error.SQL_ERROR); try { - final String sql = "INSERT IGNORE INTO nicko.DATA (`uuid`, `name`, `skin`, `bungeecord`) VALUES (?, ?, ?, ?)"; + final String sql = "INSERT IGNORE INTO nicko.DATA (`uuid`, `name`, `skin`, `locale`, `bungeecord`) VALUES (?, ?, ?, ?, ?)"; final PreparedStatement statement = connection.prepareStatement(sql); statement.setBinaryStream(1, uuidToBin(uuid)); statement.setString(2, profile.getName()); statement.setString(3, profile.getSkin()); - statement.setBoolean(4, profile.isBungeecordTransfer()); + statement.setString(4, profile.getLocale().getCode()); + statement.setBoolean(5, profile.isBungeecordTransfer()); statement.executeUpdate(); return new ActionResult<>(); } catch (SQLException e) { @@ -57,12 +57,52 @@ public class SQLStorage extends Storage { @Override public boolean isStored(UUID uuid) { - return false; + final Connection connection = getProvider().getConnection(); + if (connection == null) return false; + + try { + final String sql = "SELECT * FROM nicko.DATA WHERE uuid = ?"; + + final PreparedStatement statement = connection.prepareStatement(sql); + statement.setBinaryStream(1, uuidToBin(uuid)); + + final ResultSet resultSet = statement.executeQuery(); + return resultSet.next(); + } catch (SQLException e) { + nicko.getLogger().warning("Couldn't check if data is present: " + e.getMessage()); + return false; + } } @Override public Optional retrieve(UUID uuid) { - return Optional.empty(); + final Connection connection = getProvider().getConnection(); + if (connection == null) return Optional.empty(); + + try { + final String sql = "SELECT * FROM nicko.DATA WHERE uuid = ?"; + + final PreparedStatement statement = connection.prepareStatement(sql); + statement.setBinaryStream(1, uuidToBin(uuid)); + + final ResultSet resultSet = statement.executeQuery(); + String name = ""; + String skin = ""; + String locale = ""; + boolean bungeecord = false; + while(resultSet.next()) { + name = resultSet.getString("name"); + skin = resultSet.getString("skin"); + locale = resultSet.getString("locale"); + bungeecord = resultSet.getBoolean("bungeecord"); + } + + final NickoProfile profile = new NickoProfile(name, skin, Locale.fromCode(locale), bungeecord); + return Optional.of(profile); + } catch (SQLException e) { + nicko.getLogger().warning("Couldn't fetch profile: " + e.getMessage()); + return Optional.empty(); + } } private ByteArrayInputStream uuidToBin(UUID uuid) { @@ -72,13 +112,4 @@ public class SQLStorage extends Storage { .putLong(uuid.getLeastSignificantBits()); return new ByteArrayInputStream(bytes); } - - private UUID binToUUID(InputStream stream) { - final ByteBuffer buffer = ByteBuffer.allocate(16); - try { - buffer.put(ByteStreams.toByteArray(stream)); - buffer.flip(); - return new UUID(buffer.getLong(), buffer.getLong()); - } catch (IOException ignored) { return null; } - } } diff --git a/core/src/main/java/net/artelnatif/nicko/storage/sql/SQLStorageProvider.java b/core/src/main/java/net/artelnatif/nicko/storage/sql/SQLStorageProvider.java index 314b55b..67aa77f 100644 --- a/core/src/main/java/net/artelnatif/nicko/storage/sql/SQLStorageProvider.java +++ b/core/src/main/java/net/artelnatif/nicko/storage/sql/SQLStorageProvider.java @@ -55,7 +55,13 @@ public class SQLStorageProvider implements StorageProvider { private void createTable() throws SQLException { final Connection connection = getConnection(); - String query = "CREATE TABLE IF NOT EXISTS %s.DATA (uuid binary(16) NOT NULL,name varchar(16) NOT NULL,skin varchar(16) NOT NULL,bungeecord boolean NOT NULL,PRIMARY KEY (UUID))"; + String query = "CREATE TABLE IF NOT EXISTS %s.DATA " + + "(uuid binary(16) NOT NULL," + + "name varchar(16) NOT NULL," + + "skin varchar(16) NOT NULL," + + "locale char(2) NOT NULL," + + "bungeecord boolean NOT NULL," + + "PRIMARY KEY (UUID))"; query = query.replace("%s", schemaName); final PreparedStatement statement = connection.prepareStatement(query); diff --git a/core/src/test/java/net/artelnatif/nicko/test/storage/BrokenSQLTest.java b/core/src/test/java/net/artelnatif/nicko/test/storage/BrokenSQLTest.java index 1da2f82..2c736ea 100644 --- a/core/src/test/java/net/artelnatif/nicko/test/storage/BrokenSQLTest.java +++ b/core/src/test/java/net/artelnatif/nicko/test/storage/BrokenSQLTest.java @@ -7,12 +7,14 @@ import net.artelnatif.nicko.bukkit.NickoBukkit; import net.artelnatif.nicko.config.Configuration; import net.artelnatif.nicko.disguise.ActionResult; import net.artelnatif.nicko.disguise.NickoProfile; -import net.artelnatif.nicko.bukkit.i18n.Locale; import org.junit.jupiter.api.*; +import java.util.Optional; + public class BrokenSQLTest { private static ServerMock server; private static NickoBukkit plugin; + private static PlayerMock player; @BeforeAll public static void setup() { @@ -26,6 +28,7 @@ public class BrokenSQLTest { false); server = MockBukkit.mock(); plugin = MockBukkit.load(NickoBukkit.class, config); + player = server.addPlayer(); } @Test @@ -37,10 +40,17 @@ public class BrokenSQLTest { @Test @DisplayName("Fail to Store Player Via SQL") public void storePlayer() { - final PlayerMock playerMock = server.addPlayer(); - final NickoProfile profile = new NickoProfile("Notch", "Notch", Locale.ENGLISH, true); - final ActionResult storeAction = plugin.getNicko().getDataStore().getStorage().store(playerMock.getUniqueId(), profile); - Assertions.assertTrue(storeAction.isError()); + final Optional optionalProfile = plugin.getNicko().getDataStore().getData(player.getUniqueId()); + Assertions.assertFalse(optionalProfile.isPresent()); + ActionResult result = plugin.getNicko().getDataStore().saveData(player); + Assertions.assertTrue(result.isError()); + } + + @Test + @DisplayName("Fail to Retrieve Player Via SQL") + public void retrievePlayer() { + final Optional storeAction = plugin.getNicko().getDataStore().getData(player.getUniqueId()); + Assertions.assertFalse(storeAction.isPresent()); } @AfterAll diff --git a/core/src/test/java/net/artelnatif/nicko/test/storage/SQLStorageTest.java b/core/src/test/java/net/artelnatif/nicko/test/storage/SQLStorageTest.java index f48f3a0..2d6d8ca 100644 --- a/core/src/test/java/net/artelnatif/nicko/test/storage/SQLStorageTest.java +++ b/core/src/test/java/net/artelnatif/nicko/test/storage/SQLStorageTest.java @@ -10,9 +10,12 @@ import net.artelnatif.nicko.disguise.NickoProfile; import net.artelnatif.nicko.bukkit.i18n.Locale; import org.junit.jupiter.api.*; +import java.util.Optional; + public class SQLStorageTest { private static ServerMock server; private static NickoBukkit plugin; + private static PlayerMock player; @BeforeAll public static void setup() { @@ -26,6 +29,7 @@ public class SQLStorageTest { false); server = MockBukkit.mock(); plugin = MockBukkit.load(NickoBukkit.class, config); + player = server.addPlayer(); } @Test @@ -37,14 +41,30 @@ public class SQLStorageTest { @Test @DisplayName("Store Player Via SQL") public void storePlayer() { - final PlayerMock playerMock = server.addPlayer(); - final NickoProfile profile = new NickoProfile("Notch", "Notch", Locale.ENGLISH, true); - final ActionResult storeAction = plugin.getNicko().getDataStore().getStorage().store(playerMock.getUniqueId(), profile); - Assertions.assertFalse(storeAction.isError()); + final Optional optionalProfile = plugin.getNicko().getDataStore().getData(player.getUniqueId()); + Assertions.assertTrue(optionalProfile.isPresent()); + + final NickoProfile profile = optionalProfile.get(); + profile.setName("Notch"); + profile.setSkin("Notch"); + profile.setLocale(Locale.ENGLISH); + profile.setBungeecordTransfer(true); + ActionResult result = plugin.getNicko().getDataStore().saveData(player); + Assertions.assertFalse(result.isError()); + } + + @Test + @DisplayName("Retrieve Player Via SQL") + public void retrievePlayer() { + final Optional storeAction = plugin.getNicko().getDataStore().getData(player.getUniqueId()); + Assertions.assertTrue(storeAction.isPresent()); + final NickoProfile profile = storeAction.get(); + Assertions.assertEquals("Notch", profile.getName()); + Assertions.assertEquals(Locale.ENGLISH, profile.getLocale()); } @AfterAll public static void shutdown() { MockBukkit.unmock(); } -} +} \ No newline at end of file diff --git a/v1_19_R2/src/main/java/net/artelnatif/nicko/impl/v1_19_R2.java b/v1_19_R2/src/main/java/net/artelnatif/nicko/impl/v1_19_R2.java index 288b95f..2741305 100644 --- a/v1_19_R2/src/main/java/net/artelnatif/nicko/impl/v1_19_R2.java +++ b/v1_19_R2/src/main/java/net/artelnatif/nicko/impl/v1_19_R2.java @@ -26,6 +26,7 @@ import org.bukkit.event.player.PlayerTeleportEvent; import java.lang.reflect.Field; import java.util.*; +@Unstable public class v1_19_R2 implements Internals { @Override public void updateSelf(Player player) {