diff --git a/.gitignore b/.gitignore index 8a238de..d7a802e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,50 @@ -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ -### IntelliJ IDEA ### -.idea/ -*.iws +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +# Idea files *.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ +# Nicko first pass build files +core/target +v1_14_R1/target +v1_15_R1/target +v1_16_R1/target +v1_16_R2/target +v1_16_R3/target +v1_17_R1/target +v1_18_R1/target +v1_18_R2/target +v1_19_R1/target -### Server ### -run/ \ No newline at end of file +# Idea Folder +.idea + +# Build folder +target + +# Maven Dependency Reduced Pom +dist/dependency-reduced-pom.xml +core/dependency-reduced-pom.xml \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 95e68fb..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 1632839..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index ba18467..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml deleted file mode 100644 index 2b63946..0000000 --- a/.idea/uiDesigner.xml +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/CHANGELOG.log b/CHANGELOG.log deleted file mode 100644 index 088f3a2..0000000 --- a/CHANGELOG.log +++ /dev/null @@ -1,129 +0,0 @@ -1.3.0-RC1: Update n°13 (XX/XX/25) - [FEATURES] - - Players are now able to mark disguises as favorites. - - [FIXES] - - Fixed a bug where a player was improperly named when inspecting it in the admin panel. - - Added a missing sound when players undisguised. - -1.2.0-RC1: Update n°12 (XX/XX/25) - [FEATURES] - - 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. - - Cleaned up GUI titles. - - [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 - -1.1.6-RC1: Update n°11 (04/05/24) - [FEATURES] - - Update dependencies in preparation to the 1.20.5 update - - [OTHER] - - Restored download link on spigotmc.org - -1.1.5-RC1: Update n°10 (25/12/23) - [FEATURES] - - Various improvements to performance. - [FIXES] - - Fixed a bug related to configuration migration. - -1.1.4-RC1: Update n°9 (07/02/23) - [OTHER] - - The repository hosting the previous version of Nicko had expired, this is now fixed. - -1.1.3-RC1: Hotfix n°4 (28/12/23) - [FIXES] - - Fixed the English Locale version being late. - -1.1.2-RC1: Update n°8 (28/12/23) - [FEATURES] - - Players now default back to their original appearance upon failure. - - [FIXES] - - Fixed an invalid placeholder parameter (%nicko_random_skin% now gets if the player has random skin on login set or not). - - Fixed the error reason not appearing upon failure. - - Fixed player profiles (name and skin associated) not being reset gracefully upon failure. - - Fixed error messages not being precise enough. - - Various optimizations and improvements. - -1.1.1-RC1: Update n°7 (27/12/23) - [FEATURES] - - Made Nicko compatible with 1.20.3 and 1.20.4. - - -1.1.0-RC1: Update n°6 (23/12/23) - [BREAKING] - - The language system has been updated to use the Adventure library (https://docs.advntr.dev/index.html). This results in the custom locale 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] - - 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 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. - - [OTHER] - - Various optimizations and improvements. - - Internal refactoring - -1.0.7-RC1: Update n°4 (13/12/23) - [OTHER] - - In line with my thinking that Minecraft servers should always be in one of the latest versions to give developers more freedom and less maintenance hassle, Nicko will now only be supporting the current major version and the one before it. This results in this version of Nicko now needing at minimum Java version 17 and a server running 1.19. If you can't upgrade, consider myself sorry. - - Various optimizations and improvements following the upgrade to Java 17. - -1.0.6-RC1: Update n°3 (11/12/23) - [OTHER] - - Added telemetry via bStats to gather useful informations about Nicko. This feature is optional and can be disabled inside the "bStats" folder found in plugins folder. Informations gathered are public record and can be found at: https://bstats.org/plugin/bukkit/Nicko/20483. - -1.0.5-RC1: Update n°2 (11/12/23) - [OTHER] - - Moved plugin to the Gradle build chain, resulting in faster builds and smaller Jar. This has no consequences for players. - -1.0.4-RC1: - [FEATURES] - - The players check GUI is now updated upon player's joining and leaving - - Administrators are now able to remove a player's disguise through the player check GUI - -1.0.3-RC1: Hotfix n°3 (07/12/23) - [FIXES] - - Fixed a visual bug where players in survival mode were seeing themselves as having full health and hunger after disguising. - -1.0.2-RC1: Hotfix n°2 (06/12/23) - [OTHER] - - Internal refactoring - -1.0.1-RC1: Hotfix n°1 (06/12/23) - [FIXES] - - Fixed an issue when joining and players being disguised were not for the player joining. - - diff --git a/README.md b/README.md index 1d25acd..850d86b 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,14 @@ -# *Nicko* +# _Nicko_ -## The feature packed, next generation disguise plugin for Minecraft. +## The next-generation, most feature-packed disguise plugin for Minecraft. ---- +### Download: -## Download: - -https://www.spigotmc.org/resources/nicko.113868/ - ---- - -## Known bugs: - -- Players who have operator (OP) status lose access to the Operator Items tab in creative mode - after disguising **(1.20 and up)**. -- When disguising and only changing their display name, players will have the new default - skins **(1.20 and up)**. - ---- +Coming soon! ⏳ #### Version compatibility table -| Version | Plugin | -|---------------|----------------------------------------------------------------------------| -| 1.7 and lower | Unsupported | -| 1.8 - 1.12.2 | Use [NickReloaded](https://www.spigotmc.org/resources/nickreloaded.46335/) | -| 1.13 to 1.19 | Unsupported | -| 1.20 - 1.21 | Use Nicko | +| Version | Supported | +|-----------|----------------------------------------------------------------------------| +| < 1.12.2 | Use [NickReloaded](https://www.spigotmc.org/resources/nickreloaded.46335/) | +| \> 1.12.2 | Supported | \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index ae23b63..0000000 --- a/build.gradle.kts +++ /dev/null @@ -1,106 +0,0 @@ -plugins { - id("java") - id("com.gradleup.shadow") version "8.3.2" - id("xyz.jpenilla.run-paper") version "2.3.0" - id("io.papermc.paperweight.userdev") version "2.0.0-beta.17" -} - -group = "xyz.ineanto" -version = "1.2.0" - -val invuiVersion: String = "1.44" - -java { - sourceCompatibility = JavaVersion.VERSION_22 - targetCompatibility = JavaVersion.VERSION_22 - - toolchain { - languageVersion = JavaLanguageVersion.of(22) - } -} - -repositories { - mavenCentral() - mavenLocal() - - maven("https://repo.xenondevs.xyz/releases") - maven("https://repo.papermc.io/repository/maven-public/") - maven("https://repo.codemc.io/repository/maven-snapshots/") - maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") -} - -dependencies { - paperweight.paperDevBundle("1.21.5-R0.1-SNAPSHOT") - - compileOnly("me.clip:placeholderapi:2.11.5") - compileOnly("net.kyori:adventure-api:4.21.0") - compileOnly("xyz.xenondevs.invui:invui-core:$invuiVersion") - compileOnly("net.wesjd:anvilgui:1.10.4-SNAPSHOT") - compileOnly("com.comphenix.protocol:ProtocolLib:5.4.0-SNAPSHOT") - - implementation("de.rapha149.signgui:signgui:2.5.0") - implementation("com.github.jsixface:yamlconfig:1.2") - implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.1") - implementation("com.fasterxml.jackson.core:jackson-core:2.18.1") - 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.13.1") -} - -tasks { - - processResources { - from("src/main/resources") - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - filesMatching("*.yml") { - expand("version" to version) - } - } - - shadowJar { - // RELOCATIONS - relocate("net.wesjd", "xyz.ineanto.nicko.libs.anvilgui") - relocate("com.github.jsixface", "xyz.ineanto.nicko.libs.yaml") - relocate("me.clip", "xyz.ineanto.nicko.libs.placeholderapi") - relocate("com.fasterxml.jackson", "xyz.ineanto.nicko.libs.jackson") - relocate("com.mysql", "xyz.ineanto.nicko.libs.mysql") - relocate("org.mariadb.jdbc", "xyz.ineanto.nicko.libs.mariadb") - relocate("redis.clients", "xyz.ineanto.nicko.libs.redis") - relocate("com.google.gson", "xyz.ineanto.nicko.libs.gson") - relocate("org.apache.commons.pool2", "xyz.ineanto.nicko.libs.pool2") - - // EXCLUSIONS - exclude("colors.bin") - exclude("waffle/**") - exclude("com/sun/**") - exclude("com/google/protobuf/**") - exclude("com/google/errorprone/**") - exclude("org/apache/commons/logging/**") - exclude("org/jetbrains/**") - exclude("org/intellij/**") - exclude("org/checkerframework/**") - exclude("org/json/**") - exclude("org/slf4j/**") - exclude("org/yaml/**") - exclude("google/protobuf/**") - exclude("net/kyori/**") - - // MINIFY - minimize { - exclude(dependency("xyz.xenondevs.invui:.*")) - exclude(dependency("de.rapha149.signgui:.*")) - } - } - - runServer { - downloadPlugins { - url("https://download.luckperms.net/1593/bukkit/loader/LuckPerms-Bukkit-5.5.8.jar") - - // 1.20.5 - latest testing - url("https://ci.dmulloy2.net/job/ProtocolLib/lastSuccessfulBuild/artifact/build/libs/ProtocolLib.jar") - } - - minecraftVersion("1.21.5") - } -} \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml new file mode 100644 index 0000000..6c98136 --- /dev/null +++ b/core/pom.xml @@ -0,0 +1,178 @@ + + + 4.0.0 + + core + 1.0-SNAPSHOT + + + net.artelnatif + nicko-parent + 1.0-SNAPSHOT + + + + UTF-8 + + + + + papermc + https://repo.papermc.io/repository/maven-public/ + + + spigot-repo + https://hub.spigotmc.org/nexus/content/groups/public/ + + + codemc-snapshots + https://repo.codemc.io/repository/maven-snapshots/ + + + placeholderapi + https://repo.extendedclip.com/content/repositories/placeholderapi/ + + + + + + + me.clip + placeholderapi + 2.11.2 + provided + + + + org.spigotmc + spigot-api + 1.19.4-R0.1-SNAPSHOT + provided + + + + xyz.xenondevs.invui + invui + 1.0-SNAPSHOT + + + + net.wesjd + anvilgui + 1.6.3-SNAPSHOT + + + + com.google.guava + guava + 31.1-jre + provided + + + + com.github.seeseemelk + MockBukkit-v1.19 + 2.29.0 + test + + + + org.mariadb.jdbc + mariadb-java-client + 3.1.2 + + + + com.github.jsixface + yamlconfig + 1.1.2 + + + com.fasterxml.jackson.core + jackson-core + 2.14.2 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.14.2 + + + + redis.clients + jedis + 4.3.0 + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M7 + + + org.apache.maven.plugins + maven-shade-plugin + 3.3.1-SNAPSHOT + + + package + + shade + + + + + net.wesjd:anvilgui + xyz.xenondevs.invui:* + com.github.jsixface:* + com.fasterxml.jackson.dataformat + com.fasterxml.jackson.core + org.mariadb.jdbc + + + + + net.wesjd.anvilgui + net.artelnatif.libs.anvilgui + + + xyz.xenondevs.invui + net.artelnatif.libs.invui + + + com.github.jsixface + net.artelnatif.libs.yaml + + + com.fasterxml.jackson.dataformat + net.artelnatif.libs.jackson.yaml + + + com.fasterxml.jackson.core + net.artelnatif.libs.jackson.core + + + org.mariadb.jdbc + net.artelnatif.libs.mariadb + + + + false + + + + + + + + true + ${basedir}/src/main/resources/ + + + + \ No newline at end of file diff --git a/core/src/main/java/net/artelnatif/nicko/NickoBukkit.java b/core/src/main/java/net/artelnatif/nicko/NickoBukkit.java new file mode 100644 index 0000000..b5cc0ec --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/NickoBukkit.java @@ -0,0 +1,167 @@ +package net.artelnatif.nicko; + +import net.artelnatif.nicko.gui.items.common.OptionUnavailable; +import xyz.xenondevs.invui.gui.structure.Structure; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.SimpleItem; +import net.artelnatif.nicko.command.NickoCommand; +import net.artelnatif.nicko.config.Configuration; +import net.artelnatif.nicko.config.ConfigurationManager; +import net.artelnatif.nicko.event.PlayerJoinListener; +import net.artelnatif.nicko.event.PlayerQuitListener; +import net.artelnatif.nicko.gui.items.main.ExitGUI; +import net.artelnatif.nicko.i18n.Locale; +import net.artelnatif.nicko.i18n.LocaleFileManager; +import net.artelnatif.nicko.impl.Internals; +import net.artelnatif.nicko.impl.InternalsProvider; +import net.artelnatif.nicko.mojang.MojangAPI; +import net.artelnatif.nicko.placeholder.PlaceHolderHook; +import net.artelnatif.nicko.storage.PlayerDataStore; +import net.artelnatif.nicko.storage.name.PlayerNameStore; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.command.PluginCommand; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.plugin.java.JavaPluginLoader; + +import java.io.File; +import java.io.IOException; + +public class NickoBukkit extends JavaPlugin { + private static NickoBukkit plugin; + + private final boolean unitTesting; + + private MojangAPI mojangAPI; + private PlayerDataStore dataStore; + private ConfigurationManager configurationManager; + private Configuration configuration; + private LocaleFileManager localeFileManager; + private PlayerNameStore nameStore; + + public NickoBukkit() { this.unitTesting = false; } + + /** + * Used by MockBukkit + */ + protected NickoBukkit(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) { + this(loader, description, dataFolder, file, null); + } + + /** + * Used by MockBukkit + */ + protected NickoBukkit(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file, Configuration configuration) { + super(loader, description, dataFolder, file); + unitTesting = true; + this.configuration = configuration; + getLogger().info("Unit Testing Mode enabled."); + } + + @Override + public void onEnable() { + plugin = this; + configurationManager = new ConfigurationManager(getDataFolder()); + configurationManager.saveDefaultConfig(); + + mojangAPI = new MojangAPI(); + dataStore = new PlayerDataStore(mojangAPI, getNickoConfig()); + nameStore = new PlayerNameStore(); + + if (!dataStore.getStorage().isError()) { + getLogger().info("Loading persistence..."); + if (!dataStore.getStorage().getProvider().init()) { + dataStore.getStorage().setError(true); + getLogger().severe("Failed to open persistence, data will NOT be saved!"); + } + } + + if (!unitTesting) { + getLogger().info("Loading internals..."); + if (getInternals() == null) { + getLogger().severe("Nicko could not find a valid implementation for this server version. Is your server supported?"); + dataStore.getStorage().setError(true); + getServer().getPluginManager().disablePlugin(this); + } + + + localeFileManager = new LocaleFileManager(); + if (configuration.isCustomLocale()) { + if (localeFileManager.dumpFromLocale(Locale.ENGLISH)) { + getLogger().info("Successfully loaded custom language file."); + } else { + getLogger().severe("Failed to load custom language file!"); + } + } + + final PluginCommand command = getCommand("nicko"); + if (command != null) { + command.setExecutor(new NickoCommand()); + } + + Structure.addGlobalIngredient('#', new SimpleItem(new ItemBuilder(Material.BLACK_STAINED_GLASS_PANE).setDisplayName(" "))); + Structure.addGlobalIngredient('%', new SimpleItem(new ItemBuilder(Material.ORANGE_STAINED_GLASS_PANE).setDisplayName(" "))); + Structure.addGlobalIngredient('U', new OptionUnavailable()); + Structure.addGlobalIngredient('E', new ExitGUI()); + + new PlaceHolderHook(this).hook(); + + getServer().getPluginManager().registerEvents(new PlayerJoinListener(), this); + getServer().getPluginManager().registerEvents(new PlayerQuitListener(), this); + + getLogger().info("Nicko (Bukkit) has been enabled."); + } + } + + @Override + public void onDisable() { + if (!getDataStore().getStorage().isError()) { + getLogger().info("Closing persistence..."); + nameStore.clearStoredNames(); + Bukkit.getOnlinePlayers().forEach(player -> dataStore.saveData(player)); + if (!dataStore.getStorage().getProvider().close()) { + getLogger().severe("Failed to close persistence!"); + } + } + + getLogger().info("Nicko (Bukkit) has been disabled."); + } + + public static NickoBukkit getInstance() { + return plugin; + } + + public Configuration getNickoConfig() { + try { + if (configuration == null) { return configuration = configurationManager.load(); } + return configuration; + } catch (IOException e) { + getLogger().severe("Failed to load the configuration file!"); + getLogger().severe("It may be have been generated with an older version of Nicko."); + getLogger().severe("Delete the configuration and restart the server please :)"); + getLogger().severe("(" + e.getMessage() + ")"); + return null; + } + } + + public PlayerDataStore getDataStore() { + return dataStore; + } + + public PlayerNameStore getNameStore() { + return nameStore; + } + + public MojangAPI getMojangAPI() { + return mojangAPI; + } + + public LocaleFileManager getLocaleFileManager() { + return localeFileManager; + } + + public Internals getInternals() { + return InternalsProvider.getInternals(); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/anvil/AnvilManager.java b/core/src/main/java/net/artelnatif/nicko/anvil/AnvilManager.java new file mode 100644 index 0000000..b82d18d --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/anvil/AnvilManager.java @@ -0,0 +1,106 @@ +package net.artelnatif.nicko.anvil; + +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.appearance.AppearanceManager; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.i18n.I18N; +import net.artelnatif.nicko.i18n.I18NDict; +import net.artelnatif.nicko.mojang.MojangUtils; +import net.wesjd.anvilgui.AnvilGUI; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.Collections; +import java.util.List; + +public class AnvilManager { + private final Player player; + private final AppearanceManager appearanceManager; + + public AnvilManager(Player player) { + this.player = player; + this.appearanceManager = AppearanceManager.get(player); + } + + public void openNameThenSkinAnvil() { + getNameThenSkinAnvil().open(player); + } + + public void openSkinAnvil() { + getSkinAnvil().open(player); + } + + public void openNameAnvil() { + getNameAnvil().open(player); + } + + public AnvilGUI.Builder getNameThenSkinAnvil() { + return new AnvilGUI.Builder() + .plugin(NickoBukkit.getInstance()) + .itemLeft(getLeftItem(false)) + .interactableSlots(AnvilGUI.Slot.OUTPUT) + .onComplete((completion) -> { + if (MojangUtils.isUsernameInvalid(completion.getText())) { + return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!")); + } else { + appearanceManager.setName(completion.getText()); + openSkinAnvil(); + return Collections.singletonList(AnvilGUI.ResponseAction.close()); + } + }) + .text("New name..."); + } + + public AnvilGUI.Builder getNameAnvil() { + return new AnvilGUI.Builder() + .plugin(NickoBukkit.getInstance()) + .itemLeft(getLeftItem(false)) + .interactableSlots(AnvilGUI.Slot.OUTPUT) + .onComplete((completion) -> { + if (MojangUtils.isUsernameInvalid(completion.getText())) { + return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!")); + } else { + appearanceManager.setName(completion.getText()); + final ActionResult actionResult = appearanceManager.updatePlayer(false); + return sendResultAndClose(actionResult); + } + }) + .text("New name..."); + } + + private AnvilGUI.Builder getSkinAnvil() { + return new AnvilGUI.Builder() + .plugin(NickoBukkit.getInstance()) + .itemLeft(getLeftItem(true)) + .interactableSlots(AnvilGUI.Slot.OUTPUT) + .onComplete((completion) -> { + if (MojangUtils.isUsernameInvalid(completion.getText())) { + return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!")); + } else { + appearanceManager.setSkin(completion.getText()); + final ActionResult actionResult = appearanceManager.updatePlayer(true); + return sendResultAndClose(actionResult); + } + }) + .text("New skin..."); + } + + private List sendResultAndClose(ActionResult actionResult) { + if (!actionResult.isError()) { + player.sendMessage(I18N.translate(player, I18NDict.Event.Disguise.SUCCESS)); + } else { + player.sendMessage(I18N.translate(player, I18NDict.Event.Disguise.FAIL, I18N.translateWithoutPrefix(player, actionResult.getErrorMessage()))); + } + return Collections.singletonList(AnvilGUI.ResponseAction.close()); + } + + private ItemStack getLeftItem(boolean skin) { + final ItemStack item = new ItemStack(Material.PAPER); + final ItemMeta meta = item.getItemMeta(); + meta.setDisplayName("§0New " + (skin ? "skin" : "name") + "..."); + item.setItemMeta(meta); + return item; + } +} \ No newline at end of file diff --git a/core/src/main/java/net/artelnatif/nicko/appearance/AppearanceManager.java b/core/src/main/java/net/artelnatif/nicko/appearance/AppearanceManager.java new file mode 100644 index 0000000..3120f39 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/appearance/AppearanceManager.java @@ -0,0 +1,89 @@ +package net.artelnatif.nicko.appearance; + +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.storage.PlayerDataStore; +import net.artelnatif.nicko.storage.name.PlayerNameStore; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.UUID; + +public class AppearanceManager { + private final NickoProfile profile; + private final Player player; + private final NickoBukkit instance = NickoBukkit.getInstance(); + private final PlayerDataStore dataStore = instance.getDataStore(); + private final PlayerNameStore nameStore = instance.getNameStore(); + + private AppearanceManager(UUID uuid) { + this.player = Bukkit.getPlayer(uuid); + this.profile = dataStore.getData(uuid).orElse(NickoProfile.EMPTY_PROFILE.clone()); + } + + private AppearanceManager(String name) { + this.player = null; + this.profile = dataStore.getOfflineData(name).orElse(NickoProfile.EMPTY_PROFILE.clone()); + } + + public static AppearanceManager get(Player player) { + return new AppearanceManager(player.getUniqueId()); + } + + public static AppearanceManager get(String name) { + return new AppearanceManager(name); + } + + public boolean hasData() { + return !profile.isEmpty(); + } + + public void setSkin(String skin) { + profile.setSkin(skin); + } + + public String getSkin() { + return profile.getSkin(); + } + + public boolean needsASkinChange() { + return profile.getSkin() != null && !profile.getSkin().equals(player.getName()); + } + + public void setName(String name) { + profile.setName(name); + } + + public String getName() { + return profile.getName(); + } + + public NickoProfile getProfile() { + return profile; + } + + public void setNameAndSkin(String name, String skin) { + this.profile.setName(name); + this.profile.setSkin(skin); + updatePlayer(true); + } + + public ActionResult reset() { + final String defaultName = nameStore.getStoredName(player); + this.profile.setName(defaultName); + this.profile.setSkin(defaultName); + final ActionResult actionResult = resetPlayer(); + this.profile.setSkin(null); + this.profile.setName(null); + return actionResult; + } + + public ActionResult resetPlayer() { + return NickoBukkit.getInstance().getInternals().updateProfile(player, profile, true, true); + } + + public ActionResult updatePlayer(boolean skinChange) { + return NickoBukkit.getInstance().getInternals().updateProfile(player, profile, skinChange, false); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/command/NickoCommand.java b/core/src/main/java/net/artelnatif/nicko/command/NickoCommand.java new file mode 100644 index 0000000..e10d6c6 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/command/NickoCommand.java @@ -0,0 +1,48 @@ +package net.artelnatif.nicko.command; + +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.command.sub.NickoCheckSubCmd; +import net.artelnatif.nicko.command.sub.NickoDebugSubCmd; +import net.artelnatif.nicko.gui.MainGUI; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class NickoCommand implements CommandExecutor { + private String helpMessage = "§cNicko §8§o[{version}] §f- §2Help:\n" + + "§6/nicko §f- §7Open the GUI.\n" + + "§6/nicko help §f- §7Print this help message.\n"; + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (sender instanceof Player) { + Player player = (Player) sender; + if (args.length >= 1) { + switch (args[0]) { + case "debug": + new NickoDebugSubCmd().execute(player, args); + break; + case "check": + new NickoCheckSubCmd().execute(player, args); + break; + default: + sendHelpMessage(sender); + break; + } + return false; + } + + new MainGUI(player).open(); + return false; + } + + sender.sendMessage("This plugin can only be used in-game. Sorry!"); + return false; + } + + public void sendHelpMessage(CommandSender sender) { + helpMessage = helpMessage.replace("{version}", NickoBukkit.getInstance().getDescription().getVersion()); + sender.sendMessage(helpMessage); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/command/sub/NickoCheckSubCmd.java b/core/src/main/java/net/artelnatif/nicko/command/sub/NickoCheckSubCmd.java new file mode 100644 index 0000000..848a87b --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/command/sub/NickoCheckSubCmd.java @@ -0,0 +1,42 @@ +package net.artelnatif.nicko.command.sub; + +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.appearance.AppearanceManager; +import net.artelnatif.nicko.i18n.I18N; +import net.artelnatif.nicko.i18n.I18NDict; +import net.artelnatif.nicko.mojang.MojangUtils; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.StringJoiner; + +public class NickoCheckSubCmd { + public void execute(Player player, String[] args) { + final String targetName = args[1]; + final Player target = Bukkit.getPlayerExact(targetName); + + AppearanceManager appearanceManager; + if (MojangUtils.isUsernameInvalid(targetName)) { + player.sendMessage(I18N.translate(player, I18NDict.Error.INVALID_USERNAME)); + return; + } + + if (target == null) { + appearanceManager = AppearanceManager.get(targetName); + } else { + appearanceManager = AppearanceManager.get(target); + } + + final StringJoiner builder = new StringJoiner("\n"); + builder.add("§c" + NickoBukkit.getInstance().getNickoConfig().getPrefix() + "§6Check for: §f§o" + targetName); + if (!appearanceManager.hasData()) { + builder.add("§cThis player has not data."); + } else { + builder.add("§7- §fNicked: §a✔"); + builder.add("§7- §fName: §6" + appearanceManager.getName()); + builder.add("§7- §fSkin: §6" + appearanceManager.getSkin()); + } + + player.sendMessage(builder.toString()); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/command/sub/NickoDebugSubCmd.java b/core/src/main/java/net/artelnatif/nicko/command/sub/NickoDebugSubCmd.java new file mode 100644 index 0000000..b691de9 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/command/sub/NickoDebugSubCmd.java @@ -0,0 +1,48 @@ +package net.artelnatif.nicko.command.sub; + +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.appearance.AppearanceManager; +import net.artelnatif.nicko.mojang.MojangUtils; +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class NickoDebugSubCmd { + public void execute(CommandSender sender, String[] args) { + final String prefix = NickoBukkit.getInstance().getNickoConfig().getPrefix(); + + Player target; + String name, skin; + if (args.length == 3) { + target = (Player) sender; + name = args[1]; + skin = args[2]; + } else { + if (args.length < 3) { + sender.sendMessage(prefix + "§cMissing argument."); + return; + } + + final String playerName = args[1]; + name = args[2]; + skin = args[3]; + + target = Bukkit.getPlayer(playerName); + if (target == null) { + sender.sendMessage(prefix + "§cSpecified player is offline."); + return; + } + } + + final AppearanceManager appearanceManager = AppearanceManager.get(target.getPlayer()); + + if (MojangUtils.isUsernameInvalid(name) || MojangUtils.isUsernameInvalid(skin)) { + sender.sendMessage(prefix + "§cSpecified username is invalid."); + } + + appearanceManager.setNameAndSkin(name, skin); + target.sendMessage(prefix + "§aWhoosh!"); + target.playSound(target.getLocation(), Sound.ENTITY_ITEM_FRAME_PLACE, 1, 1); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/config/Configuration.java b/core/src/main/java/net/artelnatif/nicko/config/Configuration.java new file mode 100644 index 0000000..3dbd3ec --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/config/Configuration.java @@ -0,0 +1,51 @@ +package net.artelnatif.nicko.config; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class Configuration { + @JsonProperty("sql") + private final DataSourceConfiguration sqlConfiguration; + @JsonProperty("redis") + private final DataSourceConfiguration redisConfiguration; + private final String prefix; + private final Boolean local; + private final Boolean customLocale; + + public Configuration(DataSourceConfiguration sqlConfiguration, DataSourceConfiguration redisConfiguration, String prefix, Boolean local, Boolean customLocale) { + this.sqlConfiguration = sqlConfiguration; + this.redisConfiguration = redisConfiguration; + this.prefix = prefix; + this.local = local; + this.customLocale = customLocale; + } + + public Configuration() { + this( + new DataSourceConfiguration("", 3306, "", ""), + new DataSourceConfiguration("", 6379, "", ""), + "", + false, + false + ); + } + + public DataSourceConfiguration getSqlConfiguration() { + return sqlConfiguration; + } + + public DataSourceConfiguration getRedisConfiguration() { + return redisConfiguration; + } + + public String getPrefix() { + return prefix; + } + + public Boolean isLocal() { + return local; + } + + public Boolean isCustomLocale() { + return customLocale; + } +} diff --git a/src/main/java/xyz/ineanto/nicko/config/ConfigurationManager.java b/core/src/main/java/net/artelnatif/nicko/config/ConfigurationManager.java similarity index 72% rename from src/main/java/xyz/ineanto/nicko/config/ConfigurationManager.java rename to core/src/main/java/net/artelnatif/nicko/config/ConfigurationManager.java index de83afb..37c680b 100644 --- a/src/main/java/xyz/ineanto/nicko/config/ConfigurationManager.java +++ b/core/src/main/java/net/artelnatif/nicko/config/ConfigurationManager.java @@ -1,4 +1,4 @@ -package xyz.ineanto.nicko.config; +package net.artelnatif.nicko.config; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; @@ -7,23 +7,16 @@ 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 { @@ -38,6 +31,7 @@ public class ConfigurationManager { try { final InputStream input = getClass().getResourceAsStream("/config.yml"); if (input != null) { + logger.info("Saved default configuration as config.yml"); Files.createDirectories(file.getParentFile().toPath()); Files.createFile(file.toPath()); Files.copy(input, file.toPath(), StandardCopyOption.REPLACE_EXISTING); @@ -53,12 +47,4 @@ public class ConfigurationManager { return mapper.readValue(reader, Configuration.class); } } - - public File getFile() { - return file; - } - - public File getBackupFile() { - return backupFile; - } } diff --git a/core/src/main/java/net/artelnatif/nicko/config/DataSourceConfiguration.java b/core/src/main/java/net/artelnatif/nicko/config/DataSourceConfiguration.java new file mode 100644 index 0000000..d180824 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/config/DataSourceConfiguration.java @@ -0,0 +1,36 @@ +package net.artelnatif.nicko.config; + +public class DataSourceConfiguration { + public static final DataSourceConfiguration SQL_EMPTY = new DataSourceConfiguration("127.0.0.1", 3306, "root", ""); + public static final DataSourceConfiguration REDIS_EMPTY = new DataSourceConfiguration("127.0.0.1", 6379, "", ""); + + private final String address; + private final Integer port; + private final String username; + private final String password; + + public DataSourceConfiguration(String address, Integer port, String username, String password) { + this.address = address; + this.port = port; + this.username = username; + this.password = password; + } + + public DataSourceConfiguration() { this("", 0, "", ""); } + + public String getAddress() { + return address; + } + + public Integer getPort() { + return port; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/disguise/ActionResult.java b/core/src/main/java/net/artelnatif/nicko/disguise/ActionResult.java new file mode 100644 index 0000000..83158b0 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/disguise/ActionResult.java @@ -0,0 +1,36 @@ +package net.artelnatif.nicko.disguise; + +import net.artelnatif.nicko.i18n.I18NDict; + +public class ActionResult { + private final I18NDict errorMessage; + private boolean error = false; + private R result; + + public ActionResult() { + this.errorMessage = null; + } + + public ActionResult(I18NDict errorMessage) { + this.errorMessage = errorMessage; + this.error = true; + this.result = null; + } + + public ActionResult(R result) { + this.errorMessage = null; + this.result = result; + } + + public R getResult() { + return result; + } + + public boolean isError() { + return error; + } + + public I18NDict getErrorMessage() { + return errorMessage; + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/disguise/NickoProfile.java b/core/src/main/java/net/artelnatif/nicko/disguise/NickoProfile.java new file mode 100644 index 0000000..926ad28 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/disguise/NickoProfile.java @@ -0,0 +1,72 @@ +package net.artelnatif.nicko.disguise; + +import net.artelnatif.nicko.i18n.Locale; + +public class NickoProfile implements Cloneable { + public static final NickoProfile EMPTY_PROFILE = new NickoProfile(null, null, Locale.ENGLISH, true); + + private String name; + private String skin; + private Locale locale; + private boolean bungeecordTransfer; + + public NickoProfile(String name, String skin, Locale locale, boolean bungeecordTransfer) { + this.name = name; + this.skin = skin; + this.locale = locale; + this.bungeecordTransfer = bungeecordTransfer; + } + + public boolean isEmpty() { + return name == null && skin == null; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSkin() { + return skin; + } + + public void setSkin(String skin) { + this.skin = skin; + } + + public Locale getLocale() { return locale; } + + public void setLocale(Locale locale) { this.locale = locale; } + + public boolean isBungeecordTransfer() { + return bungeecordTransfer; + } + + public void setBungeecordTransfer(boolean bungeecordTransfer) { + this.bungeecordTransfer = bungeecordTransfer; + } + + @Override + public String toString() { + return "NickoProfile{" + + "name='" + name + '\'' + + ", skin='" + skin + '\'' + + ", locale=" + locale + + ", bungeecordTransfer=" + bungeecordTransfer + + '}'; + } + + @Override + public NickoProfile clone() { + Object o; + try { + o = super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + return (NickoProfile) o; + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/event/PlayerJoinListener.java b/core/src/main/java/net/artelnatif/nicko/event/PlayerJoinListener.java new file mode 100644 index 0000000..2afa2c3 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/event/PlayerJoinListener.java @@ -0,0 +1,42 @@ +package net.artelnatif.nicko.event; + +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.appearance.AppearanceManager; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.i18n.I18N; +import net.artelnatif.nicko.i18n.I18NDict; +import net.artelnatif.nicko.storage.PlayerDataStore; +import net.artelnatif.nicko.storage.name.PlayerNameStore; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +public class PlayerJoinListener implements Listener { + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + final Player player = event.getPlayer(); + final NickoBukkit instance = NickoBukkit.getInstance(); + + final PlayerDataStore dataStore = instance.getDataStore(); + final PlayerNameStore nameStore = instance.getNameStore(); + nameStore.storeName(player); + + // TODO: 2/20/23 BungeeCord transfer + + dataStore.performProfileUpdate(player.getUniqueId(), NickoProfile.EMPTY_PROFILE); + Bukkit.getScheduler().runTaskLater(instance, () -> { + final AppearanceManager appearanceManager = AppearanceManager.get(player); + if (appearanceManager.hasData()) { + final ActionResult actionResult = appearanceManager.updatePlayer(appearanceManager.needsASkinChange()); + if (!actionResult.isError()) { + player.sendMessage(I18N.translate(player, I18NDict.Event.PreviousSkin.SUCCESS)); + } else { + player.sendMessage(I18N.translate(player, I18NDict.Event.PreviousSkin.FAIL, I18N.translate(player, actionResult.getErrorMessage()))); + } + } + }, 20L); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/event/PlayerQuitListener.java b/core/src/main/java/net/artelnatif/nicko/event/PlayerQuitListener.java new file mode 100644 index 0000000..6d83949 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/event/PlayerQuitListener.java @@ -0,0 +1,19 @@ +package net.artelnatif.nicko.event; + +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.ActionResult; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; + +public class PlayerQuitListener implements Listener { + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + final Player player = event.getPlayer(); + final ActionResult result = NickoBukkit.getInstance().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/gui/AdminGUI.java b/core/src/main/java/net/artelnatif/nicko/gui/AdminGUI.java new file mode 100644 index 0000000..6baa5a7 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/AdminGUI.java @@ -0,0 +1,35 @@ +package net.artelnatif.nicko.gui; + +import net.artelnatif.nicko.gui.items.admin.ManageCache; +import net.artelnatif.nicko.gui.items.common.GoBack; +import org.bukkit.entity.Player; +import xyz.xenondevs.invui.gui.Gui; +import xyz.xenondevs.invui.window.Window; + +public class AdminGUI { + public static final String TITLE = "Nicko > Administration"; + + private final Player player; + private final Gui gui; + + public AdminGUI(Player player) { + this.gui = Gui.normal() + .setStructure( + "# # # # # # # # #", + "# # # S U U # # #", + "B # # # # # # # #" + ) + .addIngredient('S', new ManageCache()) + .addIngredient('B', new GoBack(new MainGUI(player).getGUI())) + .build(); + this.player = player; + } + + public Gui getGUI() { + return gui; + } + + public void open() { + Window.single().setGui(gui).setTitle(TITLE).open(player); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/MainGUI.java b/core/src/main/java/net/artelnatif/nicko/gui/MainGUI.java new file mode 100644 index 0000000..e8afe54 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/MainGUI.java @@ -0,0 +1,46 @@ +package net.artelnatif.nicko.gui; + +import net.artelnatif.nicko.gui.items.main.AdminSubGUI; +import net.artelnatif.nicko.gui.items.main.ResetAppearance; +import net.artelnatif.nicko.gui.items.main.SettingsSubGUI; +import net.artelnatif.nicko.gui.items.skin.ChangeName; +import net.artelnatif.nicko.gui.items.skin.ChangeNameAndSkin; +import net.artelnatif.nicko.gui.items.skin.ChangeSkin; +import org.bukkit.entity.Player; +import xyz.xenondevs.invui.gui.Gui; +import xyz.xenondevs.invui.window.Window; + +public class MainGUI { + private final Player player; + private final Gui gui; + + public MainGUI(Player player) { + final String[] dynamicStructure = new String[]{ + "# # # # # # # # #", + "# # # N B S # # #", + "E P A # # # # # R"}; + + if (!player.hasPermission("nicko.admin") || !player.isOp()) { + dynamicStructure[2] = dynamicStructure[2].replace("A", "#"); + } + + this.gui = Gui.normal() + .setStructure(dynamicStructure) + .addIngredient('R', new ResetAppearance()) + .addIngredient('N', new ChangeName()) + .addIngredient('B', new ChangeNameAndSkin()) + .addIngredient('S', new ChangeSkin(player)) + .addIngredient('P', new SettingsSubGUI()) + .addIngredient('A', new AdminSubGUI()) + .build(); + this.player = player; + } + + public Gui getGUI() { + return gui; + } + + public void open() { + Window.single().setGui(gui).setTitle("Nicko - Home").open(player); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/SettingsGUI.java b/core/src/main/java/net/artelnatif/nicko/gui/SettingsGUI.java new file mode 100644 index 0000000..eef7069 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/SettingsGUI.java @@ -0,0 +1,38 @@ +package net.artelnatif.nicko.gui; + +import net.artelnatif.nicko.gui.items.common.GoBack; +import net.artelnatif.nicko.gui.items.settings.BungeeCordCycling; +import net.artelnatif.nicko.gui.items.settings.LanguageCycling; +import org.bukkit.entity.Player; +import xyz.xenondevs.invui.gui.Gui; +import xyz.xenondevs.invui.window.Window; + +public class SettingsGUI { + public static final String TITLE = "Nicko > Settings"; + + private final Player player; + private final Gui gui; + + public SettingsGUI(Player player) { + final String[] dynamicStructure = new String[]{ + "# # # # # # # # #", + "# # # L T U # # #", + "B # # # # # # # #" + }; + + // TODO: 3/6/23 Replace when Redis is not enabled + dynamicStructure[1] = dynamicStructure[1].replace("T", "U"); + + this.gui = Gui.normal() + .setStructure(dynamicStructure) + .addIngredient('B', new GoBack(new MainGUI(player).getGUI())) + .addIngredient('L', new LanguageCycling().get(player)) + .addIngredient('T', new BungeeCordCycling().get(player)) + .build(); + this.player = player; + } + + public void open() { + Window.single().setGui(gui).setTitle(TITLE).open(player); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/admin/CacheManagementGUI.java b/core/src/main/java/net/artelnatif/nicko/gui/admin/CacheManagementGUI.java new file mode 100644 index 0000000..196ad40 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/admin/CacheManagementGUI.java @@ -0,0 +1,36 @@ +package net.artelnatif.nicko.gui.admin; + +import net.artelnatif.nicko.gui.AdminGUI; +import net.artelnatif.nicko.gui.items.admin.cache.CacheDetailed; +import net.artelnatif.nicko.gui.items.admin.cache.CacheInvalidate; +import net.artelnatif.nicko.gui.items.admin.cache.CacheOverview; +import net.artelnatif.nicko.gui.items.common.GoBack; +import org.bukkit.entity.Player; +import xyz.xenondevs.invui.gui.Gui; +import xyz.xenondevs.invui.window.Window; + +public class CacheManagementGUI { + public static final String TITLE = "Nicko > Admin... > Cache"; + + private final Player player; + private final Gui gui; + + public CacheManagementGUI(Player player) { + this.gui = Gui.normal() + .setStructure("B # S A D") + .addIngredient('B', new GoBack(new AdminGUI(player).getGUI())) + .addIngredient('S', new CacheOverview()) + .addIngredient('A', new CacheInvalidate()) + .addIngredient('D', new CacheDetailed()) + .build(); + this.player = player; + } + + public Gui getGUI() { + return gui; + } + + public void open() { + Window.single().setGui(gui).setTitle(TITLE).open(player); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/admin/cache/CacheDetailedGUI.java b/core/src/main/java/net/artelnatif/nicko/gui/admin/cache/CacheDetailedGUI.java new file mode 100644 index 0000000..c02134c --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/admin/cache/CacheDetailedGUI.java @@ -0,0 +1,61 @@ +package net.artelnatif.nicko.gui.admin.cache; + +import xyz.xenondevs.invui.gui.Gui; +import xyz.xenondevs.invui.gui.ScrollGui; +import xyz.xenondevs.invui.gui.structure.Markers; +import xyz.xenondevs.invui.item.Item; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.gui.items.admin.cache.SkinPlaceholder; +import net.artelnatif.nicko.gui.admin.CacheManagementGUI; +import net.artelnatif.nicko.gui.items.common.GoBack; +import net.artelnatif.nicko.gui.items.common.ScrollDown; +import net.artelnatif.nicko.gui.items.common.ScrollUp; +import net.artelnatif.nicko.mojang.MojangSkin; +import org.bukkit.entity.Player; +import xyz.xenondevs.invui.window.Window; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentMap; +import java.util.stream.Collectors; + +public class CacheDetailedGUI { + public static final String TITLE = "... > Cache > Invalidate"; + + private final Player player; + private final Gui gui; + + public CacheDetailedGUI(Player player) { + final ConcurrentMap> skins = NickoBukkit.getInstance().getMojangAPI().getCache().asMap(); + final List loadedSkins = skins.entrySet().stream() + .filter(entry -> entry.getValue().isPresent()) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + + final List items = loadedSkins.stream() + .map(SkinPlaceholder::new) + .collect(Collectors.toList()); + + gui = ScrollGui.items(guiItemBuilder -> { + guiItemBuilder.setStructure( + "# # # # # # # # #", + "# x x x x x x U #", + "# x x x x x x # #", + "# x x x x x x # #", + "# x x x x x x D #", + "B # # # # # # # #"); + guiItemBuilder.addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL); + guiItemBuilder.addIngredient('U', new ScrollUp()); + guiItemBuilder.addIngredient('D', new ScrollDown()); + guiItemBuilder.addIngredient('B', new GoBack(new CacheManagementGUI(player).getGUI())); + guiItemBuilder.setContent(items); + }); + + this.player = player; + } + + public void open() { + Window.single().setGui(gui).setTitle(TITLE).open(player); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/admin/ManageCache.java b/core/src/main/java/net/artelnatif/nicko/gui/items/admin/ManageCache.java new file mode 100644 index 0000000..ed814ec --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/admin/ManageCache.java @@ -0,0 +1,33 @@ +package net.artelnatif.nicko.gui.items.admin; + +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.builder.SkullBuilder; +import xyz.xenondevs.invui.item.impl.AsyncItem; +import net.artelnatif.nicko.gui.admin.CacheManagementGUI; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.jetbrains.annotations.NotNull; + +public class ManageCache extends AsyncItem { + public ManageCache() { + super(new ItemBuilder(Material.PAINTING) + .setDisplayName("§fManage §6skin §fcache...") + .addLoreLines("§7Access the skin cache management panel."), + () -> { + final SkullBuilder builder = new SkullBuilder("Notch"); + builder.setDisplayName("§fManage §6skin §fcache..."); + builder.addLoreLines("§7Access the skin cache management panel."); + return builder; + }); + } + + @Override + public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + if (clickType.isLeftClick() || clickType.isRightClick()) { + event.getView().close(); + new CacheManagementGUI(player).open(); + } + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/admin/cache/CacheDetailed.java b/core/src/main/java/net/artelnatif/nicko/gui/items/admin/cache/CacheDetailed.java new file mode 100644 index 0000000..830a859 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/admin/cache/CacheDetailed.java @@ -0,0 +1,26 @@ +package net.artelnatif.nicko.gui.items.admin.cache; + +import net.artelnatif.nicko.gui.admin.cache.CacheDetailedGUI; +import org.bukkit.Material; +import org.bukkit.event.inventory.ClickType; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.SuppliedItem; + +public class CacheDetailed extends SuppliedItem { + public CacheDetailed() { + super(() -> { + final ItemBuilder builder = new ItemBuilder(Material.PAPER); + builder.setDisplayName("§6Invalidate specific skin..."); + builder.addLoreLines("§7Select a specific skin to invalidate."); + return builder; + }, (click) -> { + final ClickType clickType = click.getClickType(); + if (clickType.isLeftClick() || clickType.isRightClick()) { + click.getEvent().getView().close(); + new CacheDetailedGUI(click.getPlayer()).open(); + return true; + } + return false; + }); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/admin/cache/CacheInvalidate.java b/core/src/main/java/net/artelnatif/nicko/gui/items/admin/cache/CacheInvalidate.java new file mode 100644 index 0000000..f454e92 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/admin/cache/CacheInvalidate.java @@ -0,0 +1,36 @@ +package net.artelnatif.nicko.gui.items.admin.cache; + +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.i18n.I18N; +import net.artelnatif.nicko.i18n.I18NDict; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.SuppliedItem; + +public class CacheInvalidate extends SuppliedItem { + public CacheInvalidate() { + super(() -> { + final ItemBuilder builder = new ItemBuilder(Material.TNT); + builder.setDisplayName("§fInvalidate §6skin cache"); + builder.addLoreLines( + "§c§oNOT RECOMMENDED", + "§7Invalidates every skin entry present in the cache.", + "§7Does not reset player disguises.", + "§7Could be useful if a skin has been updated", + "§7recently and the cache is now outdated."); + return builder; + }, (click) -> { + final ClickType clickType = click.getClickType(); + if (clickType.isLeftClick() || clickType.isRightClick()) { + final Player player = click.getPlayer(); + click.getEvent().getView().close(); + player.sendMessage(I18N.translate(player, I18NDict.Event.Admin.CACHE_CLEAN)); + NickoBukkit.getInstance().getMojangAPI().getCache().invalidateAll(); + return true; + } + return false; + }); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/admin/cache/CacheOverview.java b/core/src/main/java/net/artelnatif/nicko/gui/items/admin/cache/CacheOverview.java new file mode 100644 index 0000000..b916b95 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/admin/cache/CacheOverview.java @@ -0,0 +1,28 @@ +package net.artelnatif.nicko.gui.items.admin.cache; + +import com.google.common.cache.CacheStats; +import com.google.common.cache.LoadingCache; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.mojang.MojangSkin; +import org.bukkit.Material; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.SuppliedItem; + +import java.util.Optional; + +public class CacheOverview extends SuppliedItem { + public CacheOverview() { + super(() -> { + final ItemBuilder builder = new ItemBuilder(Material.OAK_SIGN); + final LoadingCache> cache = NickoBukkit.getInstance().getMojangAPI().getCache(); + final CacheStats stats = cache.stats(); + builder.setDisplayName("§6Skin cache §foverview:"); + builder.addLoreLines( + "Request Count: §2" + stats.requestCount(), + "Skin Cached: §2" + Math.round(cache.size()), + "§7§oCache is cleared every 24 hours.", + "§7§o(Click to refresh)"); + return builder; + }, (event) -> true); + } +} \ No newline at end of file diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/admin/cache/SkinPlaceholder.java b/core/src/main/java/net/artelnatif/nicko/gui/items/admin/cache/SkinPlaceholder.java new file mode 100644 index 0000000..2984c53 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/admin/cache/SkinPlaceholder.java @@ -0,0 +1,21 @@ +package net.artelnatif.nicko.gui.items.admin.cache; + +import org.bukkit.Material; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.builder.SkullBuilder; +import xyz.xenondevs.invui.item.impl.AsyncItem; + +import java.util.UUID; + +public class SkinPlaceholder extends AsyncItem { + public SkinPlaceholder(String name) { + super(new ItemBuilder(Material.PAINTING).setDisplayName("§7§oLoading..."), () -> { + final String stringUUID = name.replaceAll("(.{8})(.{4})(.{4})(.{4})(.+)", "$1-$2-$3-$4-$5"); + final UUID uuid = UUID.fromString(stringUUID); + final SkullBuilder skull = new SkullBuilder(uuid); + skull.setDisplayName("§6Skin Entry"); + skull.addLoreLines("§7Click to invalidate skin"); + return skull; + }); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/common/GoBack.java b/core/src/main/java/net/artelnatif/nicko/gui/items/common/GoBack.java new file mode 100644 index 0000000..5d44816 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/common/GoBack.java @@ -0,0 +1,23 @@ +package net.artelnatif.nicko.gui.items.common; + +import org.bukkit.Material; +import xyz.xenondevs.invui.gui.Gui; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.SuppliedItem; +import xyz.xenondevs.invui.window.Window; + +public class GoBack extends SuppliedItem { + public GoBack(Gui gui) { + super(() -> { + final ItemBuilder builder = new ItemBuilder(Material.ARROW); + builder.setDisplayName("Go back"); + builder.addLoreLines("§7Return to the previous window."); + return builder; + }, click -> { + click.getEvent().getView().close(); + // TODO: 4/1/23 Get title of parent GUI + Window.single().setGui(gui).setTitle("Nicko").open(click.getPlayer()); + return true; + }); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/common/OptionUnavailable.java b/core/src/main/java/net/artelnatif/nicko/gui/items/common/OptionUnavailable.java new file mode 100644 index 0000000..1f8e38a --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/common/OptionUnavailable.java @@ -0,0 +1,16 @@ +package net.artelnatif.nicko.gui.items.common; + +import org.bukkit.Material; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.SuppliedItem; + +public class OptionUnavailable extends SuppliedItem { + public OptionUnavailable() { + super(() -> { + final ItemBuilder builder = new ItemBuilder(Material.RED_TERRACOTTA); + builder.setDisplayName("§cFeature unavailable :("); + builder.addLoreLines("§7This button is disabled."); + return builder; + }, click -> true); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/common/ScrollDown.java b/core/src/main/java/net/artelnatif/nicko/gui/items/common/ScrollDown.java new file mode 100644 index 0000000..8cc8536 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/common/ScrollDown.java @@ -0,0 +1,25 @@ +package net.artelnatif.nicko.gui.items.common; + +import org.bukkit.Material; +import xyz.xenondevs.invui.gui.ScrollGui; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.controlitem.ScrollItem; + +public class ScrollDown extends ScrollItem { + + public ScrollDown() { + super(1); + } + + @Override + public ItemProvider getItemProvider(ScrollGui gui) { + ItemBuilder builder = new ItemBuilder(Material.GREEN_STAINED_GLASS_PANE); + builder.setDisplayName("§7Scroll down"); + if (!gui.canScroll(1)) + builder.addLoreLines("§cYou can't scroll further down"); + + return builder; + } +} + diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/common/ScrollUp.java b/core/src/main/java/net/artelnatif/nicko/gui/items/common/ScrollUp.java new file mode 100644 index 0000000..cd30663 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/common/ScrollUp.java @@ -0,0 +1,26 @@ +package net.artelnatif.nicko.gui.items.common; + +import org.bukkit.Material; +import xyz.xenondevs.invui.gui.ScrollGui; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.controlitem.ScrollItem; + +public class ScrollUp extends ScrollItem { + + public ScrollUp() { + super(-1); + } + + @Override + public ItemProvider getItemProvider(ScrollGui gui) { + ItemBuilder builder = new ItemBuilder(Material.RED_STAINED_GLASS_PANE); + builder.setDisplayName("§7Scroll up"); + if (!gui.canScroll(-1)) + builder.addLoreLines("§cYou've reached the top"); + + return builder; + } + +} + diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/home/AdminAccessItem.java b/core/src/main/java/net/artelnatif/nicko/gui/items/main/AdminSubGUI.java similarity index 52% rename from src/main/java/xyz/ineanto/nicko/gui/items/home/AdminAccessItem.java rename to core/src/main/java/net/artelnatif/nicko/gui/items/main/AdminSubGUI.java index 3510058..0ad8982 100644 --- a/src/main/java/xyz/ineanto/nicko/gui/items/home/AdminAccessItem.java +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/main/AdminSubGUI.java @@ -1,25 +1,22 @@ -package xyz.ineanto.nicko.gui.items.home; +package net.artelnatif.nicko.gui.items.main; +import net.artelnatif.nicko.gui.AdminGUI; import org.bukkit.Material; -import org.bukkit.entity.Player; +import org.bukkit.enchantments.Enchantment; import org.bukkit.event.inventory.ClickType; -import xyz.ineanto.nicko.gui.AdminGUI; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; +import org.bukkit.inventory.ItemFlag; import xyz.xenondevs.invui.item.builder.ItemBuilder; import xyz.xenondevs.invui.item.impl.SuppliedItem; -public class AdminAccessItem { - private final PlayerLanguage playerLanguage; - - public AdminAccessItem(Player player) { - this.playerLanguage = new PlayerLanguage(player); - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { +public class AdminSubGUI extends SuppliedItem { + public AdminSubGUI() { + super(() -> { final ItemBuilder builder = new ItemBuilder(Material.COMMAND_BLOCK); - return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.ADMIN); + builder.addEnchantment(Enchantment.DAMAGE_ALL, 1, false); + builder.addItemFlags(ItemFlag.HIDE_ENCHANTS); + builder.setDisplayName("§cAdministration panel..."); + builder.addLoreLines("§7Access the administration panel."); + return builder; }, click -> { final ClickType clickType = click.getClickType(); if (clickType.isLeftClick() || clickType.isRightClick()) { diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/main/ExitGUI.java b/core/src/main/java/net/artelnatif/nicko/gui/items/main/ExitGUI.java new file mode 100644 index 0000000..3992b2a --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/main/ExitGUI.java @@ -0,0 +1,17 @@ +package net.artelnatif.nicko.gui.items.main; + +import org.bukkit.Material; +import org.bukkit.event.inventory.ClickType; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.SimpleItem; + +public class ExitGUI extends SimpleItem { + public ExitGUI() { + super(new ItemBuilder(Material.OAK_DOOR).setDisplayName("§fExit"), click -> { + final ClickType clickType = click.getClickType(); + if (clickType.isLeftClick() || clickType.isRightClick()) { + click.getEvent().getView().close(); + } + }); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/main/ResetAppearance.java b/core/src/main/java/net/artelnatif/nicko/gui/items/main/ResetAppearance.java new file mode 100644 index 0000000..48fa0d6 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/main/ResetAppearance.java @@ -0,0 +1,42 @@ +package net.artelnatif.nicko.gui.items.main; + +import net.artelnatif.nicko.appearance.AppearanceManager; +import net.artelnatif.nicko.i18n.I18N; +import net.artelnatif.nicko.i18n.I18NDict; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.SuppliedItem; + +public class ResetAppearance extends SuppliedItem { + public ResetAppearance() { + super(() -> { + final ItemBuilder builder = new ItemBuilder(Material.TNT); + builder.setDisplayName("§fReset"); + builder.addLoreLines("§7Get rid of your disguise."); + return builder; + }, (event) -> { + final Player player = event.getPlayer(); + final ClickType clickType = event.getClickType(); + if (clickType.isLeftClick() || clickType.isRightClick()) { + final AppearanceManager appearanceManager = AppearanceManager.get(player); + + if (!appearanceManager.hasData()) { + player.sendMessage(I18N.translate(player, I18NDict.Event.Undisguise.NONE)); + event.getEvent().getView().close(); + return true; + } + + if (!appearanceManager.reset().isError()) { + player.sendMessage(I18N.translate(player, I18NDict.Event.Undisguise.SUCCESS)); + return true; + } else { + player.sendMessage(I18N.translate(player, I18NDict.Event.Undisguise.FAIL)); + return false; + } + } + return false; + }); + } +} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/home/SettingsAccessItem.java b/core/src/main/java/net/artelnatif/nicko/gui/items/main/SettingsSubGUI.java similarity index 51% rename from src/main/java/xyz/ineanto/nicko/gui/items/home/SettingsAccessItem.java rename to core/src/main/java/net/artelnatif/nicko/gui/items/main/SettingsSubGUI.java index ea3f849..52f9c28 100644 --- a/src/main/java/xyz/ineanto/nicko/gui/items/home/SettingsAccessItem.java +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/main/SettingsSubGUI.java @@ -1,25 +1,18 @@ -package xyz.ineanto.nicko.gui.items.home; +package net.artelnatif.nicko.gui.items.main; +import net.artelnatif.nicko.gui.SettingsGUI; import org.bukkit.Material; -import org.bukkit.entity.Player; import org.bukkit.event.inventory.ClickType; -import xyz.ineanto.nicko.gui.SettingsGUI; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; import xyz.xenondevs.invui.item.builder.ItemBuilder; import xyz.xenondevs.invui.item.impl.SuppliedItem; -public class SettingsAccessItem { - private final PlayerLanguage playerLanguage; - - public SettingsAccessItem(Player player) { - this.playerLanguage = new PlayerLanguage(player); - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { +public class SettingsSubGUI extends SuppliedItem { + public SettingsSubGUI() { + super(() -> { final ItemBuilder builder = new ItemBuilder(Material.COMPARATOR); - return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.SETTINGS); + builder.setDisplayName("§fSettings..."); + builder.addLoreLines("§7Adjust your preferences."); + return builder; }, click -> { final ClickType clickType = click.getClickType(); if (clickType.isLeftClick() || clickType.isRightClick()) { diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/settings/BungeeCordCycling.java b/core/src/main/java/net/artelnatif/nicko/gui/items/settings/BungeeCordCycling.java new file mode 100644 index 0000000..61a389b --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/settings/BungeeCordCycling.java @@ -0,0 +1,49 @@ +package net.artelnatif.nicko.gui.items.settings; + +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.NickoProfile; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.AbstractItem; +import xyz.xenondevs.invui.item.impl.CycleItem; +import xyz.xenondevs.invui.item.impl.SimpleItem; + +import java.util.Optional; + +public class BungeeCordCycling { + private final ItemProvider[] providers = new ItemProvider[]{ + getItemProviderForValue(true), + getItemProviderForValue(false) + }; + + public AbstractItem get(Player player) { + final Optional profile = NickoBukkit.getInstance().getDataStore().getData(player.getUniqueId()); + if (profile.isPresent()) { + final NickoProfile nickoProfile = profile.get(); + int startingState = nickoProfile.isBungeecordTransfer() ? 0 : 1; + return CycleItem.withStateChangeHandler((observer, integer) -> { + nickoProfile.setBungeecordTransfer(integer != 1); + observer.playSound(player, Sound.UI_BUTTON_CLICK, 1f, 0.707107f); // 0.707107 ~= C + }, startingState, providers); + } + + return new SimpleItem(ItemProvider.EMPTY); + } + + private ItemProvider getItemProviderForValue(boolean enabled) { + final ItemBuilder builder = new ItemBuilder(Material.COMPASS); + builder.setDisplayName("§6BungeeCord transfer:"); + if (enabled) { + builder.addLoreLines("§7> §cDisabled"); + builder.addLoreLines("§6§l> §a§lEnabled"); + } else { + builder.addLoreLines("§6§l> §c§lDisabled"); + builder.addLoreLines("§7> §aEnabled"); + } + builder.addLoreLines("§7§oCycle through the values by", "§7§oleft and right clicking."); + return builder; + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/settings/LanguageCycling.java b/core/src/main/java/net/artelnatif/nicko/gui/items/settings/LanguageCycling.java new file mode 100644 index 0000000..abe1399 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/settings/LanguageCycling.java @@ -0,0 +1,63 @@ +package net.artelnatif.nicko.gui.items.settings; + +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.i18n.Locale; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.AbstractItem; +import xyz.xenondevs.invui.item.impl.CycleItem; +import xyz.xenondevs.invui.item.impl.SimpleItem; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class LanguageCycling { + private final ItemProvider[] providers = getItems(); + + public AbstractItem get(Player player) { + final Optional profile = NickoBukkit.getInstance().getDataStore().getData(player.getUniqueId()); + if (profile.isPresent()) { + final NickoProfile nickoProfile = profile.get(); + int localeOrdinal = nickoProfile.getLocale().ordinal(); + return CycleItem.withStateChangeHandler((observer, integer) -> { + nickoProfile.setLocale(Locale.values()[integer]); + observer.playSound(player, Sound.UI_BUTTON_CLICK, 1f, 0.707107f); // 0.707107 ~= C + }, localeOrdinal, providers); + } + + return new SimpleItem(ItemProvider.EMPTY); + } + + private ItemProvider generateItem(Locale locale, List locales) { + final ItemBuilder builder = new ItemBuilder(Material.OAK_SIGN); + builder.setDisplayName("§6Select your language:"); + for (Locale value : locales) { + if (locale != value) { + builder.addLoreLines("§7> " + value.getName()); + } else { + builder.addLoreLines("§6§l> §f" + value.getName()); + } + } + builder.addLoreLines("§7§oCycle through the values by", "§7§oleft and right clicking."); + return builder; + } + + private ItemProvider[] getItems() { + final NickoBukkit instance = NickoBukkit.getInstance(); + final ArrayList items = new ArrayList<>(); + + final ArrayList localesToGenerate = new ArrayList<>(); + Collections.addAll(localesToGenerate, Locale.values()); + if (!instance.getNickoConfig().isCustomLocale()) { + localesToGenerate.remove(Locale.CUSTOM); + } + localesToGenerate.forEach(locale -> items.add(generateItem(locale, localesToGenerate))); + return items.toArray(new ItemProvider[]{}); + } +} \ No newline at end of file diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/skin/ChangeName.java b/core/src/main/java/net/artelnatif/nicko/gui/items/skin/ChangeName.java new file mode 100644 index 0000000..c485e1a --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/skin/ChangeName.java @@ -0,0 +1,26 @@ +package net.artelnatif.nicko.gui.items.skin; + +import net.artelnatif.nicko.anvil.AnvilManager; +import org.bukkit.Material; +import org.bukkit.event.inventory.ClickType; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.SuppliedItem; + +public class ChangeName extends SuppliedItem { + public ChangeName() { + super(() -> { + final ItemBuilder builder = new ItemBuilder(Material.NAME_TAG); + builder.setDisplayName("§fChange §6name"); + builder.addLoreLines("§7Only change your name."); + return builder; + }, click -> { + final ClickType clickType = click.getClickType(); + if (clickType.isLeftClick() || clickType.isRightClick()) { + click.getEvent().getView().close(); + final AnvilManager manager = new AnvilManager(click.getPlayer()); + manager.openNameAnvil(); + } + return true; + }); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/skin/ChangeNameAndSkin.java b/core/src/main/java/net/artelnatif/nicko/gui/items/skin/ChangeNameAndSkin.java new file mode 100644 index 0000000..91fe0ff --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/skin/ChangeNameAndSkin.java @@ -0,0 +1,26 @@ +package net.artelnatif.nicko.gui.items.skin; + +import net.artelnatif.nicko.anvil.AnvilManager; +import org.bukkit.Material; +import org.bukkit.event.inventory.ClickType; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.SuppliedItem; + +public class ChangeNameAndSkin extends SuppliedItem { + public ChangeNameAndSkin() { + super(() -> { + final ItemBuilder builder = new ItemBuilder(Material.END_PORTAL_FRAME); + builder.setDisplayName("§6Skin §fand §6name §fchange"); + builder.addLoreLines("§7Change both your skin and name."); + return builder; + }, click -> { + final ClickType clickType = click.getClickType(); + if (clickType.isLeftClick() || clickType.isRightClick()) { + click.getEvent().getView().close(); + final AnvilManager manager = new AnvilManager(click.getPlayer()); + manager.openNameThenSkinAnvil(); + } + return true; + }); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/gui/items/skin/ChangeSkin.java b/core/src/main/java/net/artelnatif/nicko/gui/items/skin/ChangeSkin.java new file mode 100644 index 0000000..e3b96a3 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/gui/items/skin/ChangeSkin.java @@ -0,0 +1,26 @@ +package net.artelnatif.nicko.gui.items.skin; + +import net.artelnatif.nicko.anvil.AnvilManager; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import xyz.xenondevs.invui.item.builder.SkullBuilder; +import xyz.xenondevs.invui.item.impl.SuppliedItem; + +public class ChangeSkin extends SuppliedItem { + public ChangeSkin(Player player) { + super(() -> { + final SkullBuilder builder = new SkullBuilder(player.getName()); + builder.setDisplayName("§fChange §6skin"); + builder.addLoreLines("§7Only change your skin."); + return builder; + }, click -> { + final ClickType clickType = click.getClickType(); + if (clickType.isLeftClick() || clickType.isRightClick()) { + click.getEvent().getView().close(); + final AnvilManager manager = new AnvilManager(click.getPlayer()); + manager.openSkinAnvil(); + } + return true; + }); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/i18n/I18N.java b/core/src/main/java/net/artelnatif/nicko/i18n/I18N.java new file mode 100644 index 0000000..40fe6ef --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/i18n/I18N.java @@ -0,0 +1,62 @@ +package net.artelnatif.nicko.i18n; + +import com.github.jsixface.YamlConfig; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.NickoProfile; +import org.bukkit.entity.Player; + +import java.io.InputStream; +import java.text.MessageFormat; +import java.util.Optional; + +public class I18N { + private final static MessageFormat formatter = new MessageFormat(""); + + private static Locale getLocale(Player player) { + final NickoBukkit instance = NickoBukkit.getInstance(); + try { + final Optional profile = instance.getDataStore().getData(player.getUniqueId()); + 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; + } + } + + public static String translate(Player player, I18NDict key, Object... arguments) { + final NickoBukkit instance = NickoBukkit.getInstance(); + final String translation = findTranslation(player, key); + + try { + formatter.applyPattern(translation); + return instance.getNickoConfig().getPrefix() + formatter.format(arguments); + } catch (Exception e) { + return instance.getNickoConfig().getPrefix() + key.key(); + } + } + + public static String translateWithoutPrefix(Player player, I18NDict key, Object... arguments) { + final String translation = findTranslation(player, key); + try { + formatter.applyPattern(translation); + return formatter.format(arguments); + } catch (Exception e) { + return key.key(); + } + } + + private static String findTranslation(Player player, I18NDict key) { + final NickoBukkit instance = NickoBukkit.getInstance(); + final Locale locale = getLocale(player); + String translation; + if (locale == Locale.CUSTOM) { + translation = instance.getLocaleFileManager().get(key.key()); + } else { + final InputStream resource = instance.getResource(locale.getCode() + ".yml"); + final YamlConfig yamlConfig = YamlConfig.load(resource); + translation = yamlConfig.getString(key.key()); + } + + return translation; + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/i18n/I18NDict.java b/core/src/main/java/net/artelnatif/nicko/i18n/I18NDict.java new file mode 100644 index 0000000..4997810 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/i18n/I18NDict.java @@ -0,0 +1,44 @@ +package net.artelnatif.nicko.i18n; + +public class I18NDict { + private final String key; + + public I18NDict(String key) { this.key = key; } + + public static class Event { + public static class Admin { + public static final I18NDict CACHE_CLEAN = new I18NDict("event.admin.cache_clear"); + } + + public static class Disguise { + public static final I18NDict SUCCESS = new I18NDict("event.disguise.success"); + public static final I18NDict FAIL = new I18NDict("event.disguise.fail"); + } + + public static class Undisguise { + public static final I18NDict SUCCESS = new I18NDict("event.undisguise.success"); + public static final I18NDict FAIL = new I18NDict("event.undisguise.fail"); + public static final I18NDict NONE = new I18NDict("event.undisguise.none"); + } + + public static class PreviousSkin { + public static final I18NDict SUCCESS = new I18NDict("event.previous_skin_applied.success"); + public static final I18NDict FAIL = new I18NDict("event.previous_skin_applied.fail"); + } + } + + public static class Error { + public static final I18NDict PLAYER_OFFLINE = new I18NDict("error.player_offline"); + public static final I18NDict SKIN_FAIL_MOJANG = new I18NDict("error.couldnt_get_skin_from_mojang"); + public static final I18NDict SKIN_FAIL_CACHE = new I18NDict("error.couldnt_get_skin_from_cache"); + public static final I18NDict NAME_FAIL_MOJANG = new I18NDict("error.couldnt_get_name_from_mojang"); + public static final I18NDict INVALID_USERNAME = new I18NDict("error.invalid_username"); + public static final I18NDict UNEXPECTED_ERROR = new I18NDict("error.generic"); + public static final I18NDict SQL_ERROR = new I18NDict("error.sql"); + public static final I18NDict JSON_ERROR = new I18NDict("error.json"); + } + + public String key() { + return key; + } +} diff --git a/src/main/java/xyz/ineanto/nicko/language/Language.java b/core/src/main/java/net/artelnatif/nicko/i18n/Locale.java similarity index 59% rename from src/main/java/xyz/ineanto/nicko/language/Language.java rename to core/src/main/java/net/artelnatif/nicko/i18n/Locale.java index 3511342..479e5cf 100644 --- a/src/main/java/xyz/ineanto/nicko/language/Language.java +++ b/core/src/main/java/net/artelnatif/nicko/i18n/Locale.java @@ -1,26 +1,24 @@ -package xyz.ineanto.nicko.language; - -import xyz.ineanto.nicko.version.Version; +package net.artelnatif.nicko.i18n; import java.io.Serializable; -public enum Language implements Serializable { +public enum Locale implements Serializable { ENGLISH("en", "English"), FRENCH("fr", "Français"), CUSTOM("cm", "Server Custom"); - public static final Version VERSION = new Version(1, 4, 0); + public static final Locale FALLBACK_LOCALE = ENGLISH; private final String code; private transient final String name; - Language(String code, String name) { + Locale(String code, String name) { this.code = code; this.name = name; } - public static Language fromCode(String code) { - for (Language value : values()) { + public static Locale fromCode(String code) { + for (Locale value : values()) { if (code.equals(value.code)) return value; } return ENGLISH; diff --git a/core/src/main/java/net/artelnatif/nicko/i18n/LocaleFileManager.java b/core/src/main/java/net/artelnatif/nicko/i18n/LocaleFileManager.java new file mode 100644 index 0000000..ed7fe00 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/i18n/LocaleFileManager.java @@ -0,0 +1,42 @@ +package net.artelnatif.nicko.i18n; + +import com.github.jsixface.YamlConfig; +import net.artelnatif.nicko.NickoBukkit; +import xyz.xenondevs.invui.util.IOUtils; + +import java.io.*; +import java.nio.file.Files; + +public class LocaleFileManager { + private final File folder = new File(NickoBukkit.getInstance().getDataFolder() + "/lang/"); + private final File file = new File(folder, "lang.yml"); + + public String get(String key) { + if (!file.exists()) return key; + try (BufferedInputStream inputStream = new BufferedInputStream(Files.newInputStream(file.toPath()))) { + final YamlConfig yamlConfig = YamlConfig.load(inputStream); + return yamlConfig.getString(key); + } catch (IOException e) { + return key; + } + } + + public boolean dumpFromLocale(Locale locale) { + if (locale == Locale.CUSTOM) return true; + if (file.exists()) return true; + final InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(locale.getCode() + ".yml"); + try { + if (folder.mkdirs()) { + if (file.createNewFile()) { + try (FileOutputStream outputStream = new FileOutputStream(file)) { + IOUtils.copy(inputStream, outputStream, 8192); + } + } + } + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/impl/Internals.java b/core/src/main/java/net/artelnatif/nicko/impl/Internals.java new file mode 100644 index 0000000..0c68318 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/impl/Internals.java @@ -0,0 +1,38 @@ +package net.artelnatif.nicko.impl; + +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.i18n.I18NDict; +import net.artelnatif.nicko.mojang.MojangAPI; +import net.artelnatif.nicko.mojang.MojangSkin; +import org.bukkit.entity.Player; + +import java.io.IOException; +import java.util.Optional; +import java.util.concurrent.ExecutionException; + +public interface Internals { + void updateSelf(Player player); + + void updateOthers(Player player); + + ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset); + + default ActionResult fetchSkinTextures(NickoProfile profile, boolean reset) { + Optional skin; + try { + final MojangAPI mojang = NickoBukkit.getInstance().getMojangAPI(); + final Optional uuid = mojang.getUUID(profile.getSkin()); + if (uuid.isPresent()) { + skin = (reset ? mojang.getSkinWithoutCaching(uuid.get()) : mojang.getSkin(uuid.get())); + return skin.map(ActionResult::new).orElseGet(() -> new ActionResult<>(I18NDict.Error.SKIN_FAIL_MOJANG)); + } + return new ActionResult<>(I18NDict.Error.NAME_FAIL_MOJANG); + } catch (ExecutionException e) { + return new ActionResult<>(I18NDict.Error.SKIN_FAIL_CACHE); + } catch (IOException e) { + return new ActionResult<>(I18NDict.Error.NAME_FAIL_MOJANG); + } + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/impl/InternalsProvider.java b/core/src/main/java/net/artelnatif/nicko/impl/InternalsProvider.java new file mode 100644 index 0000000..d0ebd0d --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/impl/InternalsProvider.java @@ -0,0 +1,29 @@ +package net.artelnatif.nicko.impl; + +import org.bukkit.Bukkit; + +import java.lang.reflect.InvocationTargetException; +import java.util.logging.Logger; + +public class InternalsProvider { + private static final Logger logger = Logger.getLogger("Internals"); + private static Internals internals; + + static { + try { + final String packageName = Internals.class.getPackage().getName(); + final String bukkitVersion = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; + final String fullClassName = packageName + "." + bukkitVersion; + final Class clazz = Class.forName(fullClassName); + internals = (Internals) clazz.getConstructors()[0].newInstance(); + logger.info("Loaded support for " + bukkitVersion); + } catch (InvocationTargetException | ClassNotFoundException | InstantiationException | IllegalAccessException | + ClassCastException exception) { + internals = null; + } + } + + public static Internals getInternals() { + return internals; + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/mojang/MojangAPI.java b/core/src/main/java/net/artelnatif/nicko/mojang/MojangAPI.java new file mode 100644 index 0000000..d8b3bf2 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/mojang/MojangAPI.java @@ -0,0 +1,116 @@ +package net.artelnatif.nicko.mojang; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; + +import javax.annotation.Nonnull; +import javax.net.ssl.HttpsURLConnection; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +public class MojangAPI { + public static final String URL_NAME = "https://api.mojang.com/users/profiles/minecraft/{name}"; + public static final String URL_SKIN = "https://sessionserver.mojang.com/session/minecraft/profile/{uuid}?unsigned=false"; + + private final Logger logger = Logger.getLogger("MojangAPI"); + + private final CacheLoader> loader = new CacheLoader>() { + @Nonnull + public Optional load(@Nonnull String uuid) throws Exception { + return getSkinFromMojang(uuid); + } + }; + + private final LoadingCache> cache = CacheBuilder + .newBuilder() + .recordStats() + .expireAfterWrite(24, TimeUnit.HOURS) + .build(loader); + + public Optional getSkin(String uuid) throws IOException, ExecutionException { + return cache.get(uuid); + } + + public Optional getSkinWithoutCaching(String uuid) throws IOException { + return getSkinFromMojang(uuid); + } + + public Optional getUUID(String name) throws IOException { + final String parametrizedUrl = URL_NAME.replace("{name}", name); + final JsonObject object = getRequestToUrl(parametrizedUrl); + if (hasNoError(object)) { + return Optional.of(object.get("id").getAsString()); + } + return Optional.empty(); + } + + private Optional getSkinFromMojang(String uuid) throws IOException { + final String parametrizedUrl = URL_SKIN.replace("{uuid}", uuid); + final JsonObject object = getRequestToUrl(parametrizedUrl); + if (hasNoError(object)) { + final MojangSkin skin = MojangSkin.buildFromJson(object); + return Optional.of(skin); + } + + return Optional.empty(); + } + + private JsonObject getRequestToUrl(String parametrizedUrl) throws IOException { + final URL url = new URL(parametrizedUrl); + final HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); + con.setDoInput(true); + con.setRequestMethod("GET"); + + switch (con.getResponseCode()) { + case 400: + logger.warning("Failed to parse request: Invalid Name"); + return getErrorObject(); + case 429: + logger.warning("Failed to parse request: The connection is throttled."); + return getErrorObject(); + case 200: + final BufferedReader input = new BufferedReader(new InputStreamReader(con.getInputStream())); + final StringBuilder builder = new StringBuilder(); + String line; + while ((line = input.readLine()) != null) { + builder.append(line); + } + + try { + final JsonElement jsonElt = JsonParser.parseString(builder.toString()); + return jsonElt.getAsJsonObject(); + } catch (JsonParseException | IllegalStateException exception) { + logger.warning("Failed to parse request (" + parametrizedUrl + ")!"); + return getErrorObject(); + } + default: + logger.warning("Unhandled response code from Mojang: " + con.getResponseCode()); + return getErrorObject(); + } + } + + private JsonObject getErrorObject() { + final JsonObject errorObject = new JsonObject(); + errorObject.addProperty("error", "An error occurred."); + return errorObject; + } + + private boolean hasNoError(JsonObject object) { + return object.get("error") == null; + } + + public LoadingCache> getCache() { + return cache; + } +} diff --git a/src/main/java/xyz/ineanto/nicko/mojang/MojangSkin.java b/core/src/main/java/net/artelnatif/nicko/mojang/MojangSkin.java similarity index 52% rename from src/main/java/xyz/ineanto/nicko/mojang/MojangSkin.java rename to core/src/main/java/net/artelnatif/nicko/mojang/MojangSkin.java index 0b22223..8980bcb 100644 --- a/src/main/java/xyz/ineanto/nicko/mojang/MojangSkin.java +++ b/core/src/main/java/net/artelnatif/nicko/mojang/MojangSkin.java @@ -1,12 +1,16 @@ -package xyz.ineanto.nicko.mojang; +package net.artelnatif.nicko.mojang; -import com.destroystokyo.paper.profile.ProfileProperty; import com.google.gson.JsonObject; -import java.util.Collection; -import java.util.Collections; +public class MojangSkin { + private final String value; + private final String signature; + + public MojangSkin(String value, String signature) { + this.value = value; + this.signature = signature; + } -public record MojangSkin(String value, String signature) { public static MojangSkin buildFromJson(JsonObject object) { final JsonObject properties = object.get("properties").getAsJsonArray().get(0).getAsJsonObject(); final String value = properties.get("value").getAsString(); @@ -14,7 +18,11 @@ public record MojangSkin(String value, String signature) { return new MojangSkin(value, signature); } - public Collection asProfileProperties() { - return Collections.singleton(new ProfileProperty("textures", value, signature)); + public String getValue() { + return value; + } + + public String getSignature() { + return signature; } } diff --git a/src/main/java/xyz/ineanto/nicko/mojang/MojangUtils.java b/core/src/main/java/net/artelnatif/nicko/mojang/MojangUtils.java similarity index 95% rename from src/main/java/xyz/ineanto/nicko/mojang/MojangUtils.java rename to core/src/main/java/net/artelnatif/nicko/mojang/MojangUtils.java index 6ed3606..135c210 100644 --- a/src/main/java/xyz/ineanto/nicko/mojang/MojangUtils.java +++ b/core/src/main/java/net/artelnatif/nicko/mojang/MojangUtils.java @@ -1,4 +1,4 @@ -package xyz.ineanto.nicko.mojang; +package net.artelnatif.nicko.mojang; import java.util.UUID; import java.util.regex.Pattern; diff --git a/src/main/java/xyz/ineanto/nicko/placeholder/NickoExpansion.java b/core/src/main/java/net/artelnatif/nicko/placeholder/NickoExpansion.java similarity index 59% rename from src/main/java/xyz/ineanto/nicko/placeholder/NickoExpansion.java rename to core/src/main/java/net/artelnatif/nicko/placeholder/NickoExpansion.java index b51455a..7f3e39d 100644 --- a/src/main/java/xyz/ineanto/nicko/placeholder/NickoExpansion.java +++ b/core/src/main/java/net/artelnatif/nicko/placeholder/NickoExpansion.java @@ -1,19 +1,19 @@ -package xyz.ineanto.nicko.placeholder; +package net.artelnatif.nicko.placeholder; import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.NickoProfile; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.profile.NickoProfile; import java.util.Optional; public class NickoExpansion extends PlaceholderExpansion { - private final Nicko instance; + private final NickoBukkit instance; - public NickoExpansion(Nicko instance) { + public NickoExpansion(NickoBukkit instance) { this.instance = instance; } @@ -24,12 +24,12 @@ public class NickoExpansion extends PlaceholderExpansion { @Override public @NotNull String getAuthor() { - return "Ineanto"; + return "Aro"; } @Override public @NotNull String getVersion() { - return "1.0.1"; + return "1.0.0"; } @Override @@ -42,27 +42,29 @@ public class NickoExpansion extends PlaceholderExpansion { if (player == null) return null; String name, skin, locale; - boolean randomSkin; + boolean bungeecord; name = skin = player.getName(); locale = "N/A"; - randomSkin = false; + bungeecord = true; final Optional optionalProfile = instance.getDataStore().getData(player.getUniqueId()); if (optionalProfile.isPresent()) { final NickoProfile profile = optionalProfile.get(); - name = profile.getName() == null ? player.getName() : profile.getName(); - skin = profile.getSkin() == null ? player.getName() : profile.getSkin(); + if (!profile.isEmpty()) { + name = profile.getName(); + skin = profile.getSkin(); + } locale = profile.getLocale().getName(); - randomSkin = profile.isRandomSkin(); + bungeecord = profile.isBungeecordTransfer(); } - return switch (params) { - case "name" -> name; - case "skin" -> skin; - case "locale" -> locale; - case "random_skin" -> String.valueOf(randomSkin); - default -> null; - }; + switch (params) { + case "name": return name; + case "skin": return skin; + case "locale": return locale; + case "bungeecord": return String.valueOf(bungeecord); + default: return null; + } } } diff --git a/core/src/main/java/net/artelnatif/nicko/placeholder/PlaceHolderHook.java b/core/src/main/java/net/artelnatif/nicko/placeholder/PlaceHolderHook.java new file mode 100644 index 0000000..c4d3750 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/placeholder/PlaceHolderHook.java @@ -0,0 +1,19 @@ +package net.artelnatif.nicko.placeholder; + +import net.artelnatif.nicko.NickoBukkit; +import org.bukkit.Bukkit; + +public class PlaceHolderHook { + private final NickoBukkit instance; + + public PlaceHolderHook(NickoBukkit instance) { + this.instance = instance; + } + + public void hook() { + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { + instance.getLogger().info("Enabling PlaceHolderAPI support..."); + new NickoExpansion(instance).register(); + } + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/storage/PlayerDataStore.java b/core/src/main/java/net/artelnatif/nicko/storage/PlayerDataStore.java new file mode 100644 index 0000000..002c839 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/storage/PlayerDataStore.java @@ -0,0 +1,92 @@ +package net.artelnatif.nicko.storage; + +import net.artelnatif.nicko.config.Configuration; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.i18n.I18NDict; +import net.artelnatif.nicko.mojang.MojangAPI; +import net.artelnatif.nicko.mojang.MojangUtils; +import net.artelnatif.nicko.storage.cache.Cache; +import net.artelnatif.nicko.storage.cache.redis.RedisCache; +import net.artelnatif.nicko.storage.json.JSONStorage; +import net.artelnatif.nicko.storage.sql.SQLStorage; +import org.bukkit.entity.Player; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Optional; +import java.util.UUID; + +public class PlayerDataStore { + private final Storage storage; + private final Cache cache; + private final MojangAPI mojangAPI; + private final HashMap profiles = new HashMap<>(); + + public PlayerDataStore(MojangAPI mojangAPI, Configuration configuration) { + this.mojangAPI = mojangAPI; + this.storage = configuration.isLocal() ? new JSONStorage() : new SQLStorage(configuration); + this.cache = new RedisCache(); // The only option for now! + } + + public void performProfileUpdate(UUID uuid, NickoProfile profile) { + if (!profiles.containsKey(uuid)) { + profiles.put(uuid, profile); + return; + } + + profiles.replace(uuid, profile); + } + + public Optional getData(UUID uuid) { + if (storage.isError()) { + return Optional.empty(); + } + + if (profiles.containsKey(uuid)) { + return Optional.of(profiles.get(uuid)); + } else if (storage.isStored(uuid)) { + final Optional retrievedProfile = storage.retrieve(uuid); + retrievedProfile.ifPresent(profile -> profiles.put(uuid, profile)); + return retrievedProfile; + } else { + final NickoProfile newProfile = NickoProfile.EMPTY_PROFILE.clone(); + profiles.put(uuid, newProfile); + return Optional.of(newProfile); + } + } + + public Optional getOfflineData(String name) { + if (storage.isError()) { + return Optional.empty(); + } + + try { + final Optional uuidTrimmed = mojangAPI.getUUID(name); + if (uuidTrimmed.isPresent()) { + final UUID uuid = MojangUtils.fromTrimmed(uuidTrimmed.get()); + return getData(uuid); + } + return Optional.empty(); + } catch (IOException e) { + return Optional.empty(); + } + } + + 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); } + + final ActionResult store = storage.store(player.getUniqueId(), profiles.get(player.getUniqueId())); + profiles.remove(player.getUniqueId()); + return store; + } + + public Storage getStorage() { + return storage; + } + + public Cache getCache() { + return cache; + } +} diff --git a/src/main/java/xyz/ineanto/nicko/storage/Storage.java b/core/src/main/java/net/artelnatif/nicko/storage/Storage.java similarity index 62% rename from src/main/java/xyz/ineanto/nicko/storage/Storage.java rename to core/src/main/java/net/artelnatif/nicko/storage/Storage.java index 9cac04a..b23b1ef 100644 --- a/src/main/java/xyz/ineanto/nicko/storage/Storage.java +++ b/core/src/main/java/net/artelnatif/nicko/storage/Storage.java @@ -1,7 +1,7 @@ -package xyz.ineanto.nicko.storage; +package net.artelnatif.nicko.storage; -import xyz.ineanto.nicko.appearance.ActionResult; -import xyz.ineanto.nicko.profile.NickoProfile; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; import java.util.Optional; import java.util.UUID; @@ -11,14 +11,12 @@ public abstract class Storage { public abstract StorageProvider getProvider(); - public abstract ActionResult store(UUID uuid, NickoProfile profile); + public abstract ActionResult store(UUID uuid, NickoProfile profile); public abstract boolean isStored(UUID uuid); public abstract Optional retrieve(UUID uuid); - public abstract ActionResult delete(UUID uuid); - public boolean isError() { return error; } diff --git a/src/main/java/xyz/ineanto/nicko/storage/StorageProvider.java b/core/src/main/java/net/artelnatif/nicko/storage/StorageProvider.java similarity index 67% rename from src/main/java/xyz/ineanto/nicko/storage/StorageProvider.java rename to core/src/main/java/net/artelnatif/nicko/storage/StorageProvider.java index 3981771..41b66de 100644 --- a/src/main/java/xyz/ineanto/nicko/storage/StorageProvider.java +++ b/core/src/main/java/net/artelnatif/nicko/storage/StorageProvider.java @@ -1,4 +1,4 @@ -package xyz.ineanto.nicko.storage; +package net.artelnatif.nicko.storage; public interface StorageProvider { boolean init(); diff --git a/src/main/java/xyz/ineanto/nicko/storage/Cache.java b/core/src/main/java/net/artelnatif/nicko/storage/cache/Cache.java similarity index 62% rename from src/main/java/xyz/ineanto/nicko/storage/Cache.java rename to core/src/main/java/net/artelnatif/nicko/storage/cache/Cache.java index 1b2616d..92d5b75 100644 --- a/src/main/java/xyz/ineanto/nicko/storage/Cache.java +++ b/core/src/main/java/net/artelnatif/nicko/storage/cache/Cache.java @@ -1,7 +1,7 @@ -package xyz.ineanto.nicko.storage; +package net.artelnatif.nicko.storage.cache; -import xyz.ineanto.nicko.appearance.ActionResult; -import xyz.ineanto.nicko.profile.NickoProfile; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; import java.util.Optional; import java.util.UUID; @@ -11,14 +11,12 @@ public abstract class Cache { public abstract CacheProvider getProvider(); - public abstract ActionResult cache(UUID uuid, NickoProfile profile); + public abstract ActionResult cache(UUID uuid, NickoProfile profile); public abstract boolean isCached(UUID uuid); public abstract Optional retrieve(UUID uuid); - public abstract ActionResult delete(UUID uuid); - public boolean isError() { return error; } diff --git a/src/main/java/xyz/ineanto/nicko/storage/CacheProvider.java b/core/src/main/java/net/artelnatif/nicko/storage/cache/CacheProvider.java similarity index 63% rename from src/main/java/xyz/ineanto/nicko/storage/CacheProvider.java rename to core/src/main/java/net/artelnatif/nicko/storage/cache/CacheProvider.java index 3597ac5..e85fd3a 100644 --- a/src/main/java/xyz/ineanto/nicko/storage/CacheProvider.java +++ b/core/src/main/java/net/artelnatif/nicko/storage/cache/CacheProvider.java @@ -1,4 +1,4 @@ -package xyz.ineanto.nicko.storage; +package net.artelnatif.nicko.storage.cache; public interface CacheProvider { boolean init(); diff --git a/core/src/main/java/net/artelnatif/nicko/storage/cache/redis/RedisCache.java b/core/src/main/java/net/artelnatif/nicko/storage/cache/redis/RedisCache.java new file mode 100644 index 0000000..824cd72 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/storage/cache/redis/RedisCache.java @@ -0,0 +1,31 @@ +package net.artelnatif.nicko.storage.cache.redis; + +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.storage.cache.Cache; +import net.artelnatif.nicko.storage.cache.CacheProvider; + +import java.util.Optional; +import java.util.UUID; + +public class RedisCache extends Cache { + @Override + public CacheProvider getProvider() { + return null; + } + + @Override + public ActionResult cache(UUID uuid, NickoProfile profile) { + return null; + } + + @Override + public boolean isCached(UUID uuid) { + return false; + } + + @Override + public Optional retrieve(UUID uuid) { + return Optional.empty(); + } +} diff --git a/core/src/main/java/net/artelnatif/nicko/storage/cache/redis/RedisCacheProvider.java b/core/src/main/java/net/artelnatif/nicko/storage/cache/redis/RedisCacheProvider.java new file mode 100644 index 0000000..65347e3 --- /dev/null +++ b/core/src/main/java/net/artelnatif/nicko/storage/cache/redis/RedisCacheProvider.java @@ -0,0 +1,26 @@ +package net.artelnatif.nicko.storage.cache.redis; + +import net.artelnatif.nicko.storage.cache.CacheProvider; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; + +public class RedisCacheProvider implements CacheProvider { + private JedisPool pool; + + @Override + public boolean init() { + // TODO: 3/12/23 Get port from configuration + pool = new JedisPool("localhost", 6379); + return !pool.isClosed() && pool.getResource() != null; + } + + @Override + public boolean close() { + pool.close(); + return pool.isClosed(); + } + + public Jedis getJedis() { + return pool.getResource(); + } +} diff --git a/src/main/java/xyz/ineanto/nicko/storage/json/JSONStorage.java b/core/src/main/java/net/artelnatif/nicko/storage/json/JSONStorage.java similarity index 67% rename from src/main/java/xyz/ineanto/nicko/storage/json/JSONStorage.java rename to core/src/main/java/net/artelnatif/nicko/storage/json/JSONStorage.java index cf7a15c..a0f6846 100644 --- a/src/main/java/xyz/ineanto/nicko/storage/json/JSONStorage.java +++ b/core/src/main/java/net/artelnatif/nicko/storage/json/JSONStorage.java @@ -1,12 +1,13 @@ -package xyz.ineanto.nicko.storage.json; +package net.artelnatif.nicko.storage.json; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.appearance.ActionResult; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.ineanto.nicko.storage.Storage; -import xyz.ineanto.nicko.storage.StorageProvider; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.i18n.I18NDict; +import net.artelnatif.nicko.storage.Storage; +import net.artelnatif.nicko.storage.StorageProvider; import java.io.*; import java.util.Optional; @@ -16,7 +17,7 @@ import java.util.logging.Logger; public class JSONStorage extends Storage { private final Logger logger = Logger.getLogger("JSONStorage"); private final Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create(); - private final File directory = new File(Nicko.getInstance().getDataFolder() + "/players/"); + private final File directory = new File(NickoBukkit.getInstance().getDataFolder() + "/players/"); private JSONStorageProvider provider; @@ -29,7 +30,7 @@ public class JSONStorage extends Storage { } @Override - public ActionResult store(UUID uuid, NickoProfile profile) { + public ActionResult store(UUID uuid, NickoProfile profile) { final String profileToJson = gson.toJson(profile); final File file = new File(directory, uuid.toString() + ".json"); @@ -41,26 +42,27 @@ public class JSONStorage extends Storage { } } catch (IOException e) { logger.warning("Could not write to file."); - return ActionResult.error(); + return new ActionResult<>(I18NDict.Error.JSON_ERROR); } } } catch (IOException e) { logger.warning("Could not create file."); - e.printStackTrace(); - return ActionResult.error(); + return new ActionResult<>(I18NDict.Error.JSON_ERROR); } - return ActionResult.ok(); + return new ActionResult<>(); } @Override public boolean isStored(UUID uuid) { + final File directory = new File(NickoBukkit.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(NickoBukkit.getInstance().getDataFolder() + "/players/"); final File file = new File(directory, uuid.toString() + ".json"); try (FileReader fileReader = new FileReader(file)) { try (BufferedReader reader = new BufferedReader(fileReader)) { @@ -72,21 +74,7 @@ public class JSONStorage extends Storage { } } - @Override - public ActionResult delete(UUID uuid) { - final File file = new File(directory, uuid.toString() + ".json"); - if (file.delete() || !file.exists()) { - return ActionResult.ok(); - } - return ActionResult.error(); - } - 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/core/src/main/java/net/artelnatif/nicko/storage/json/JSONStorageProvider.java similarity index 63% rename from src/main/java/xyz/ineanto/nicko/storage/json/JSONStorageProvider.java rename to core/src/main/java/net/artelnatif/nicko/storage/json/JSONStorageProvider.java index e360b6f..ac2be77 100644 --- a/src/main/java/xyz/ineanto/nicko/storage/json/JSONStorageProvider.java +++ b/core/src/main/java/net/artelnatif/nicko/storage/json/JSONStorageProvider.java @@ -1,6 +1,6 @@ -package xyz.ineanto.nicko.storage.json; +package net.artelnatif.nicko.storage.json; -import xyz.ineanto.nicko.storage.StorageProvider; +import net.artelnatif.nicko.storage.StorageProvider; import java.io.File; @@ -12,12 +12,11 @@ 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/name/PlayerNameStore.java b/core/src/main/java/net/artelnatif/nicko/storage/name/PlayerNameStore.java similarity index 93% rename from src/main/java/xyz/ineanto/nicko/storage/name/PlayerNameStore.java rename to core/src/main/java/net/artelnatif/nicko/storage/name/PlayerNameStore.java index 1e90b11..c439d4e 100644 --- a/src/main/java/xyz/ineanto/nicko/storage/name/PlayerNameStore.java +++ b/core/src/main/java/net/artelnatif/nicko/storage/name/PlayerNameStore.java @@ -1,4 +1,4 @@ -package xyz.ineanto.nicko.storage.name; +package net.artelnatif.nicko.storage.name; import org.bukkit.entity.Player; diff --git a/src/main/java/xyz/ineanto/nicko/storage/mariadb/MariaDBStorage.java b/core/src/main/java/net/artelnatif/nicko/storage/sql/SQLStorage.java similarity index 54% rename from src/main/java/xyz/ineanto/nicko/storage/mariadb/MariaDBStorage.java rename to core/src/main/java/net/artelnatif/nicko/storage/sql/SQLStorage.java index 6bc9c43..8dcea73 100644 --- a/src/main/java/xyz/ineanto/nicko/storage/mariadb/MariaDBStorage.java +++ b/core/src/main/java/net/artelnatif/nicko/storage/sql/SQLStorage.java @@ -1,57 +1,53 @@ -package xyz.ineanto.nicko.storage.mariadb; +package net.artelnatif.nicko.storage.sql; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; -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; -import xyz.ineanto.nicko.storage.Storage; +import net.artelnatif.nicko.config.Configuration; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.i18n.I18NDict; +import net.artelnatif.nicko.i18n.Locale; +import net.artelnatif.nicko.storage.Storage; +import java.io.ByteArrayInputStream; +import java.nio.ByteBuffer; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.Collections; -import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.logging.Logger; -public class MariaDBStorage extends Storage { +public class SQLStorage extends Storage { private final Logger logger = Logger.getLogger("SQLStorage"); private final Configuration configuration; - private final Gson gson = new GsonBuilder().serializeNulls().create(); - private MariaDBStorageProvider provider; + private SQLStorageProvider provider; - public MariaDBStorage(Configuration configuration) { + public SQLStorage(Configuration configuration) { this.configuration = configuration; } @Override - public MariaDBStorageProvider getProvider() { + public SQLStorageProvider getProvider() { if (provider == null) { - provider = new MariaDBStorageProvider(configuration); + provider = new SQLStorageProvider(configuration); } return provider; } @Override - public ActionResult store(UUID uuid, NickoProfile profile) { + public ActionResult store(UUID uuid, NickoProfile profile) { final Connection connection = getProvider().getConnection(); - if (connection == null) return ActionResult.error(); + if (connection == null) return new ActionResult<>(I18NDict.Error.SQL_ERROR); try { final PreparedStatement statement = isStored(uuid) ? getUpdateStatement(connection, uuid, profile) : getInsertStatement(connection, uuid, profile); statement.executeUpdate(); - return ActionResult.ok(); + return new ActionResult<>(); } catch (SQLException e) { logger.warning("Couldn't send SQL Request: " + e.getMessage()); - return ActionResult.error(); + return new ActionResult<>(I18NDict.Error.SQL_ERROR); } } @@ -61,10 +57,10 @@ public class MariaDBStorage extends Storage { if (connection == null) return false; try { - final String sql = "SELECT uuid FROM nicko.DATA WHERE uuid = ?"; + final String sql = "SELECT * FROM nicko.DATA WHERE uuid = ?"; final PreparedStatement statement = connection.prepareStatement(sql); - statement.setString(1, uuid.toString()); + statement.setBinaryStream(1, uuidToBin(uuid)); final ResultSet resultSet = statement.executeQuery(); return resultSet.next(); @@ -78,29 +74,26 @@ public class MariaDBStorage extends Storage { public Optional retrieve(UUID uuid) { final Connection connection = getProvider().getConnection(); if (connection == null) return Optional.empty(); - if (!isStored(uuid)) return Optional.empty(); try { final String sql = "SELECT * FROM nicko.DATA WHERE uuid = ?"; final PreparedStatement statement = connection.prepareStatement(sql); - statement.setString(1, uuid.toString()); + statement.setBinaryStream(1, uuidToBin(uuid)); final ResultSet resultSet = statement.executeQuery(); String name = ""; String skin = ""; String locale = ""; - boolean randomSkin = false; - List favorites = Collections.emptyList(); + boolean bungeecord = false; while (resultSet.next()) { name = resultSet.getString("name"); skin = resultSet.getString("skin"); locale = resultSet.getString("locale"); - randomSkin = resultSet.getBoolean("randomskin"); - favorites = gson.fromJson(resultSet.getString("favorites"), new TypeToken>() { }.getType()); + bungeecord = resultSet.getBoolean("bungeecord"); } - final NickoProfile profile = new NickoProfile(new Appearance(name, skin), Language.fromCode(locale), randomSkin, favorites); + final NickoProfile profile = new NickoProfile(name, skin, Locale.fromCode(locale), bungeecord); return Optional.of(profile); } catch (SQLException e) { logger.warning("Couldn't fetch profile: " + e.getMessage()); @@ -108,43 +101,33 @@ public class MariaDBStorage extends Storage { } } - @Override - public ActionResult delete(UUID uuid) { - final Connection connection = getProvider().getConnection(); - if (connection == null) return ActionResult.error(); - - try { - final String sql = "DELETE FROM nicko.DATA WHERE uuid = ?"; - final PreparedStatement statement = connection.prepareStatement(sql); - statement.setString(1, uuid.toString()); - int rows = statement.executeUpdate(); - return (rows == 1 ? ActionResult.ok() : ActionResult.error()); - } catch (SQLException e) { - logger.warning("Couldn't delete profile: " + e.getMessage()); - return ActionResult.error(); - } - } - private PreparedStatement getInsertStatement(Connection connection, UUID uuid, NickoProfile profile) throws SQLException { - final String sql = "INSERT IGNORE INTO nicko.DATA (`uuid`, `name`, `skin`, `locale`, `randomskin`, `favorites`) VALUES (?, ?, ?, ?, ?, ?)"; + final String sql = "INSERT IGNORE INTO nicko.DATA (`uuid`, `name`, `skin`, `locale`, `bungeecord`) VALUES (?, ?, ?, ?, ?)"; final PreparedStatement statement = connection.prepareStatement(sql); - statement.setString(1, uuid.toString()); + statement.setBinaryStream(1, uuidToBin(uuid)); statement.setString(2, profile.getName() == null ? null : profile.getName()); statement.setString(3, profile.getSkin() == null ? null : profile.getSkin()); statement.setString(4, profile.getLocale().getCode()); - statement.setBoolean(5, profile.isRandomSkin()); - statement.setString(6, gson.toJson(profile.getFavorites(), new TypeToken>() { }.getRawType())); + statement.setBoolean(5, profile.isBungeecordTransfer()); return statement; } private PreparedStatement getUpdateStatement(Connection connection, UUID uuid, NickoProfile profile) throws SQLException { - final String sql = "UPDATE nicko.DATA SET name = ?, skin = ?, locale = ?, randomskin = ? WHERE uuid = ?"; + final String sql = "UPDATE nicko.DATA SET name = ?, skin = ?, locale = ?, bungeecord = ? 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()); statement.setString(3, profile.getLocale().getCode()); - statement.setBoolean(4, profile.isRandomSkin()); - statement.setString(5, uuid.toString()); + statement.setBoolean(4, profile.isBungeecordTransfer()); + statement.setBinaryStream(5, uuidToBin(uuid)); return statement; } + + private ByteArrayInputStream uuidToBin(UUID uuid) { + byte[] bytes = new byte[16]; + ByteBuffer.wrap(bytes) + .putLong(uuid.getMostSignificantBits()) + .putLong(uuid.getLeastSignificantBits()); + return new ByteArrayInputStream(bytes); + } } diff --git a/src/main/java/xyz/ineanto/nicko/storage/mariadb/MariaDBStorageProvider.java b/core/src/main/java/net/artelnatif/nicko/storage/sql/SQLStorageProvider.java similarity index 54% rename from src/main/java/xyz/ineanto/nicko/storage/mariadb/MariaDBStorageProvider.java rename to core/src/main/java/net/artelnatif/nicko/storage/sql/SQLStorageProvider.java index 045e107..1a733e7 100644 --- a/src/main/java/xyz/ineanto/nicko/storage/mariadb/MariaDBStorageProvider.java +++ b/core/src/main/java/net/artelnatif/nicko/storage/sql/SQLStorageProvider.java @@ -1,24 +1,24 @@ -package xyz.ineanto.nicko.storage.mariadb; +package net.artelnatif.nicko.storage.sql; +import net.artelnatif.nicko.config.Configuration; +import net.artelnatif.nicko.config.DataSourceConfiguration; +import net.artelnatif.nicko.storage.StorageProvider; import org.mariadb.jdbc.MariaDbDataSource; -import xyz.ineanto.nicko.config.Configuration; -import xyz.ineanto.nicko.config.DataSourceConfiguration; -import xyz.ineanto.nicko.storage.StorageProvider; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.logging.Logger; -public class MariaDBStorageProvider implements StorageProvider { - private final Logger logger = Logger.getLogger("MariaDBStorageProvider"); +public class SQLStorageProvider implements StorageProvider { + private final Logger logger = Logger.getLogger("SQLStorageProvider"); private final Configuration configuration; private Connection connection; private final String schemaName = "nicko"; - public MariaDBStorageProvider(Configuration configuration) { + public SQLStorageProvider(Configuration configuration) { this.configuration = configuration; } @@ -26,12 +26,11 @@ public class MariaDBStorageProvider implements StorageProvider { public boolean init() { try { final MariaDbDataSource dataSource = new MariaDbDataSource(); - final DataSourceConfiguration sqlConfiguration = configuration.getSqlConfiguration(); - dataSource.setUrl("jdbc:mariadb://" + sqlConfiguration.getAddress() + ":" + sqlConfiguration.getPort()); - dataSource.setUser(sqlConfiguration.getUsername()); - dataSource.setPassword(sqlConfiguration.getPassword()); + final DataSourceConfiguration dataSourceConfiguration = configuration.getSqlConfiguration(); + dataSource.setUrl("jdbc:mariadb://" + dataSourceConfiguration.getAddress() + ":" + dataSourceConfiguration.getPort()); + dataSource.setUser(dataSourceConfiguration.getUsername()); + dataSource.setPassword(dataSourceConfiguration.getPassword()); connection = dataSource.getConnection(); - connection.setAutoCommit(true); final boolean initialized = connection != null && !connection.isClosed(); if (!initialized) return false; @@ -40,7 +39,7 @@ public class MariaDBStorageProvider implements StorageProvider { createTable(); return true; } catch (SQLException e) { - logger.severe("Couldn't establish a connection to the MariaDB database: " + e.getMessage()); + logger.severe("Couldn't establish a connection to the MySQL database: " + e.getMessage()); return false; } } @@ -58,24 +57,30 @@ public class MariaDBStorageProvider implements StorageProvider { private void createTable() throws SQLException { final Connection connection = getConnection(); - final String query = "CREATE TABLE IF NOT EXISTS %s.DATA ".replace("%s", schemaName) + - "(uuid varchar(36) NOT NULL," + - "name varchar(16)," + - "skin varchar(16)," + - "locale char(2) 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)," + + "skin varchar(16)," + + "locale char(2) NOT NULL," + + "bungeecord boolean NOT NULL," + + "PRIMARY KEY (UUID))"; + query = query.replace("%s", schemaName); final PreparedStatement statement = connection.prepareStatement(query); statement.executeUpdate(); + statement.close(); } private void createDatabase() throws SQLException { final Connection connection = getConnection(); - final String query = "CREATE DATABASE IF NOT EXISTS %s".replace("%s", schemaName); + + String query = "CREATE DATABASE IF NOT EXISTS %s"; + query = query.replace("%s", schemaName); final PreparedStatement statement = connection.prepareStatement(query); statement.executeUpdate(); + statement.close(); } public Connection getConnection() { diff --git a/src/main/resources/config.yml b/core/src/main/resources/config.yml similarity index 62% rename from src/main/resources/config.yml rename to core/src/main/resources/config.yml index 0ed717d..22f718a 100644 --- a/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -1,23 +1,16 @@ -# Nicko ${version} - Config: +# Nicko ${project.version} - Config: -# Specifies the configuration version, don't change. -version: "1.2.0" +########### +# STORAGE # +########### -# Nicko will copy the English locale as "lang.yml" -# and will use the translations in that file when "Server Custom" -# is selected as the player's locale. +# Indicates wherever the data will be stored +# locally through a .json file or a (My)SQL database. # Accepted values: false (Disabled), true (Enabled) -customLocale: false +local: true +# This configuration section manages SQL. sql: - # Indicates wherever the data will be stored locally - # inside a .json file or in an SQL database. - # Accepted values: false (Disabled), true (Enabled) - enabled: false - # Toggles between the MariaDB and MySQL drivers. - # If you use the MySQL database engine, switch this to off. - # Accepted values: false (Disabled), true (Enabled) - mariadb: true # SQL database's address # Accepted values: valid IP address (e.g. localhost, 127.0.0.1) address: "localhost" @@ -31,13 +24,15 @@ sql: # Accepted values: any string password: "password" +# This configuration section manages Redis (BungeeCord support). +# It is used to transfer data between multiple servers. redis: # Indicates wherever the data will be stored through # Redis to transfer whenever a player switches server. # Accepted values: false (Disabled), true (Enabled) enabled: false # Redis server's address - # Accepted values: valid IP address (e.g.: localhost, 127.0.0.1) + # Accepted values: valid IP address (e.g. localhost, 127.0.0.1) address: "localhost" # Redis server's port # Accepted values: valid integer (e.g. 3306, 25565) @@ -47,4 +42,18 @@ redis: username: "username" # Redis server's password # Accepted values: any string - password: "password" \ No newline at end of file + password: "password" + +########### +# DISPLAY # +########### + +# Nicko's messages prefix. +# Accepted values: any string +prefix: "§6Nicko §8§l| §r" + +# Nicko will copy the English locale as "lang.yml" +# and will use the translations in that file when "Server Custom" +# is selected as the player's locale. +# Accepted values: false (Disabled), true (Enabled) +customLocale: false \ No newline at end of file diff --git a/core/src/main/resources/en.yml b/core/src/main/resources/en.yml new file mode 100644 index 0000000..b35b72c --- /dev/null +++ b/core/src/main/resources/en.yml @@ -0,0 +1,22 @@ +error: + couldnt_get_name_from_mojang: "Failed to get username from Mojang" + couldnt_get_skin_from_cache: "Failed to get skin from cache" + couldnt_get_skin_from_mojang: "Failed to get skin from Mojang" + generic: "Unknown error" + invalid_username: "§cThe specified username is not a valid Minecraft username." + player_offline: "§c{0} §fis offline, please try again." + sql: "SQL Error" + json: "JSON Error" +event: + admin: + cache_clear: "§aSkin cache cleaned." + disguise: + fail: "§cUnable to apply your disguise. §7§o({0})" + success: "§aDisguise applied!" + previous_skin_applied: + fail: "§cFailed to apply your previous disguise back. §7§o({0})" + success: "§aYour previous active disguise has been applied back." + undisguise: + fail: "§cUnable to remove your disguise. It will be set back to default on your next login. Sorry!" + none: "§cYou do not have an active disguise." + success: "§aDisguise removed." \ No newline at end of file diff --git a/core/src/main/resources/fr.yml b/core/src/main/resources/fr.yml new file mode 100644 index 0000000..d4b346d --- /dev/null +++ b/core/src/main/resources/fr.yml @@ -0,0 +1,22 @@ +error: + couldnt_get_name_from_mojang: "Impossible de récupérer le nom d''utilisateur depuis Mojang" + couldnt_get_skin_from_cache: "Impossible de récupérer le skin depuis le cache" + couldnt_get_skin_from_mojang: "Impossible de récupérer le skin depuis Mojang" + generic: "Erreur inconnue" + invalid_username: "§cLe pseudo spécifié n''est pas un pseudo Minecraft valide." + player_offline: "§c{0} §fest hors-ligne, veuillez réessayer." + sql: "Erreur SQL" + json: "Erreur JSON" +event: + admin: + cache_clear: "§aCache des skins nettoyé." + disguise: + fail: "§cImpossible d''appliquer votre déguisement. §7§o({0})" + success: "§aDéguisement appliqué !" + previous_skin_applied: + fail: "§cImpossible d''appliquer votre déguisement précédent. §7§o({0})" + success: "§aVotre précédent déguisement a été réappliqué." + undisguise: + fail: "§cImpossible de retier votre déguisement. Il sera remis par défaut à votre prochaine reconnexion. Désolé !" + none: "§cVous n''avez pas de déguisement." + success: "§aDéguisement retiré." \ No newline at end of file diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml new file mode 100644 index 0000000..093c4b8 --- /dev/null +++ b/core/src/main/resources/plugin.yml @@ -0,0 +1,17 @@ +name: Nicko +main: net.artelnatif.nicko.NickoBukkit +version: 1.0-SNAPSHOT +author: Aro +api-version: 1.13 +softdepend: [ PlaceholderAPI ] +commands: + nicko: + description: "Opens Nicko's GUI." + permission: nicko.admin +permissions: + nicko.*: + default: op + children: + - nicko.use + nicko.use: + default: op \ No newline at end of file diff --git a/core/src/test/java/net/artelnatif/nicko/test/NickoPluginTest.java b/core/src/test/java/net/artelnatif/nicko/test/NickoPluginTest.java new file mode 100644 index 0000000..f71f9c6 --- /dev/null +++ b/core/src/test/java/net/artelnatif/nicko/test/NickoPluginTest.java @@ -0,0 +1,36 @@ +package net.artelnatif.nicko.test; + +import be.seeseemelk.mockbukkit.MockBukkit; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.config.Configuration; +import net.artelnatif.nicko.config.DataSourceConfiguration; +import org.junit.jupiter.api.*; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class NickoPluginTest { + private static NickoBukkit plugin; + + @BeforeAll + public static void setup() { + final Configuration config = new Configuration( + DataSourceConfiguration.SQL_EMPTY, + DataSourceConfiguration.REDIS_EMPTY, + "", + true, + false); + MockBukkit.mock(); + plugin = MockBukkit.load(NickoBukkit.class, config); + } + + @Test + @DisplayName("Plugin Initialization") + public void initializePlugin() { + assertNotNull(plugin); + } + + @AfterAll + public static void shutdown() { + MockBukkit.unmock(); + } +} diff --git a/core/src/test/java/net/artelnatif/nicko/test/config/ConfigurationTest.java b/core/src/test/java/net/artelnatif/nicko/test/config/ConfigurationTest.java new file mode 100644 index 0000000..8c461c8 --- /dev/null +++ b/core/src/test/java/net/artelnatif/nicko/test/config/ConfigurationTest.java @@ -0,0 +1,33 @@ +package net.artelnatif.nicko.test.config; + +import be.seeseemelk.mockbukkit.MockBukkit; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.config.Configuration; +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 static org.junit.jupiter.api.Assertions.assertTrue; + +public class ConfigurationTest { + private static NickoBukkit plugin; + + @BeforeAll + public static void setup() { + MockBukkit.mock(); + plugin = MockBukkit.load(NickoBukkit.class); + } + + @Test + @DisplayName("Read configuration") + public void readConfiguration() { + final Configuration configuration = plugin.getNickoConfig(); + assertTrue(configuration.isLocal()); + } + + @AfterAll + public static void shutdown() { + MockBukkit.unmock(); + } +} 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 new file mode 100644 index 0000000..1b4f7c2 --- /dev/null +++ b/core/src/test/java/net/artelnatif/nicko/test/storage/BrokenSQLTest.java @@ -0,0 +1,62 @@ +package net.artelnatif.nicko.test.storage; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.config.Configuration; +import net.artelnatif.nicko.config.DataSourceConfiguration; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import org.junit.jupiter.api.*; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class BrokenSQLTest { + private static ServerMock server; + private static NickoBukkit plugin; + private static PlayerMock player; + + @BeforeAll + public static void setup() { + final Configuration config = new Configuration( + DataSourceConfiguration.SQL_EMPTY, + DataSourceConfiguration.REDIS_EMPTY, + "", + false, + false); + server = MockBukkit.mock(); + plugin = MockBukkit.load(NickoBukkit.class, config); + player = server.addPlayer(); + } + + @Test + @DisplayName("Fail to create Tables") + public void createSQLTables() { + assertTrue(plugin.getDataStore().getStorage().isError()); + } + + @Test + @DisplayName("Fail to Store Player Via SQL") + public void storePlayer() { + final Optional optionalProfile = plugin.getDataStore().getData(player.getUniqueId()); + assertFalse(optionalProfile.isPresent()); + ActionResult result = plugin.getDataStore().saveData(player); + assertTrue(result.isError()); + } + + @Test + @DisplayName("Fail to Retrieve Player Via SQL") + public void retrievePlayer() { + final Optional storeAction = plugin.getDataStore().getData(player.getUniqueId()); + assertFalse(storeAction.isPresent()); + } + + @AfterAll + public static void shutdown() { + MockBukkit.unmock(); + } +} \ No newline at end of file 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 new file mode 100644 index 0000000..15f3b82 --- /dev/null +++ b/core/src/test/java/net/artelnatif/nicko/test/storage/SQLStorageTest.java @@ -0,0 +1,117 @@ +package net.artelnatif.nicko.test.storage; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.config.DataSourceConfiguration; +import net.artelnatif.nicko.i18n.Locale; +import net.artelnatif.nicko.config.Configuration; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import org.junit.jupiter.api.*; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class SQLStorageTest { + private static ServerMock server; + private static NickoBukkit plugin; + private static PlayerMock player; + + @BeforeAll + public static void setup() { + final Configuration config = new Configuration( + new DataSourceConfiguration("127.0.0.1", 3306, "root", "12345"), + DataSourceConfiguration.REDIS_EMPTY, + "", + false, + false); + server = MockBukkit.mock(); + plugin = MockBukkit.load(NickoBukkit.class, config); + player = server.addPlayer(); + } + + @Test + @DisplayName("Create SQL Tables") + @Order(1) + public void createSQLTables() { + assertFalse(plugin.getDataStore().getStorage().isError()); + } + + @Test + @DisplayName("Store Player Via SQL") + @Order(2) + public void storePlayer() { + final Optional optionalProfile = plugin.getDataStore().getData(player.getUniqueId()); + assertTrue(optionalProfile.isPresent()); + } + + @Test + @DisplayName("Retrieve Player Via SQL") + @Order(3) + public void retrievePlayer() { + final Optional storeAction = plugin.getDataStore().getData(player.getUniqueId()); + assertTrue(storeAction.isPresent()); + } + + @Test + @DisplayName("Update Player Via SQL") + @Order(4) + public void updatePlayer() { + final Optional optionalProfile = plugin.getDataStore().getData(player.getUniqueId()); + assertTrue(optionalProfile.isPresent()); + + final NickoProfile profile = optionalProfile.get(); + Assertions.assertNull(profile.getName()); + Assertions.assertNull(profile.getSkin()); + Assertions.assertEquals(profile.getLocale(), Locale.ENGLISH); + assertTrue(profile.isBungeecordTransfer()); + + profile.setName("Notch"); + profile.setSkin("Notch"); + profile.setLocale(Locale.FRENCH); + profile.setBungeecordTransfer(false); + + final ActionResult result = plugin.getDataStore().saveData(player); + assertFalse(result.isError()); + + final Optional optionalUpdatedProfile = plugin.getDataStore().getData(player.getUniqueId()); + assertTrue(optionalUpdatedProfile.isPresent()); + final NickoProfile updatedProfile = optionalProfile.get(); + Assertions.assertEquals(updatedProfile.getName(), "Notch"); + Assertions.assertEquals(updatedProfile.getSkin(), "Notch"); + Assertions.assertEquals(updatedProfile.getLocale(), Locale.FRENCH); + assertFalse(updatedProfile.isBungeecordTransfer()); + } + + @Test + @DisplayName("Remove Player Disguise Via SQL") + @Order(5) + public void removePlayerDisguise() { + final Optional optionalProfile = plugin.getDataStore().getData(player.getUniqueId()); + assertTrue(optionalProfile.isPresent()); + + final NickoProfile profile = optionalProfile.get(); + + profile.setName(null); + profile.setSkin(null); + + final ActionResult result = plugin.getDataStore().saveData(player); + assertFalse(result.isError()); + + final Optional optionalUpdatedProfile = plugin.getDataStore().getData(player.getUniqueId()); + assertTrue(optionalUpdatedProfile.isPresent()); + final NickoProfile updatedProfile = optionalProfile.get(); + Assertions.assertNull(updatedProfile.getName()); + Assertions.assertNull(updatedProfile.getSkin()); + } + + @AfterAll + public static void shutdown() { + MockBukkit.unmock(); + } +} \ No newline at end of file diff --git a/core/src/test/java/net/artelnatif/nicko/test/storage/cache/CacheStorageTest.java b/core/src/test/java/net/artelnatif/nicko/test/storage/cache/CacheStorageTest.java new file mode 100644 index 0000000..1e039af --- /dev/null +++ b/core/src/test/java/net/artelnatif/nicko/test/storage/cache/CacheStorageTest.java @@ -0,0 +1,49 @@ +package net.artelnatif.nicko.test.storage.cache; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.config.Configuration; +import net.artelnatif.nicko.config.DataSourceConfiguration; +import net.artelnatif.nicko.disguise.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 java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class CacheStorageTest { + private static ServerMock server; + private static NickoBukkit plugin; + private static PlayerMock player; + + @BeforeAll + public static void setup() { + final Configuration config = new Configuration( + new DataSourceConfiguration("127.0.0.1", 3306, "root", "12345"), + DataSourceConfiguration.REDIS_EMPTY, + "", + false, + false); + server = MockBukkit.mock(); + plugin = MockBukkit.load(NickoBukkit.class, config); + player = server.addPlayer(); + } + + @Test + @DisplayName("Cache Player Data") + public void cachePlayerData() { + final Optional optionalProfile = plugin.getDataStore().getData(player.getUniqueId()); + assertTrue(optionalProfile.isPresent()); + //assertTrue(plugin.getDataStore().isCached(player.getUniqueId())); + } + + @AfterAll + public static void shutdown() { + MockBukkit.unmock(); + } +} diff --git a/dist/pom.xml b/dist/pom.xml new file mode 100644 index 0000000..b1ecb77 --- /dev/null +++ b/dist/pom.xml @@ -0,0 +1,115 @@ + + + 4.0.0 + + + net.artelnatif + nicko-parent + 1.0-SNAPSHOT + + + dist + 1.0-SNAPSHOT + + + ../target + nicko-${project.version} + + + org.apache.maven.plugins + maven-shade-plugin + 3.3.1-SNAPSHOT + + + package + + shade + + + + + net.artelnatif:* + + + + + + + + + + + + net.artelnatif + core + ${project.parent.version} + + + net.artelnatif + v1_13_R1 + ${project.parent.version} + + + net.artelnatif + v1_13_R2 + ${project.parent.version} + + + net.artelnatif + v1_14_R1 + ${project.parent.version} + + + net.artelnatif + v1_15_R1 + ${project.parent.version} + + + net.artelnatif + v1_16_R1 + ${project.parent.version} + + + net.artelnatif + v1_16_R2 + ${project.parent.version} + + + net.artelnatif + v1_16_R3 + ${project.parent.version} + + + net.artelnatif + v1_17_R1 + ${project.parent.version} + + + net.artelnatif + v1_18_R1 + ${project.parent.version} + + + net.artelnatif + v1_18_R2 + ${project.parent.version} + + + net.artelnatif + v1_19_R1 + ${project.parent.version} + + + net.artelnatif + v1_19_R2 + ${project.parent.version} + + + net.artelnatif + v1_19_R3 + ${project.parent.version} + + + \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index a4b76b9..0000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index ff23a68..0000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew deleted file mode 100755 index f3b75f3..0000000 --- a/gradlew +++ /dev/null @@ -1,251 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 9b42019..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,94 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/img/LOGO.png b/img/LOGO.png new file mode 100644 index 0000000..4719376 Binary files /dev/null and b/img/LOGO.png differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..e9f92dc --- /dev/null +++ b/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + net.artelnatif + nicko-parent + 1.0-SNAPSHOT + Nicko + pom + + + core + dist + v1_13_R1 + v1_13_R2 + v1_14_R1 + v1_15_R1 + v1_16_R1 + v1_16_R2 + v1_16_R3 + v1_17_R1 + v1_18_R1 + v1_18_R2 + v1_19_R1 + v1_19_R2 + v1_19_R3 + + + + 11 + 11 + + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts deleted file mode 100644 index 4549581..0000000 --- a/settings.gradle.kts +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = "nicko" - diff --git a/src/main/java/xyz/ineanto/nicko/Nicko.java b/src/main/java/xyz/ineanto/nicko/Nicko.java deleted file mode 100644 index 8b96749..0000000 --- a/src/main/java/xyz/ineanto/nicko/Nicko.java +++ /dev/null @@ -1,176 +0,0 @@ -package xyz.ineanto.nicko; - -import com.comphenix.protocol.utility.MinecraftVersion; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.plugin.java.JavaPlugin; -import xyz.ineanto.nicko.appearance.random.RandomNameFetcher; -import xyz.ineanto.nicko.command.NickoCommand; -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.language.CustomLanguage; -import xyz.ineanto.nicko.language.Language; -import xyz.ineanto.nicko.migration.ConfigurationMigrator; -import xyz.ineanto.nicko.migration.CustomLocaleMigrator; -import xyz.ineanto.nicko.mojang.MojangAPI; -import xyz.ineanto.nicko.placeholder.NickoExpansion; -import xyz.ineanto.nicko.storage.PlayerDataStore; -import xyz.ineanto.nicko.storage.json.JSONStorage; -import xyz.ineanto.nicko.storage.map.MapCache; -import xyz.ineanto.nicko.storage.name.PlayerNameStore; -import xyz.xenondevs.invui.InvUI; -import xyz.xenondevs.invui.gui.structure.Structure; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SimpleItem; - -import java.io.IOException; - -public class Nicko extends JavaPlugin { - private static Nicko plugin; - - private MojangAPI mojangAPI; - private PlayerDataStore dataStore; - private ConfigurationManager configurationManager; - private Configuration configuration; - private CustomLanguage customLanguage; - private PlayerNameStore nameStore; - private RandomNameFetcher nameFetcher; - - @Override - public void onEnable() { - plugin = this; - - configurationManager = new ConfigurationManager(getDataFolder()); - configurationManager.saveDefaultConfig(); - - dataStore = new PlayerDataStore(mojangAPI, getNickoConfig()); - - if (!MinecraftVersion.v1_21_5.atOrAbove()) { - getLogger().severe("This version (" + MinecraftVersion.getCurrentVersion().getVersion() + ") is not supported by Nicko!"); - getLogger().severe("As of version 1.2.0, Nicko only supports the latest Minecraft version. (Currently 1.21.5)"); - dataStore.getStorage().setError(true); - 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."); - final JSONStorage storage = new JSONStorage(); - storage.getProvider().init(); - dataStore.setStorage(storage); - } - - getLogger().info("Loading cache..."); - if (!dataStore.getCache().getProvider().init()) { - getLogger().severe("Couldn't connect to distant cache, falling back on local cache."); - final MapCache cache = new MapCache(); - cache.getProvider().init(); - dataStore.setCache(cache); - } - - nameStore = new PlayerNameStore(); - mojangAPI = new MojangAPI(); - nameFetcher = new RandomNameFetcher(this); - - new ConfigurationMigrator(this).migrate(); - InvUI.getInstance().setPlugin(this); - - if (configuration.isCustomLocale()) { - try { - CustomLanguage.dumpIntoFile(Language.ENGLISH); - customLanguage = new CustomLanguage(); - new CustomLocaleMigrator(this, customLanguage).migrate(); - getLogger().info("Successfully loaded the custom locale."); - } catch (IOException e) { - getLogger().severe("Failed to load the custom locale!"); - } - } - - registerCommand("nicko", new NickoCommand()); - - Structure.addGlobalIngredient('#', new SimpleItem(new ItemBuilder(Material.AIR))); - Structure.addGlobalIngredient('%', new SimpleItem(new ItemBuilder(Material.BLACK_STAINED_GLASS_PANE).setDisplayName(" "))); - - if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { - getLogger().info("Enabling PlaceHolderAPI support..."); - new NickoExpansion(this).register(); - } - - getServer().getPluginManager().registerEvents(new PlayerJoinListener(), this); - getServer().getPluginManager().registerEvents(new PlayerQuitListener(), this); - - getLogger().info("Nicko has been enabled."); - } - - @Override - public void onDisable() { - if (!getDataStore().getStorage().isError()) { - Bukkit.getOnlinePlayers().forEach(player -> dataStore.saveData(player)); - if (!dataStore.getStorage().getProvider().close()) { - getLogger().severe("Failed to close persistence!"); - } else { - getLogger().info("Persistence closed."); - } - } - - nameStore.clearStoredNames(); - getLogger().info("Nicko (Bukkit) has been disabled."); - } - - public static Nicko getInstance() { - return plugin; - } - - public Configuration getNickoConfig() { - try { - if (configuration == null) { - configuration = configurationManager.load(); - getLogger().info("Configuration file loaded."); - } - return configuration; - } catch (IOException e) { - getLogger().severe("Failed to load the configuration file!"); - getLogger().severe("It may be have been generated with an older version of Nicko."); - getLogger().severe("Delete the configuration and restart the server please :)"); - getLogger().severe("(" + e.getMessage() + ")"); - return null; - } - } - - public RandomNameFetcher getNameFetcher() { - return nameFetcher; - } - - public PlayerDataStore getDataStore() { - return dataStore; - } - - public ConfigurationManager getConfigurationManager() { - return configurationManager; - } - - public PlayerNameStore getNameStore() { - return nameStore; - } - - public MojangAPI getMojangAPI() { - return mojangAPI; - } - - public CustomLanguage getCustomLocale() { - return customLanguage; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/appearance/ActionResult.java b/src/main/java/xyz/ineanto/nicko/appearance/ActionResult.java deleted file mode 100644 index b7cd505..0000000 --- a/src/main/java/xyz/ineanto/nicko/appearance/ActionResult.java +++ /dev/null @@ -1,35 +0,0 @@ -package xyz.ineanto.nicko.appearance; - -public class ActionResult { - private final String errorKey; - private boolean error = false; - - public static ActionResult ok() { - return new ActionResult(); - } - - public static ActionResult error() { - return new ActionResult(null); - } - - public static ActionResult error(String errorMessage) { - return new ActionResult(errorMessage); - } - - private ActionResult() { - this.errorKey = null; - } - - private ActionResult(String errorMessage) { - this.errorKey = errorMessage; - this.error = true; - } - - public boolean isError() { - return error; - } - - public String getErrorKey() { - return errorKey; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/appearance/Appearance.java b/src/main/java/xyz/ineanto/nicko/appearance/Appearance.java deleted file mode 100644 index 9c40462..0000000 --- a/src/main/java/xyz/ineanto/nicko/appearance/Appearance.java +++ /dev/null @@ -1,8 +0,0 @@ -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/appearance/AppearanceManager.java b/src/main/java/xyz/ineanto/nicko/appearance/AppearanceManager.java deleted file mode 100644 index 7de7d31..0000000 --- a/src/main/java/xyz/ineanto/nicko/appearance/AppearanceManager.java +++ /dev/null @@ -1,70 +0,0 @@ -package xyz.ineanto.nicko.appearance; - -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.event.custom.PlayerDisguiseEvent; -import xyz.ineanto.nicko.event.custom.PlayerResetDisguiseEvent; -import xyz.ineanto.nicko.packet.PacketSender; -import xyz.ineanto.nicko.packet.PaperPacketSender; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.ineanto.nicko.storage.PlayerDataStore; -import xyz.ineanto.nicko.storage.name.PlayerNameStore; - -import java.util.Optional; - -public class AppearanceManager { - private final Nicko instance = Nicko.getInstance(); - private final PlayerDataStore dataStore = instance.getDataStore(); - private final PlayerNameStore nameStore = instance.getNameStore(); - private final PacketSender packetSender; - - private final Player player; - - public AppearanceManager(Player player) { - this.player = player; - this.packetSender = new PaperPacketSender(player, getNickoProfile()); - } - - public ActionResult reset() { - final NickoProfile profile = getNickoProfile(); - - // Call the event. - final PlayerResetDisguiseEvent event = new PlayerResetDisguiseEvent(player); - Bukkit.getPluginManager().callEvent(event); - - profile.setName(null); - profile.setSkin(null); - dataStore.getCache().cache(player.getUniqueId(), profile); - - return ActionResult.error(); - } - - public ActionResult update(boolean skinChange) { - final NickoProfile profile = getNickoProfile(); - final String displayName = profile.getName() == null ? player.getName() : profile.getName(); - - final ActionResult result = packetSender.updatePlayerProfile(displayName); - - if (skinChange) { - final ActionResult propertiesUpdateResult = packetSender.updatePlayerProfileProperties(); - - if (propertiesUpdateResult.isError()) { - return reset(); - } - } - - // Call the event. - final PlayerDisguiseEvent event = new PlayerDisguiseEvent(player, profile.getSkin(), profile.getName()); - Bukkit.getPluginManager().callEvent(event); - - packetSender.sendEntityMetadataUpdate(); - packetSender.sendTabListUpdate(displayName); - return result; - } - - private NickoProfile getNickoProfile() { - final Optional optionalProfile = dataStore.getData(player.getUniqueId()); - return optionalProfile.orElse(NickoProfile.EMPTY_PROFILE.clone()); - } -} \ No newline at end of file diff --git a/src/main/java/xyz/ineanto/nicko/appearance/random/RandomNameFetcher.java b/src/main/java/xyz/ineanto/nicko/appearance/random/RandomNameFetcher.java deleted file mode 100644 index 3ff9574..0000000 --- a/src/main/java/xyz/ineanto/nicko/appearance/random/RandomNameFetcher.java +++ /dev/null @@ -1,36 +0,0 @@ -package xyz.ineanto.nicko.appearance.random; - -import xyz.ineanto.nicko.Nicko; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Random; - -public class RandomNameFetcher { - private final Nicko instance; - - public RandomNameFetcher(Nicko instance) { - this.instance = instance; - } - - public String getRandomUsername() { - final InputStream resource = instance.getResource("names.txt"); - final List> records = new ArrayList<>(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource))) { - String line; - while ((line = reader.readLine()) != null) { - final String[] values = line.split("\n"); - records.add(Arrays.asList(values)); - } - return records.get(new Random().nextInt(records.size() - 1)).getFirst(); - } catch (IOException e) { - instance.getLogger().severe("Unable to fetch random names."); - return "Ineanto"; - } - } -} diff --git a/src/main/java/xyz/ineanto/nicko/command/NickoCommand.java b/src/main/java/xyz/ineanto/nicko/command/NickoCommand.java deleted file mode 100644 index 7b18427..0000000 --- a/src/main/java/xyz/ineanto/nicko/command/NickoCommand.java +++ /dev/null @@ -1,69 +0,0 @@ -package xyz.ineanto.nicko.command; - -import io.papermc.paper.command.brigadier.BasicCommand; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.jspecify.annotations.Nullable; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.config.Configuration; -import xyz.ineanto.nicko.gui.HomeGUI; -import xyz.ineanto.nicko.language.Language; -import xyz.ineanto.nicko.language.PlayerLanguage; - -import java.util.Collection; -import java.util.List; - -@SuppressWarnings("UnstableApiUsage") -public class NickoCommand implements BasicCommand { - @Override - public void execute(CommandSourceStack stack, String[] args) { - final Entity executor = stack.getExecutor(); - final Player player = (Player) executor; - final PlayerLanguage playerLanguage = new PlayerLanguage(player); - - if (player == null) { return; } - - if (args.length >= 1 && args[0].equalsIgnoreCase("about")) { - final Component firstAboutMessage = MiniMessage.miniMessage().deserialize( - " (© 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 ", - Placeholder.component("prefix", playerLanguage.getPrefixComponent()), - Placeholder.unparsed("configversion", Configuration.VERSION.toString()), - Placeholder.unparsed("i18nversion", Language.VERSION.toString()) - - ); - - player.sendMessage(firstAboutMessage); - player.sendMessage(secondAboutMessage); - - return; - } - - new HomeGUI(player).open(); - } - - @Override - public boolean canUse(CommandSender sender) { - return sender instanceof Player && sender.isOp() || sender.hasPermission(permission()); - } - - @Override - public Collection suggest(CommandSourceStack commandSourceStack, String[] args) { - return List.of("about"); - } - - @Override - public @Nullable String permission() { - return "nicko.use"; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/config/Configuration.java b/src/main/java/xyz/ineanto/nicko/config/Configuration.java deleted file mode 100644 index d112559..0000000 --- a/src/main/java/xyz/ineanto/nicko/config/Configuration.java +++ /dev/null @@ -1,54 +0,0 @@ -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, 2, 0); - public static final Configuration DEFAULT = new Configuration(VERSION.toString(), - DefaultDataSources.SQL_EMPTY, - DefaultDataSources.REDIS_EMPTY, - 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("customLocale") - private final Boolean customLocale; - - public Configuration(@JsonProperty("version") String version, - @JsonProperty("sql") SQLDataSourceConfiguration sqlConfiguration, - @JsonProperty("redis") DataSourceConfiguration redisConfiguration, - @JsonProperty("customLocale") Boolean customLocale) { - this.version = version; - this.versionObject = Version.fromString(version); - this.sqlConfiguration = sqlConfiguration; - this.redisConfiguration = redisConfiguration; - this.customLocale = customLocale; - } - - public String getVersion() { - return version; - } - - public Version getVersionObject() { - return versionObject; - } - - public SQLDataSourceConfiguration getSqlConfiguration() { - return sqlConfiguration; - } - - public DataSourceConfiguration getRedisConfiguration() { - return redisConfiguration; - } - - public Boolean isCustomLocale() { - return customLocale; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/config/DataSourceConfiguration.java b/src/main/java/xyz/ineanto/nicko/config/DataSourceConfiguration.java deleted file mode 100644 index 6d00ceb..0000000 --- a/src/main/java/xyz/ineanto/nicko/config/DataSourceConfiguration.java +++ /dev/null @@ -1,50 +0,0 @@ -package xyz.ineanto.nicko.config; - -public class DataSourceConfiguration { - private final boolean enabled; - private final String address; - private final Integer port; - private final String username; - private final String password; - - public DataSourceConfiguration(boolean enabled, String address, Integer port, String username, String password) { - this.enabled = enabled; - this.address = address; - this.port = port; - this.username = username; - this.password = password; - } - - public DataSourceConfiguration() { this(false, "", 0, "", ""); } - - public boolean isEnabled() { - return enabled; - } - - public String getAddress() { - return address; - } - - public Integer getPort() { - return port; - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } - - @Override - public String toString() { - return "DataSourceConfiguration{" + - "enabled=" + enabled + - ", address='" + address + '\'' + - ", port=" + port + - ", username='" + username + '\'' + - ", password='" + password + '\'' + - '}'; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/config/DefaultDataSources.java b/src/main/java/xyz/ineanto/nicko/config/DefaultDataSources.java deleted file mode 100644 index 715bda2..0000000 --- a/src/main/java/xyz/ineanto/nicko/config/DefaultDataSources.java +++ /dev/null @@ -1,8 +0,0 @@ -package xyz.ineanto.nicko.config; - -public class DefaultDataSources { - public static final DataSourceConfiguration REDIS_EMPTY = new DataSourceConfiguration(false, "127.0.0.1", 6379, "", ""); - public static final SQLDataSourceConfiguration MARIADB_EMPTY = new SQLDataSourceConfiguration(false, "127.0.0.1", 3306, "root", "", true); - public static final SQLDataSourceConfiguration SQL_EMPTY = new SQLDataSourceConfiguration(false, "127.0.0.1", 3306, "root", "", false); - -} diff --git a/src/main/java/xyz/ineanto/nicko/config/SQLDataSourceConfiguration.java b/src/main/java/xyz/ineanto/nicko/config/SQLDataSourceConfiguration.java deleted file mode 100644 index 583981d..0000000 --- a/src/main/java/xyz/ineanto/nicko/config/SQLDataSourceConfiguration.java +++ /dev/null @@ -1,16 +0,0 @@ -package xyz.ineanto.nicko.config; - -public class SQLDataSourceConfiguration extends DataSourceConfiguration { - private final boolean mariadb; - - public SQLDataSourceConfiguration() { this(false, "", 0, "", "", true); } - - public SQLDataSourceConfiguration(boolean enabled, String address, Integer port, String username, String password, boolean mariadb) { - super(enabled, address, port, username, password); - this.mariadb = mariadb; - } - - public boolean isMariadb() { - return mariadb; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/event/PlayerJoinListener.java b/src/main/java/xyz/ineanto/nicko/event/PlayerJoinListener.java deleted file mode 100644 index cdb2cd4..0000000 --- a/src/main/java/xyz/ineanto/nicko/event/PlayerJoinListener.java +++ /dev/null @@ -1,73 +0,0 @@ -package xyz.ineanto.nicko.event; - -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.appearance.ActionResult; -import xyz.ineanto.nicko.appearance.AppearanceManager; -import xyz.ineanto.nicko.gui.PlayerCheckGUI; -import xyz.ineanto.nicko.gui.PlayerCheckGUIData; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.ineanto.nicko.storage.PlayerDataStore; -import xyz.ineanto.nicko.storage.name.PlayerNameStore; -import xyz.xenondevs.invui.window.Window; -import xyz.xenondevs.invui.window.WindowManager; - -import java.util.ArrayList; -import java.util.Optional; -import java.util.UUID; - -public class PlayerJoinListener implements Listener { - @EventHandler(priority = EventPriority.LOWEST) - public void onPlayerJoin(PlayerJoinEvent event) { - final Player player = event.getPlayer(); - final Nicko instance = Nicko.getInstance(); - final PlayerLanguage playerLanguage = new PlayerLanguage(player); - final PlayerNameStore nameStore = instance.getNameStore(); - final PlayerDataStore dataStore = instance.getDataStore(); - nameStore.storeName(player); - - final Optional optionalProfile = dataStore.getData(player.getUniqueId()); - optionalProfile.ifPresentOrElse(profile -> { - // Random Skin on connection feature - if (profile.isRandomSkin()) { - final String name = instance.getNameFetcher().getRandomUsername(); - final String skin = instance.getNameFetcher().getRandomUsername(); - profile.setName(name); - profile.setSkin(skin); - dataStore.updateCache(player.getUniqueId(), profile); - } - - if (profile.hasData()) { - final AppearanceManager appearanceManager = new AppearanceManager(player); - final boolean needsASkinChange = profile.getSkin() != null && !profile.getSkin().equals(player.getName()); - final ActionResult actionResult = appearanceManager.update(needsASkinChange); - if (!actionResult.isError()) { - player.sendMessage(playerLanguage.translateWithWhoosh(LanguageKey.Event.Appearance.Restore.OK)); - } else { - player.sendMessage( - playerLanguage.translateWithOops(LanguageKey.Event.Appearance.Restore.ERROR, - playerLanguage.translate(actionResult.getErrorKey(), false) - )); - } - } - }, () -> instance.getLogger().warning("Failed to load data for " + player.getName())); - - @SuppressWarnings("unchecked") final ArrayList viewers = (ArrayList) PlayerCheckGUIData.VIEWERS.clone(); - viewers.forEach(uuid -> { - final Player windowWatcher = Bukkit.getPlayer(uuid); - final Window openWindow = WindowManager.getInstance().getOpenWindow(windowWatcher); - if (openWindow != null) { - final PlayerCheckGUI gui = new PlayerCheckGUI(windowWatcher, Bukkit.getOnlinePlayers()); - openWindow.close(); - gui.open(); - } - }); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/event/PlayerQuitListener.java b/src/main/java/xyz/ineanto/nicko/event/PlayerQuitListener.java deleted file mode 100644 index 020790b..0000000 --- a/src/main/java/xyz/ineanto/nicko/event/PlayerQuitListener.java +++ /dev/null @@ -1,45 +0,0 @@ -package xyz.ineanto.nicko.event; - -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerQuitEvent; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.appearance.ActionResult; -import xyz.ineanto.nicko.gui.PlayerCheckGUI; -import xyz.ineanto.nicko.gui.PlayerCheckGUIData; -import xyz.xenondevs.invui.window.Window; -import xyz.xenondevs.invui.window.WindowManager; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - -public class PlayerQuitListener implements Listener { - @EventHandler(priority = EventPriority.LOWEST) - public void onPlayerQuit(PlayerQuitEvent event) { - final Player player = event.getPlayer(); - final ActionResult result = Nicko.getInstance().getDataStore().saveData(player); - if (result.isError()) { - Nicko.getInstance().getLogger().warning("Failed to save data for " + player.getName()); - } - - // This is a dirty way to do it but could be worse tbh - @SuppressWarnings("unchecked") final ArrayList viewers = (ArrayList) PlayerCheckGUIData.VIEWERS.clone(); - viewers.forEach(uuid -> { - final Player windowWatcher = Bukkit.getPlayer(uuid); - final Window openWindow = WindowManager.getInstance().getOpenWindow(windowWatcher); - if (openWindow != null) { - final List playersWithoutOffline = Bukkit.getOnlinePlayers() - .stream() - .filter(online -> online.getUniqueId() != player.getUniqueId()).collect(Collectors.toList()); - final PlayerCheckGUI gui = new PlayerCheckGUI(windowWatcher, playersWithoutOffline); - openWindow.close(); - gui.open(); - } - }); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/event/custom/PlayerDisguiseEvent.java b/src/main/java/xyz/ineanto/nicko/event/custom/PlayerDisguiseEvent.java deleted file mode 100644 index e7c3776..0000000 --- a/src/main/java/xyz/ineanto/nicko/event/custom/PlayerDisguiseEvent.java +++ /dev/null @@ -1,47 +0,0 @@ -package xyz.ineanto.nicko.event.custom; - -import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; - -public class PlayerDisguiseEvent extends Event implements Cancellable { - private static final HandlerList HANDLERS_LIST = new HandlerList(); - private boolean isCancelled; - private final Player player; - private final String skin, name; - - public PlayerDisguiseEvent(Player player, String skin, String name) { - this.player = player; - this.skin = skin; - this.name = name; - } - - @Override - public boolean isCancelled() { - return isCancelled; - } - - @Override - public void setCancelled(boolean isCancelled) { - this.isCancelled = isCancelled; - } - - @Override - public @NotNull HandlerList getHandlers() { - return HANDLERS_LIST; - } - - public Player getPlayer() { - return player; - } - - public String getSkin() { - return skin; - } - - public String getName() { - return name; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/event/custom/PlayerResetDisguiseEvent.java b/src/main/java/xyz/ineanto/nicko/event/custom/PlayerResetDisguiseEvent.java deleted file mode 100644 index cc9f1ec..0000000 --- a/src/main/java/xyz/ineanto/nicko/event/custom/PlayerResetDisguiseEvent.java +++ /dev/null @@ -1,36 +0,0 @@ -package xyz.ineanto.nicko.event.custom; - -import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; - -public class PlayerResetDisguiseEvent extends Event implements Cancellable { - private static final HandlerList HANDLERS_LIST = new HandlerList(); - private boolean isCancelled; - private final Player player; - - public PlayerResetDisguiseEvent(Player player) { - this.player = player; - } - - @Override - public boolean isCancelled() { - return isCancelled; - } - - @Override - public void setCancelled(boolean isCancelled) { - this.isCancelled = isCancelled; - } - - @Override - public @NotNull HandlerList getHandlers() { - return HANDLERS_LIST; - } - - public Player getPlayer() { - return player; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/AdminGUI.java b/src/main/java/xyz/ineanto/nicko/gui/AdminGUI.java deleted file mode 100644 index 4118f49..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/AdminGUI.java +++ /dev/null @@ -1,49 +0,0 @@ -package xyz.ineanto.nicko.gui; - -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.gui.items.admin.ManageCacheItem; -import xyz.ineanto.nicko.gui.items.admin.ManagePlayerItem; -import xyz.ineanto.nicko.gui.items.common.GoBackItem; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.window.Window; - -public class AdminGUI { - private final Player player; - private final Gui gui; - private final String title; - - public AdminGUI(Player player) { - final PlayerLanguage playerLanguage = new PlayerLanguage(player); - this.title = playerLanguage.translate(LanguageKey.GUI.Titles.ADMIN, false); - - final HomeGUI parent = new HomeGUI(player); - final GoBackItem backItem = new GoBackItem(player); - final ManagePlayerItem managePlayerItem = new ManagePlayerItem(playerLanguage, player); - - this.gui = Gui.normal() - .setStructure( - "# # # # # # # # #", - "# # # S # C # # #", - "B # # # # # # # #" - ) - .addIngredient('S', new ManageCacheItem(playerLanguage)) - .addIngredient('C', managePlayerItem.get()) - .addIngredient('B', backItem.get(parent.getGUI(), parent.getTitle())) - .build(); - this.player = player; - } - - public Gui getGUI() { - return gui; - } - - public String getTitle() { - return title; - } - - public void open() { - Window.single().setGui(gui).setTitle(title).open(player); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/CacheManagementGUI.java b/src/main/java/xyz/ineanto/nicko/gui/CacheManagementGUI.java deleted file mode 100644 index a44d47f..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/CacheManagementGUI.java +++ /dev/null @@ -1,54 +0,0 @@ -package xyz.ineanto.nicko.gui; - -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.gui.items.admin.cache.CacheStatisticsItem; -import xyz.ineanto.nicko.gui.items.admin.cache.InvalidateCacheItem; -import xyz.ineanto.nicko.gui.items.admin.cache.InvalidateSkinItem; -import xyz.ineanto.nicko.gui.items.common.GoBackItem; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.window.Window; - -public class CacheManagementGUI { - private final Player player; - private final Gui gui; - private final String title; - - public CacheManagementGUI(Player player) { - final PlayerLanguage playerLanguage = new PlayerLanguage(player); - this.title = playerLanguage.translate(LanguageKey.GUI.Titles.CACHE, false); - - final AdminGUI parent = new AdminGUI(player); - final GoBackItem backItem = new GoBackItem(player); - - final CacheStatisticsItem cacheStatisticsItem = new CacheStatisticsItem(player); - final InvalidateCacheItem invalidateCacheItem = new InvalidateCacheItem(player); - final InvalidateSkinItem invalidateSkinItem = new InvalidateSkinItem(player); - - this.gui = Gui.normal() - .setStructure( - "# # # # # # # # #", - "# # # S C E # # #", - "B # # # # # # # #" - ) - .addIngredient('B', backItem.get(parent.getGUI(), parent.getTitle())) - .addIngredient('S', cacheStatisticsItem.get()) - .addIngredient('C', invalidateCacheItem.get()) - .addIngredient('E', invalidateSkinItem.get()) - .build(); - this.player = player; - } - - public Gui getGUI() { - return gui; - } - - public String getTitle() { - return title; - } - - public void open() { - Window.single().setGui(gui).setTitle(title).open(player); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/ChoiceGUI.java b/src/main/java/xyz/ineanto/nicko/gui/ChoiceGUI.java deleted file mode 100644 index 44c0527..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/ChoiceGUI.java +++ /dev/null @@ -1,42 +0,0 @@ -package xyz.ineanto.nicko.gui; - -import org.bukkit.Material; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.gui.items.common.choice.CancelItem; -import xyz.ineanto.nicko.gui.items.common.choice.ChoiceCallback; -import xyz.ineanto.nicko.gui.items.common.choice.ConfirmItem; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.Window; - -public class ChoiceGUI { - private final Player player; - private final Gui gui; - private final String title; - - public ChoiceGUI(Player player, ChoiceCallback callback) { - final PlayerLanguage playerLanguage = new PlayerLanguage(player); - final ConfirmItem confirmItem = new ConfirmItem(player, callback); - final CancelItem cancelItem = new CancelItem(player, callback); - - this.title = playerLanguage.translate(LanguageKey.GUI.Titles.CONFIRM, false); - this.gui = Gui.normal() - .setStructure( - "@ @ @ @ % & & & &", - "@ @ @ @ I & & & &", - "@ @ @ @ % & & & &" - ) - .addIngredient('@', confirmItem.get()) - .addIngredient('&', cancelItem.get()) - .addIngredient('I', new SimpleItem(playerLanguage.translateItem(new ItemBuilder(Material.PAPER), LanguageKey.GUI.Choice.CHOOSE))) - .build(); - this.player = player; - } - - public void open() { - Window.single().setGui(gui).setTitle(title).open(player); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/FavoritesGUI.java b/src/main/java/xyz/ineanto/nicko/gui/FavoritesGUI.java deleted file mode 100644 index 6ca0e11..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/FavoritesGUI.java +++ /dev/null @@ -1,84 +0,0 @@ -package xyz.ineanto.nicko.gui; - -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.appearance.Appearance; -import xyz.ineanto.nicko.gui.items.common.GoBackItem; -import xyz.ineanto.nicko.gui.items.common.ScrollDownItem; -import xyz.ineanto.nicko.gui.items.common.ScrollUpItem; -import xyz.ineanto.nicko.gui.items.favorites.FavoriteAddItem; -import xyz.ineanto.nicko.gui.items.favorites.FavoriteAppearanceEntryItem; -import xyz.ineanto.nicko.gui.items.favorites.FavoriteRemoveItem; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.ScrollGui; -import xyz.xenondevs.invui.gui.structure.Markers; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.window.Window; - -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -public class FavoritesGUI { - private final Player player; - private final Gui gui; - private final String title; - - public FavoritesGUI(Player player) { - final PlayerLanguage playerLanguage = new PlayerLanguage(player); - this.title = playerLanguage.translate(LanguageKey.GUI.Titles.FAVORITES, false); - - final HomeGUI parent = new HomeGUI(player); - final GoBackItem backItem = new GoBackItem(player); - final ScrollUpItem scrollUpItem = new ScrollUpItem(playerLanguage); - final ScrollDownItem scrollDownItem = new ScrollDownItem(playerLanguage); - - final FavoriteAddItem favoriteAddItem = new FavoriteAddItem(player); - final FavoriteRemoveItem favoriteRemoveItem = new FavoriteRemoveItem(playerLanguage); - - final NickoProfile profile = Nicko.getInstance().getDataStore().getData(player.getUniqueId()).orElse(NickoProfile.EMPTY_PROFILE); - final List favorites = profile.getFavorites(); - List items; - - if (favorites == null || favorites.isEmpty()) { - items = Collections.emptyList(); - } else { - items = favorites.stream() - .map((appearance) -> new FavoriteAppearanceEntryItem(player, appearance).get()) - .collect(Collectors.toList()); - } - - gui = ScrollGui.items(guiItemBuilder -> { - guiItemBuilder.setStructure( - "x x x x x x x x U", - "x x x x x x x x #", - "x x x x x x x x #", - "x x x x x x x x #", - "x x x x x x x x D", - "% % % A B R % % %"); - guiItemBuilder.addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL); - guiItemBuilder.addIngredient('U', scrollUpItem); - guiItemBuilder.addIngredient('D', scrollDownItem); - guiItemBuilder.addIngredient('B', backItem.get(parent.getGUI(), parent.getTitle())); - guiItemBuilder.addIngredient('A', favoriteAddItem.get()); - guiItemBuilder.addIngredient('R', favoriteRemoveItem.get()); - guiItemBuilder.setContent(items); - }); - this.player = player; - } - - public Gui getGUI() { - return gui; - } - - public String getTitle() { - return title; - } - - public void open() { - Window.single().setGui(gui).setTitle(title).open(player); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/HomeGUI.java b/src/main/java/xyz/ineanto/nicko/gui/HomeGUI.java deleted file mode 100644 index 90b04dc..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/HomeGUI.java +++ /dev/null @@ -1,67 +0,0 @@ -package xyz.ineanto.nicko.gui; - -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.gui.items.appearance.ChangeBothItem; -import xyz.ineanto.nicko.gui.items.appearance.ChangeNameItem; -import xyz.ineanto.nicko.gui.items.appearance.ChangeSkinItem; -import xyz.ineanto.nicko.gui.items.home.*; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.window.Window; - -public class HomeGUI { - private final Player player; - private final Gui gui; - private final String title; - - public HomeGUI(Player player) { - final String[] dynamicStructure = new String[]{ - "# # # # D # # # #", - "A # # N B S # # #", - "E P # # F # # # R"}; - - if (!player.isOp() || !player.hasPermission("nicko.admin")) { - dynamicStructure[2] = dynamicStructure[2].replace("A", "#"); - } - - final PlayerLanguage playerLanguage = new PlayerLanguage(player); - this.title = playerLanguage.translate(LanguageKey.GUI.Titles.HOME, false); - - final ExitItem exitItem = new ExitItem(player); - final ResetItem resetItem = new ResetItem(player); - final ChangeNameItem changeNameItem = new ChangeNameItem(player); - final ChangeBothItem changeBothItem = new ChangeBothItem(player); - final ChangeSkinItem changeSkinItem = new ChangeSkinItem(player); - final SettingsAccessItem settingsAccessItem = new SettingsAccessItem(player); - final AdminAccessItem adminAccessItem = new AdminAccessItem(player); - final RandomSkinItem randomSkinItem = new RandomSkinItem(player); - final FavoritesItem favoritesItem = new FavoritesItem(player); - - this.gui = Gui.normal() - .setStructure(dynamicStructure) - .addIngredient('E', exitItem.get()) - .addIngredient('R', resetItem.get()) - .addIngredient('N', changeNameItem.get()) - .addIngredient('B', changeBothItem.get()) - .addIngredient('S', changeSkinItem.get()) - .addIngredient('P', settingsAccessItem.get()) - .addIngredient('A', adminAccessItem.get()) - .addIngredient('D', randomSkinItem.get()) - .addIngredient('F', favoritesItem.get()) - .build(); - this.player = player; - } - - public Gui getGUI() { - return gui; - } - - public String getTitle() { - return title; - } - - public void open() { - Window.single().setGui(gui).setTitle(title).open(player); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/InvalidateSkinGUI.java b/src/main/java/xyz/ineanto/nicko/gui/InvalidateSkinGUI.java deleted file mode 100644 index a2d3e31..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/InvalidateSkinGUI.java +++ /dev/null @@ -1,69 +0,0 @@ -package xyz.ineanto.nicko.gui; - -import xyz.ineanto.nicko.gui.items.common.GoBackItem; -import xyz.ineanto.nicko.gui.items.common.ScrollUpItem; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.mojang.MojangSkin; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.ScrollGui; -import xyz.xenondevs.invui.gui.structure.Markers; -import xyz.xenondevs.invui.item.Item; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.gui.items.admin.cache.CacheEntryItem; -import xyz.ineanto.nicko.gui.items.common.ScrollDownItem; -import org.bukkit.entity.Player; -import xyz.xenondevs.invui.window.Window; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentMap; -import java.util.stream.Collectors; - -public class InvalidateSkinGUI { - private final Player player; - private final Gui gui; - private final String title; - - public InvalidateSkinGUI(Player player) { - final PlayerLanguage playerLanguage = new PlayerLanguage(player); - this.title = playerLanguage.translate(LanguageKey.GUI.Titles.INVALIDATE_SKIN, false); - - final ConcurrentMap> skins = Nicko.getInstance().getMojangAPI().getSkinCache().asMap(); - final List loadedSkins = skins.entrySet().stream() - .filter(entry -> entry.getValue().isPresent()) - .map(Map.Entry::getKey) - .toList(); - - final List items = loadedSkins.stream() - .map(uuid -> new CacheEntryItem(playerLanguage, uuid)) - .collect(Collectors.toList()); - - final CacheManagementGUI parent = new CacheManagementGUI(player); - final ScrollUpItem scrollUpItem = new ScrollUpItem(playerLanguage); - final ScrollDownItem scrollDownItem = new ScrollDownItem(playerLanguage); - final GoBackItem backItem = new GoBackItem(player); - - gui = ScrollGui.items(guiItemBuilder -> { - guiItemBuilder.setStructure( - "x x x x x x x x U", - "x x x x x x x x #", - "x x x x x x x x #", - "x x x x x x x x #", - "x x x x x x x x D", - "B % % % % % % % %"); - guiItemBuilder.addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL); - guiItemBuilder.addIngredient('U', scrollUpItem); - guiItemBuilder.addIngredient('D', scrollDownItem); - guiItemBuilder.addIngredient('B', backItem.get(parent.getGUI(), parent.getTitle())); - guiItemBuilder.setContent(items); - }); - - this.player = player; - } - - public void open() { - Window.single().setGui(gui).setTitle(title).open(player); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/PlayerCheckGUI.java b/src/main/java/xyz/ineanto/nicko/gui/PlayerCheckGUI.java deleted file mode 100644 index 13ff451..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/PlayerCheckGUI.java +++ /dev/null @@ -1,68 +0,0 @@ -package xyz.ineanto.nicko.gui; - -import org.bukkit.Bukkit; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.gui.items.admin.check.PlayerInformationItem; -import xyz.ineanto.nicko.gui.items.common.GoBackItem; -import xyz.ineanto.nicko.gui.items.common.ScrollDownItem; -import xyz.ineanto.nicko.gui.items.common.ScrollUpItem; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.ScrollGui; -import xyz.xenondevs.invui.gui.structure.Markers; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.window.Window; - -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -public class PlayerCheckGUI { - private final Player player; - private final Gui gui; - private final String title; - - public PlayerCheckGUI(Player player, Collection players) { - final PlayerLanguage playerLanguage = new PlayerLanguage(player); - this.title = playerLanguage.translate(LanguageKey.GUI.Titles.CHECK, false); - - final List items = players.stream() - .map(Entity::getUniqueId) - .map(Bukkit::getPlayer) - .filter(Objects::nonNull) - .map(mappedPlayer -> new PlayerInformationItem(playerLanguage, mappedPlayer)) - .collect(Collectors.toList()); - - final AdminGUI parent = new AdminGUI(player); - final GoBackItem backItem = new GoBackItem(player); - final ScrollUpItem scrollUpItem = new ScrollUpItem(playerLanguage); - final ScrollDownItem scrollDownItem = new ScrollDownItem(playerLanguage); - - gui = ScrollGui.items(guiItemBuilder -> { - guiItemBuilder.setStructure( - "x x x x x x x x U", - "x x x x x x x x #", - "x x x x x x x x #", - "x x x x x x x x #", - "x x x x x x x x D", - "B % % % % % % % %"); - guiItemBuilder.addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL); - guiItemBuilder.addIngredient('U', scrollUpItem); - guiItemBuilder.addIngredient('D', scrollDownItem); - guiItemBuilder.addIngredient('B', backItem.get(parent.getGUI(), parent.getTitle())); - guiItemBuilder.setContent(items); - }); - - this.player = player; - } - - public void open() { - final Window.Builder.Normal.Single window = Window.single().setGui(gui).setTitle(title); - window.addOpenHandler(() -> PlayerCheckGUIData.VIEWERS.add(player.getUniqueId())); - window.addCloseHandler(() -> PlayerCheckGUIData.VIEWERS.remove(player.getUniqueId())); - window.open(player); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/PlayerCheckGUIData.java b/src/main/java/xyz/ineanto/nicko/gui/PlayerCheckGUIData.java deleted file mode 100644 index e238a93..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/PlayerCheckGUIData.java +++ /dev/null @@ -1,8 +0,0 @@ -package xyz.ineanto.nicko.gui; - -import java.util.ArrayList; -import java.util.UUID; - -public class PlayerCheckGUIData { - public static final ArrayList VIEWERS = new ArrayList<>(); -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/SettingsGUI.java b/src/main/java/xyz/ineanto/nicko/gui/SettingsGUI.java deleted file mode 100644 index ee08ba1..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/SettingsGUI.java +++ /dev/null @@ -1,44 +0,0 @@ -package xyz.ineanto.nicko.gui; - -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.gui.items.common.GoBackItem; -import xyz.ineanto.nicko.gui.items.settings.LanguageCyclingItem; -import xyz.ineanto.nicko.gui.items.settings.RandomSkinCyclingItem; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.window.Window; - -public class SettingsGUI { - private final Player player; - private final Gui gui; - private final String title; - - public SettingsGUI(Player player) { - final String[] dynamicStructure = new String[]{ - "# # # # # # # # #", - "# # # L # R # # #", - "B # # # # # # # #" - }; - - final PlayerLanguage playerLanguage = new PlayerLanguage(player); - this.title = playerLanguage.translate(LanguageKey.GUI.Titles.SETTINGS, false); - - final HomeGUI parent = new HomeGUI(player); - final LanguageCyclingItem languageItem = new LanguageCyclingItem(player); - final RandomSkinCyclingItem skinItem = new RandomSkinCyclingItem(player); - final GoBackItem backItem = new GoBackItem(player); - - this.gui = Gui.normal() - .setStructure(dynamicStructure) - .addIngredient('B', backItem.get(parent.getGUI(), parent.getTitle())) - .addIngredient('L', languageItem.get()) - .addIngredient('R', skinItem.get()) - .build(); - this.player = player; - } - - public void open() { - Window.single().setGui(gui).setTitle(title).open(player); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/ItemDefaults.java b/src/main/java/xyz/ineanto/nicko/gui/items/ItemDefaults.java deleted file mode 100644 index 6529e12..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/ItemDefaults.java +++ /dev/null @@ -1,22 +0,0 @@ -package xyz.ineanto.nicko.gui.items; - -import org.bukkit.Material; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.xenondevs.invui.item.builder.AbstractItemBuilder; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.builder.SkullBuilder; - -public class ItemDefaults { - public static AbstractItemBuilder getErrorSkullItem(PlayerLanguage playerLanguage, String key, Object... args) { - // "Missing Value" (Valve's signature missing texture) Texture Value - final SkullBuilder.HeadTexture headTexture = new SkullBuilder.HeadTexture("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjNmZTU5YjJhMWQyYmYzMjcwNDA2OGVmYzg2MGM3NWY5MjEyYzIzMTBiNDNkMDdjNGJiYTRiNGViMjM0ZTY4NCJ9fX0="); - final SkullBuilder builder = new SkullBuilder(headTexture); - return playerLanguage.translateItem(builder, key, args); - } - - public static AbstractItemBuilder getUnavailableItem(PlayerLanguage playerLanguage) { - final ItemBuilder builder = new ItemBuilder(Material.RED_TERRACOTTA); - return playerLanguage.translateItem(builder, LanguageKey.GUI.UNAVAILABLE); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/admin/ManageCacheItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/admin/ManageCacheItem.java deleted file mode 100644 index 9641476..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/admin/ManageCacheItem.java +++ /dev/null @@ -1,46 +0,0 @@ -package xyz.ineanto.nicko.gui.items.admin; - -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.ineanto.nicko.gui.CacheManagementGUI; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -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; -import xyz.xenondevs.invui.item.impl.SuppliedItem; -import xyz.xenondevs.invui.util.MojangApiUtils; - -import java.io.IOException; - -public class ManageCacheItem extends AsyncItem { - public ManageCacheItem(PlayerLanguage playerLanguage) { - super(new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.PAINTING); - return playerLanguage.translateItem(builder, LanguageKey.GUI.LOADING); - }, (_ -> true)).getItemProvider(), - () -> { - AbstractItemBuilder builder; - - try { - builder = new SkullBuilder("Notch"); - } catch (MojangApiUtils.MojangApiException | IOException e) { - builder = new ItemBuilder(Material.PLAYER_HEAD); - } - - return playerLanguage.translateItem(builder, LanguageKey.GUI.Admin.MANAGE_CACHE); - }); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick() || clickType.isRightClick()) { - event.getView().close(); - new CacheManagementGUI(player).open(); - } - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/admin/ManagePlayerItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/admin/ManagePlayerItem.java deleted file mode 100644 index 0a18c0c..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/admin/ManagePlayerItem.java +++ /dev/null @@ -1,30 +0,0 @@ -package xyz.ineanto.nicko.gui.items.admin; - -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.gui.PlayerCheckGUI; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; - -public class ManagePlayerItem { - private final Player player; - private final PlayerLanguage playerLanguage; - - public ManagePlayerItem(PlayerLanguage playerLanguage, Player player) { - this.playerLanguage = playerLanguage; - this.player = player; - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.WRITABLE_BOOK); - return playerLanguage.translateItem(builder, LanguageKey.GUI.Admin.MANAGE_PLAYER); - }, _ -> { - new PlayerCheckGUI(player, Bukkit.getOnlinePlayers()).open(); - return true; - }); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/CacheEntryItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/CacheEntryItem.java deleted file mode 100644 index 705c78d..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/CacheEntryItem.java +++ /dev/null @@ -1,72 +0,0 @@ -package xyz.ineanto.nicko.gui.items.admin.cache; - -import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.gui.ChoiceGUI; -import xyz.ineanto.nicko.gui.InvalidateSkinGUI; -import xyz.ineanto.nicko.gui.items.ItemDefaults; -import xyz.ineanto.nicko.gui.items.common.choice.ChoiceCallback; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.mojang.MojangAPI; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.builder.SkullBuilder; -import xyz.xenondevs.invui.item.impl.AsyncItem; -import xyz.xenondevs.invui.item.impl.SuppliedItem; -import xyz.xenondevs.invui.util.MojangApiUtils; - -import java.io.IOException; -import java.util.UUID; - -public class CacheEntryItem extends AsyncItem { - private final String name; - private final String uuid; - private final MojangAPI mojangAPI = Nicko.getInstance().getMojangAPI(); - private final PlayerLanguage playerLanguage; - - public CacheEntryItem(PlayerLanguage playerLanguage, String uuid) { - super(new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.PAINTING); - return playerLanguage.translateItem(builder, LanguageKey.GUI.LOADING); - }, (_ -> true)).getItemProvider(), - () -> { - final String dashedUuid = uuid.replaceAll("(.{8})(.{4})(.{4})(.{4})(.+)", "$1-$2-$3-$4-$5"); - final UUID uuidObject = UUID.fromString(dashedUuid); - try { - final SkullBuilder skull = new SkullBuilder(uuidObject); - return playerLanguage.translateItem(skull, LanguageKey.GUI.Admin.Cache.ENTRY, Nicko.getInstance().getMojangAPI().getUUIDName(uuid)); - } catch (MojangApiUtils.MojangApiException | IOException e) { - Nicko.getInstance().getLogger().warning("Unable to get Head texture for specified UUID (" + uuid + ")! (GUI/Cache/Entry)"); - return ItemDefaults.getErrorSkullItem(playerLanguage, LanguageKey.GUI.Admin.Cache.ENTRY, Nicko.getInstance().getMojangAPI().getUUIDName(uuid)); - } - }); - this.playerLanguage = playerLanguage; - this.uuid = uuid; - this.name = mojangAPI.getUUIDName(uuid); - } - - @Override - public void handleClick(@NotNull ClickType click, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (click.isLeftClick() || click.isRightClick()) { - event.getView().close(); - new ChoiceGUI(player, new ChoiceCallback() { - @Override - public void onConfirm() { - player.sendMessage(playerLanguage.translate(LanguageKey.Event.Admin.Cache.INVALIDATE_ENTRY, true, name)); - player.playSound(player.getLocation(), Sound.BLOCK_WOODEN_BUTTON_CLICK_ON, 1, 1f); - mojangAPI.eraseFromCache(uuid); - } - - @Override - public void onCancel() { - new InvalidateSkinGUI(player).open(); - } - }).open(); - } - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/CacheStatisticsItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/CacheStatisticsItem.java deleted file mode 100644 index 6e4653a..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/CacheStatisticsItem.java +++ /dev/null @@ -1,35 +0,0 @@ -package xyz.ineanto.nicko.gui.items.admin.cache; - -import com.google.common.cache.CacheStats; -import com.google.common.cache.LoadingCache; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.mojang.MojangSkin; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; - -import java.util.Optional; - -public class CacheStatisticsItem { - private final PlayerLanguage playerLanguage; - - public CacheStatisticsItem(Player player) { - this.playerLanguage = new PlayerLanguage(player); - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.BOOK); - final LoadingCache> cache = Nicko.getInstance().getMojangAPI().getSkinCache(); - final CacheStats stats = cache.stats(); - - return playerLanguage.translateItem(builder, LanguageKey.GUI.Admin.Cache.STATISTICS, - stats.requestCount(), - Math.round(cache.size()) - ); - }, (event) -> true); - } -} \ No newline at end of file diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/InvalidateCacheItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/InvalidateCacheItem.java deleted file mode 100644 index 2614497..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/InvalidateCacheItem.java +++ /dev/null @@ -1,39 +0,0 @@ -package xyz.ineanto.nicko.gui.items.admin.cache; - -import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; - -public class InvalidateCacheItem { - private final PlayerLanguage playerLanguage; - - public InvalidateCacheItem(Player player) { - this.playerLanguage = new PlayerLanguage(player); - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.TNT); - return playerLanguage.translateItem(builder, LanguageKey.GUI.Admin.Cache.INVALIDATE_CACHE); - }, (click) -> { - final ClickType clickType = click.getClickType(); - if (clickType.isLeftClick() || clickType.isRightClick()) { - click.getEvent().getView().close(); - - final Player player = click.getPlayer(); - final PlayerLanguage playerLanguage = new PlayerLanguage(player); - player.sendMessage(playerLanguage.translateWithWhoosh(LanguageKey.Event.Admin.Cache.INVALIDATE_CACHE)); - player.playSound(player.getLocation(), Sound.BLOCK_WOODEN_BUTTON_CLICK_ON, 1, 1f); - Nicko.getInstance().getMojangAPI().getSkinCache().invalidateAll(); - return true; - } - return false; - }); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/InvalidateSkinItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/InvalidateSkinItem.java deleted file mode 100644 index f39034d..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/InvalidateSkinItem.java +++ /dev/null @@ -1,33 +0,0 @@ -package xyz.ineanto.nicko.gui.items.admin.cache; - -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import xyz.ineanto.nicko.gui.InvalidateSkinGUI; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; - -public class InvalidateSkinItem { - private final PlayerLanguage playerLanguage; - - public InvalidateSkinItem(Player player) { - this.playerLanguage = new PlayerLanguage(player); - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.PAPER); - return playerLanguage.translateItem(builder, LanguageKey.GUI.Admin.Cache.INVALIDATE_SKIN); - }, (click) -> { - final ClickType clickType = click.getClickType(); - if (clickType.isLeftClick() || clickType.isRightClick()) { - click.getEvent().getView().close(); - new InvalidateSkinGUI(click.getPlayer()).open(); - return true; - } - return false; - }); - } -} 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 deleted file mode 100644 index f3c61f2..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/admin/check/PlayerInformationItem.java +++ /dev/null @@ -1,97 +0,0 @@ -package xyz.ineanto.nicko.gui.items.admin.check; - -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.appearance.AppearanceManager; -import xyz.ineanto.nicko.gui.ChoiceGUI; -import xyz.ineanto.nicko.gui.PlayerCheckGUI; -import xyz.ineanto.nicko.gui.items.ItemDefaults; -import xyz.ineanto.nicko.gui.items.common.choice.ChoiceCallback; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.ineanto.nicko.storage.name.PlayerNameStore; -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; -import xyz.xenondevs.invui.item.impl.SuppliedItem; -import xyz.xenondevs.invui.util.MojangApiUtils; - -import java.io.IOException; -import java.util.Optional; - -public class PlayerInformationItem extends AsyncItem { - private final Player target; - private final NickoProfile profile; - private final PlayerLanguage playerLanguage; - - public PlayerInformationItem(PlayerLanguage playerLanguage, Player target) { - super(new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.PAINTING); - return playerLanguage.translateItem(builder, LanguageKey.GUI.LOADING); - }, (_ -> true)).getItemProvider(), () -> { - try { - final SkullBuilder skull = new SkullBuilder(target.getUniqueId()); - final Optional optionalProfile = Nicko.getInstance().getDataStore().getData(target.getUniqueId()); - final PlayerNameStore playerNameStore = Nicko.getInstance().getNameStore(); - - if (optionalProfile.isPresent()) { - final NickoProfile profile = optionalProfile.get(); - final AbstractItemBuilder headItem = playerLanguage.translateItem(skull, LanguageKey.GUI.Admin.CHECK, - playerNameStore.getStoredName(target), - (profile.hasData() ? "" : ""), - (profile.getName() == null ? "N/A" : profile.getName()), - (profile.getSkin() == null ? "N/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) { - Nicko.getInstance().getLogger().severe("Unable to get head for specified UUID ( " + target.getUniqueId() + ")! (GUI/PlayerCheck)"); - } - - return ItemDefaults.getErrorSkullItem(playerLanguage, LanguageKey.GUI.Admin.CHECK, - "§c§l?!?", "§7N/A", "§7N/A", "§7N/A" - ); - }); - this.playerLanguage = playerLanguage; - this.target = target; - this.profile = Nicko.getInstance().getDataStore().getData(target.getUniqueId()).orElse(NickoProfile.EMPTY_PROFILE); - } - - @Override - public void handleClick(@NotNull ClickType click, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (click.isLeftClick() || click.isRightClick()) { - if (profile.hasData()) { - event.getView().close(); - new ChoiceGUI(player, new ChoiceCallback() { - @Override - public void onConfirm() { - final AppearanceManager appearanceManager = new AppearanceManager(target); - appearanceManager.reset(); - player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_TRADE, 1, 1f); - player.sendMessage(playerLanguage.translate(LanguageKey.Event.Admin.Check.REMOVE_SKIN, true, target.getName())); - } - - @Override - public void onCancel() { - new PlayerCheckGUI(player, Bukkit.getOnlinePlayers()).open(); - } - }).open(); - } - } - } - -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/appearance/ChangeBothItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/appearance/ChangeBothItem.java deleted file mode 100644 index 730e667..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/appearance/ChangeBothItem.java +++ /dev/null @@ -1,33 +0,0 @@ -package xyz.ineanto.nicko.gui.items.appearance; - -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import xyz.ineanto.nicko.prompt.PromptManager; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; - -public class ChangeBothItem { - private final PlayerLanguage playerLanguage; - - public ChangeBothItem(Player player) { - this.playerLanguage = new PlayerLanguage(player); - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.ARMOR_STAND); - return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.CHANGE_BOTH); - }, click -> { - final ClickType clickType = click.getClickType(); - if (clickType.isLeftClick() || clickType.isRightClick()) { - click.getEvent().getView().close(); - final PromptManager manager = new PromptManager(click.getPlayer()); - manager.displayNameThenSkinPrompt(); - } - return true; - }); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/appearance/ChangeNameItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/appearance/ChangeNameItem.java deleted file mode 100644 index c67b848..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/appearance/ChangeNameItem.java +++ /dev/null @@ -1,33 +0,0 @@ -package xyz.ineanto.nicko.gui.items.appearance; - -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import xyz.ineanto.nicko.prompt.PromptManager; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; - -public class ChangeNameItem { - private final PlayerLanguage playerLanguage; - - public ChangeNameItem(Player player) { - this.playerLanguage = new PlayerLanguage(player); - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.NAME_TAG); - return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.CHANGE_NAME); - }, click -> { - final ClickType clickType = click.getClickType(); - if (clickType.isLeftClick() || clickType.isRightClick()) { - click.getEvent().getView().close(); - final PromptManager manager = new PromptManager(click.getPlayer()); - manager.displayNamePromptThenUpdate(); - } - return true; - }); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/appearance/ChangeSkinItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/appearance/ChangeSkinItem.java deleted file mode 100644 index 298e0ea..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/appearance/ChangeSkinItem.java +++ /dev/null @@ -1,51 +0,0 @@ -package xyz.ineanto.nicko.gui.items.appearance; - -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.prompt.PromptManager; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.storage.name.PlayerNameStore; -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.SuppliedItem; -import xyz.xenondevs.invui.util.MojangApiUtils; - -import java.io.IOException; - -public class ChangeSkinItem { - private final PlayerLanguage playerLanguage; - private final PlayerNameStore playerNameStore; - private final Player player; - - public ChangeSkinItem(Player player) { - this.playerLanguage = new PlayerLanguage(player); - this.playerNameStore = Nicko.getInstance().getNameStore(); - this.player = player; - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - AbstractItemBuilder builder; - - try { - builder = new SkullBuilder(playerNameStore.getStoredName(player)); - } catch (MojangApiUtils.MojangApiException | IOException e) { - builder = new ItemBuilder(Material.PLAYER_HEAD); - } - - return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.CHANGE_SKIN); - }, click -> { - final ClickType clickType = click.getClickType(); - if (clickType.isLeftClick() || clickType.isRightClick()) { - click.getEvent().getView().close(); - final PromptManager manager = new PromptManager(click.getPlayer()); - manager.displaySkinPrompt(); - } - return true; - }); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/common/GoBackItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/common/GoBackItem.java deleted file mode 100644 index 1dc7752..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/common/GoBackItem.java +++ /dev/null @@ -1,29 +0,0 @@ -package xyz.ineanto.nicko.gui.items.common; - -import org.bukkit.Material; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; -import xyz.xenondevs.invui.window.Window; - -public class GoBackItem { - private final PlayerLanguage playerLanguage; - - public GoBackItem(Player player) { - this.playerLanguage = new PlayerLanguage(player); - } - - public SuppliedItem get(Gui gui, String parentTitle) { - return new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.ARROW); - return playerLanguage.translateItem(builder, LanguageKey.GUI.GO_BACK); - }, click -> { - click.getEvent().getView().close(); - Window.single().setGui(gui).setTitle(parentTitle).open(click.getPlayer()); - return true; - }); - } -} \ No newline at end of file diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/common/ScrollDownItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/common/ScrollDownItem.java deleted file mode 100644 index 64054d9..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/common/ScrollDownItem.java +++ /dev/null @@ -1,39 +0,0 @@ -package xyz.ineanto.nicko.gui.items.common; - -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Material; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.Translation; -import xyz.xenondevs.invui.gui.ScrollGui; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.controlitem.ScrollItem; - -public class ScrollDownItem extends ScrollItem { - final PlayerLanguage playerLanguage; - - public ScrollDownItem(PlayerLanguage playerLanguage) { - super(1); - this.playerLanguage = playerLanguage; - } - - @Override - public ItemProvider getItemProvider(ScrollGui gui) { - final ItemBuilder builder = new ItemBuilder(Material.GREEN_STAINED_GLASS_PANE); - final Translation translation = playerLanguage.translateAndReplace(LanguageKey.GUI.SCROLL_DOWN); - builder.setDisplayName(Component.text(translation.name()).content()); - if (!gui.canScroll(1)) { - // Lore serialization - translation.lore().replaceAll(s -> { - final Component deserializedLoreLine = MiniMessage.miniMessage().deserialize(s); - return LegacyComponentSerializer.legacySection().serialize(deserializedLoreLine); - }); - translation.lore().forEach(builder::addLoreLines); - } - return builder; - } -} - diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/common/ScrollUpItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/common/ScrollUpItem.java deleted file mode 100644 index 8f1fe88..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/common/ScrollUpItem.java +++ /dev/null @@ -1,40 +0,0 @@ -package xyz.ineanto.nicko.gui.items.common; - -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Material; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.Translation; -import xyz.xenondevs.invui.gui.ScrollGui; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.controlitem.ScrollItem; - -public class ScrollUpItem extends ScrollItem { - final PlayerLanguage playerLanguage; - - public ScrollUpItem(PlayerLanguage playerLanguage) { - super(-1); - this.playerLanguage = playerLanguage; - } - - @Override - public ItemProvider getItemProvider(ScrollGui gui) { - final ItemBuilder builder = new ItemBuilder(Material.RED_STAINED_GLASS_PANE); - final Translation translation = playerLanguage.translateAndReplace(LanguageKey.GUI.SCROLL_UP); - builder.setDisplayName(Component.text(translation.name()).content()); - if (!gui.canScroll(-1)) { - // Lore serialization - translation.lore().replaceAll(s -> { - final Component deserializedLoreLine = MiniMessage.miniMessage().deserialize(s); - return LegacyComponentSerializer.legacySection().serialize(deserializedLoreLine); - }); - translation.lore().forEach(builder::addLoreLines); - } - return builder; - } - -} - diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/common/choice/CancelItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/common/choice/CancelItem.java deleted file mode 100644 index 8fa5c40..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/common/choice/CancelItem.java +++ /dev/null @@ -1,29 +0,0 @@ -package xyz.ineanto.nicko.gui.items.common.choice; - -import org.bukkit.Material; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; - -public class CancelItem { - private final PlayerLanguage playerLanguage; - private final ChoiceCallback callback; - - public CancelItem(Player player, ChoiceCallback callback) { - this.playerLanguage = new PlayerLanguage(player); - this.callback = callback; - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.RED_STAINED_GLASS_PANE); - return playerLanguage.translateItem(builder, LanguageKey.GUI.Choice.CANCEL); - }, click -> { - click.getEvent().getView().close(); - callback.onCancel(); - return true; - }); - } -} \ No newline at end of file diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/common/choice/ChoiceCallback.java b/src/main/java/xyz/ineanto/nicko/gui/items/common/choice/ChoiceCallback.java deleted file mode 100644 index ebe70fa..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/common/choice/ChoiceCallback.java +++ /dev/null @@ -1,7 +0,0 @@ -package xyz.ineanto.nicko.gui.items.common.choice; - -public interface ChoiceCallback { - void onConfirm(); - - void onCancel(); -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/common/choice/ConfirmItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/common/choice/ConfirmItem.java deleted file mode 100644 index 95fcacb..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/common/choice/ConfirmItem.java +++ /dev/null @@ -1,29 +0,0 @@ -package xyz.ineanto.nicko.gui.items.common.choice; - -import org.bukkit.Material; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; - -public class ConfirmItem { - private final PlayerLanguage playerLanguage; - private final ChoiceCallback callback; - - public ConfirmItem(Player player, ChoiceCallback callback) { - this.playerLanguage = new PlayerLanguage(player); - this.callback = callback; - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.GREEN_STAINED_GLASS_PANE); - return playerLanguage.translateItem(builder, LanguageKey.GUI.Choice.CONFIRM); - }, click -> { - click.getEvent().getView().close(); - callback.onConfirm(); - return true; - }); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/favorites/FavoriteAddItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/favorites/FavoriteAddItem.java deleted file mode 100644 index 09aa623..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/favorites/FavoriteAddItem.java +++ /dev/null @@ -1,82 +0,0 @@ -package xyz.ineanto.nicko.gui.items.favorites; - -import io.papermc.paper.datacomponent.DataComponentTypes; -import io.papermc.paper.datacomponent.item.TooltipDisplay; -import org.bukkit.DyeColor; -import org.bukkit.Material; -import org.bukkit.block.banner.Pattern; -import org.bukkit.block.banner.PatternType; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.inventory.ItemFlag; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BannerMeta; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.appearance.Appearance; -import xyz.ineanto.nicko.gui.FavoritesGUI; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.ineanto.nicko.storage.PlayerDataStore; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; - -import java.util.List; - -public class FavoriteAddItem { - private final PlayerDataStore dataStore = Nicko.getInstance().getDataStore(); - - private final Player player; - private final PlayerLanguage playerLanguage; - private final NickoProfile profile; - - public FavoriteAddItem(Player player) { - this.player = player; - this.playerLanguage = new PlayerLanguage(player); - this.profile = dataStore.getData(player.getUniqueId()).orElse(NickoProfile.EMPTY_PROFILE); - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - final ItemStack banner = new ItemStack(Material.GREEN_BANNER); - final BannerMeta bannerMeta = (BannerMeta) banner.getItemMeta(); - - // Plus sign - bannerMeta.addPattern(new Pattern(DyeColor.WHITE, PatternType.STRIPE_MIDDLE)); - bannerMeta.addPattern(new Pattern(DyeColor.WHITE, PatternType.STRIPE_CENTER)); - - // Remove excess on the borders - bannerMeta.addPattern(new Pattern(DyeColor.GREEN, PatternType.BORDER)); - bannerMeta.addPattern(new Pattern(DyeColor.GREEN, PatternType.CURLY_BORDER)); - bannerMeta.addPattern(new Pattern(DyeColor.GREEN, PatternType.STRIPE_TOP)); - bannerMeta.addPattern(new Pattern(DyeColor.GREEN, PatternType.STRIPE_BOTTOM)); - - banner.setItemMeta(bannerMeta); - - banner.addItemFlags(ItemFlag.HIDE_ADDITIONAL_TOOLTIP); - banner.setData(DataComponentTypes.TOOLTIP_DISPLAY, TooltipDisplay - .tooltipDisplay() - .addHiddenComponents(DataComponentTypes.BANNER_PATTERNS) - .build() - ); - - final ItemBuilder builder = new ItemBuilder(banner); - return playerLanguage.translateItem(builder, LanguageKey.GUI.Favorites.ADD); - }, click -> { - final ClickType clickType = click.getClickType(); - if (clickType.isShiftClick() && clickType.isLeftClick()) { - if (!profile.hasData()) { - click.getEvent().getView().close(); - return false; - } - - final List favorites = profile.getFavorites(); - favorites.add(profile.getAppearance()); - dataStore.updateCache(player.getUniqueId(), profile); - new FavoritesGUI(player).open(); - return true; - } - return false; - }); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/favorites/FavoriteAppearanceEntryItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/favorites/FavoriteAppearanceEntryItem.java deleted file mode 100644 index 78da1da..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/favorites/FavoriteAppearanceEntryItem.java +++ /dev/null @@ -1,46 +0,0 @@ -package xyz.ineanto.nicko.gui.items.favorites; - -import org.bukkit.Material; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.appearance.Appearance; -import xyz.ineanto.nicko.appearance.AppearanceManager; -import xyz.ineanto.nicko.gui.items.ItemDefaults; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.builder.SkullBuilder; -import xyz.xenondevs.invui.item.impl.AsyncItem; -import xyz.xenondevs.invui.util.MojangApiUtils; - -import java.io.IOException; - -public class FavoriteAppearanceEntryItem { - private final AppearanceManager appearanceManager; - private final PlayerLanguage playerLanguage; - private final Appearance appearance; - - public FavoriteAppearanceEntryItem(Player player, Appearance appearance) { - this.appearanceManager = new AppearanceManager(player); - this.playerLanguage = new PlayerLanguage(player); - this.appearance = appearance; - } - - public AsyncItem get() { - // TODO (Ineanto, 26/06/2025): handle click - final ItemBuilder temporaryItemBuilder = new ItemBuilder(Material.PAINTING); - return new AsyncItem(playerLanguage.translateItem(temporaryItemBuilder, LanguageKey.GUI.LOADING), - () -> { - try { - // TODO (Ineanto, 08/06/2025): set a default skin if the entry contains only a name - final String name = (appearance.name() == null ? "N/A" : appearance.name()); - final String skin = (appearance.skin() == null ? "N/A" : appearance.skin()); - final SkullBuilder skull = new SkullBuilder(skin); - return playerLanguage.translateItem(skull, LanguageKey.GUI.Favorites.ENTRY, name, skin); - } catch (MojangApiUtils.MojangApiException | IOException e) { - Nicko.getInstance().getLogger().warning("Unable to get Head texture for specified UUID (" + appearance.skin() + ")! (GUI/Favorites/Entry)"); - return ItemDefaults.getErrorSkullItem(playerLanguage, LanguageKey.GUI.Favorites.ENTRY, "N/A", "N/A"); - } - }); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/favorites/FavoriteRemoveItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/favorites/FavoriteRemoveItem.java deleted file mode 100644 index 1e66f09..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/favorites/FavoriteRemoveItem.java +++ /dev/null @@ -1,55 +0,0 @@ -package xyz.ineanto.nicko.gui.items.favorites; - -import io.papermc.paper.datacomponent.DataComponentTypes; -import io.papermc.paper.datacomponent.item.TooltipDisplay; -import org.bukkit.DyeColor; -import org.bukkit.Material; -import org.bukkit.block.banner.Pattern; -import org.bukkit.block.banner.PatternType; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.inventory.ItemFlag; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BannerMeta; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; - -public class FavoriteRemoveItem { - private final PlayerLanguage playerLanguage; - - public FavoriteRemoveItem(PlayerLanguage playerLanguage) { - this.playerLanguage = playerLanguage; - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - final ItemStack banner = new ItemStack(Material.RED_BANNER); - final BannerMeta bannerMeta = (BannerMeta) banner.getItemMeta(); - - // Minus sign - bannerMeta.addPattern(new Pattern(DyeColor.WHITE, PatternType.STRIPE_MIDDLE)); - - // Remove excess - bannerMeta.addPattern(new Pattern(DyeColor.RED, PatternType.BORDER)); - banner.setItemMeta(bannerMeta); - - banner.addItemFlags(ItemFlag.HIDE_ADDITIONAL_TOOLTIP); - banner.setData(DataComponentTypes.TOOLTIP_DISPLAY, TooltipDisplay - .tooltipDisplay() - .addHiddenComponents(DataComponentTypes.BANNER_PATTERNS) - .build() - ); - - final ItemBuilder builder = new ItemBuilder(banner); - return playerLanguage.translateItem(builder, LanguageKey.GUI.Favorites.REMOVE); - }, click -> { - final ClickType clickType = click.getClickType(); - if (clickType.isLeftClick() || clickType.isRightClick()) { - click.getEvent().getView().close(); - return true; - } - return false; - }); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/home/ExitItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/home/ExitItem.java deleted file mode 100644 index 2d04be3..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/home/ExitItem.java +++ /dev/null @@ -1,31 +0,0 @@ -package xyz.ineanto.nicko.gui.items.home; - -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; - -public class ExitItem { - private final PlayerLanguage playerLanguage; - - public ExitItem(Player player) { - this.playerLanguage = new PlayerLanguage(player); - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.OAK_DOOR); - return playerLanguage.translateItem(builder, LanguageKey.GUI.EXIT); - }, click -> { - click.getEvent().getView().close(); - final ClickType clickType = click.getClickType(); - if (clickType.isLeftClick() || clickType.isRightClick()) { - click.getEvent().getView().close(); - } - return true; - }); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/home/FavoritesItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/home/FavoritesItem.java deleted file mode 100644 index 97472da..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/home/FavoritesItem.java +++ /dev/null @@ -1,36 +0,0 @@ -package xyz.ineanto.nicko.gui.items.home; - -import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import xyz.ineanto.nicko.gui.FavoritesGUI; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; - -public class FavoritesItem { - private final PlayerLanguage playerLanguage; - - public FavoritesItem(Player player) { - this.playerLanguage = new PlayerLanguage(player); - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.CHEST); - return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.FAVORITES); - }, click -> { - final Player player = click.getPlayer(); - final ClickType clickType = click.getClickType(); - if (clickType.isLeftClick() || clickType.isRightClick()) { - click.getEvent().getView().close(); - new FavoritesGUI(click.getPlayer()).open(); - player.playSound(player.getLocation(), Sound.BLOCK_CHEST_OPEN, 1, 1f); - return true; - } - return false; - }); - } -} \ No newline at end of file diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/home/RandomSkinItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/home/RandomSkinItem.java deleted file mode 100644 index 1bb0e40..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/home/RandomSkinItem.java +++ /dev/null @@ -1,60 +0,0 @@ -package xyz.ineanto.nicko.gui.items.home; - -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.appearance.ActionResult; -import xyz.ineanto.nicko.appearance.AppearanceManager; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; - -import java.util.Optional; - -public class RandomSkinItem { - private final PlayerLanguage playerLanguage; - private final Nicko instance; - - public RandomSkinItem(Player player) { - this.instance = Nicko.getInstance(); - this.playerLanguage = new PlayerLanguage(player); - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.WIND_CHARGE); - return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.RANDOM_SKIN); - }, (event) -> { - final Player player = event.getPlayer(); - final ClickType clickType = event.getClickType(); - if (clickType.isLeftClick() || clickType.isRightClick()) { - final Optional optionalProfile = NickoProfile.get(player); - optionalProfile.ifPresent(profile -> { - final String name = instance.getNameFetcher().getRandomUsername(); - final String skin = instance.getNameFetcher().getRandomUsername(); - profile.setName(name); - profile.setSkin(skin); - instance.getDataStore().updateCache(player.getUniqueId(), profile); - - final AppearanceManager appearanceManager = new AppearanceManager(player); - final ActionResult result = appearanceManager.update(true); - if (!result.isError()) { - player.sendMessage(playerLanguage.translateWithWhoosh(LanguageKey.Event.Appearance.Set.OK)); - } else { - player.sendMessage(playerLanguage.translateWithOops( - LanguageKey.Event.Appearance.Set.ERROR, - result.getErrorKey() - ) - ); - appearanceManager.reset(); - } - }); - return true; - } - return false; - }); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/home/ResetItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/home/ResetItem.java deleted file mode 100644 index c3f1b68..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/home/ResetItem.java +++ /dev/null @@ -1,52 +0,0 @@ -package xyz.ineanto.nicko.gui.items.home; - -import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import xyz.ineanto.nicko.appearance.AppearanceManager; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.SuppliedItem; - -import java.util.Optional; - -public class ResetItem { - private final PlayerLanguage playerLanguage; - - public ResetItem(Player player) { - this.playerLanguage = new PlayerLanguage(player); - } - - public SuppliedItem get() { - return new SuppliedItem(() -> { - final ItemBuilder builder = new ItemBuilder(Material.TNT); - return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.RESET); - }, (event) -> { - final Player player = event.getPlayer(); - final ClickType clickType = event.getClickType(); - if (clickType.isLeftClick() || clickType.isRightClick()) { - final Optional optionalProfile = NickoProfile.get(player); - optionalProfile.ifPresent(profile -> { - if (!profile.hasData()) { - player.sendMessage(playerLanguage.translateWithOops(LanguageKey.Event.Appearance.Remove.MISSING)); - event.getEvent().getView().close(); - return; - } - - final AppearanceManager appearanceManager = new AppearanceManager(player); - if (!appearanceManager.reset().isError()) { - player.sendMessage(playerLanguage.translateWithWhoosh(LanguageKey.Event.Appearance.Remove.OK)); - } else { - player.sendMessage(playerLanguage.translateWithOops(LanguageKey.Event.Appearance.Remove.ERROR)); - player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 1f); - } - }); - return true; - } - return false; - }); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/settings/LanguageCyclingItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/settings/LanguageCyclingItem.java deleted file mode 100644 index b836991..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/settings/LanguageCyclingItem.java +++ /dev/null @@ -1,94 +0,0 @@ -package xyz.ineanto.nicko.gui.items.settings; - -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.gui.SettingsGUI; -import xyz.ineanto.nicko.language.Language; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.Translation; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.ineanto.nicko.storage.PlayerDataStore; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.CycleItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -public class LanguageCyclingItem { - private final Player player; - private final ItemProvider[] providers; - private final PlayerLanguage playerLanguage; - - public LanguageCyclingItem(Player player) { - this.player = player; - this.playerLanguage = new PlayerLanguage(player); - this.providers = getItems(); - } - - public AbstractItem get() { - final PlayerDataStore dataStore = Nicko.getInstance().getDataStore(); - final Optional profile = dataStore.getData(player.getUniqueId()); - if (profile.isPresent()) { - final NickoProfile nickoProfile = profile.get(); - int localeOrdinal = nickoProfile.getLocale().ordinal(); - return CycleItem.withStateChangeHandler((observer, integer) -> { - observer.playSound(player, Sound.UI_BUTTON_CLICK, 1f, 0.707107f); // 0.707107 ~= C - nickoProfile.setLocale(Language.values()[integer]); - player.getOpenInventory().close(); - if (dataStore.updateCache(player.getUniqueId(), nickoProfile).isError()) { - player.sendMessage(playerLanguage.translate(LanguageKey.Event.Settings.ERROR, true)); - } else { - new SettingsGUI(player).open(); - } - }, localeOrdinal, providers); - } - - return new SimpleItem(ItemProvider.EMPTY); - } - - private ItemProvider generateItem(Language language, List languages) { - final ItemBuilder builder = new ItemBuilder(Material.OAK_SIGN); - final Translation translation = playerLanguage.translateAndReplace(LanguageKey.GUI.Settings.LANGUAGE); - final Translation cyclingChoicesTranslation = playerLanguage.translateAndReplace(LanguageKey.GUI.Settings.CYCLING_CHOICES); - - builder.setDisplayName(Component.text(translation.name()).content()); - for (Language value : languages) { - if (language != value) { - builder.addLoreLines("§7> " + value.getName()); - } else { - builder.addLoreLines("§6§l> §f" + value.getName()); - } - } - - cyclingChoicesTranslation.lore().replaceAll(s -> { - final Component deserializedLoreLine = MiniMessage.miniMessage().deserialize(s); - return LegacyComponentSerializer.legacySection().serialize(deserializedLoreLine); - }); - cyclingChoicesTranslation.lore().forEach(builder::addLoreLines); - return builder; - } - - private ItemProvider[] getItems() { - final Nicko instance = Nicko.getInstance(); - final ArrayList items = new ArrayList<>(); - final ArrayList localesToGenerate = new ArrayList<>(); - - Collections.addAll(localesToGenerate, Language.values()); - if (!instance.getNickoConfig().isCustomLocale()) { - localesToGenerate.remove(Language.CUSTOM); - } - localesToGenerate.forEach(locale -> items.add(generateItem(locale, localesToGenerate))); - return items.toArray(new ItemProvider[]{}); - } -} \ No newline at end of file diff --git a/src/main/java/xyz/ineanto/nicko/gui/items/settings/RandomSkinCyclingItem.java b/src/main/java/xyz/ineanto/nicko/gui/items/settings/RandomSkinCyclingItem.java deleted file mode 100644 index abe356a..0000000 --- a/src/main/java/xyz/ineanto/nicko/gui/items/settings/RandomSkinCyclingItem.java +++ /dev/null @@ -1,75 +0,0 @@ -package xyz.ineanto.nicko.gui.items.settings; - -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Sound; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.Translation; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.ineanto.nicko.storage.PlayerDataStore; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.SkullBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.CycleItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; - -import java.util.Optional; - -public class RandomSkinCyclingItem { - private final Player player; - private final ItemProvider[] providers; - private final PlayerLanguage playerLanguage; - - public RandomSkinCyclingItem(Player player) { - this.player = player; - this.playerLanguage = new PlayerLanguage(player); - this.providers = new ItemProvider[]{ - getItemProviderForValue(true), - getItemProviderForValue(false) - }; - } - - public AbstractItem get() { - final PlayerDataStore dataStore = Nicko.getInstance().getDataStore(); - final Optional profile = dataStore.getData(player.getUniqueId()); - if (profile.isPresent()) { - final NickoProfile nickoProfile = profile.get(); - int localeOrdinal = nickoProfile.isRandomSkin() ? 0 : 1; - return CycleItem.withStateChangeHandler((observer, integer) -> { - observer.playSound(player, Sound.UI_BUTTON_CLICK, 1f, 0.707107f); // 0.707107 ~= C - nickoProfile.setRandomSkin(integer != 1); - if (dataStore.updateCache(player.getUniqueId(), nickoProfile).isError()) { - player.sendMessage(playerLanguage.translate(LanguageKey.Event.Settings.ERROR, true)); - player.getOpenInventory().close(); - } - }, localeOrdinal, providers); - } - - return new SimpleItem(ItemProvider.EMPTY); - } - - private ItemProvider getItemProviderForValue(boolean enabled) { - final SkullBuilder.HeadTexture texture = new SkullBuilder.HeadTexture("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzgzMTEzOGMyMDYxMWQzMDJjNDIzZmEzMjM3MWE3NDNkMTc0MzdhMTg5NzNjMzUxOTczNDQ3MGE3YWJiNCJ9fX0="); - final SkullBuilder builder = new SkullBuilder(texture); - final Translation randomSkinTranslation = playerLanguage.translateAndReplace(LanguageKey.GUI.Settings.RANDOM_SKIN); - final Translation toggleableTranslation = playerLanguage.translateAndReplace(LanguageKey.GUI.Settings.TOGGLEABLE_BUTTON, - (enabled ? "§7>§c" : "§6§l>§c§l"), - (enabled ? "§6§l>§a§l" : "§7>§a") - ); - final Translation cyclingChoicesTranslation = playerLanguage.translateAndReplace(LanguageKey.GUI.Settings.CYCLING_CHOICES); - - builder.setDisplayName(randomSkinTranslation.name()); - toggleableTranslation.lore().forEach(builder::addLoreLines); - - cyclingChoicesTranslation.lore().replaceAll(s -> { - final Component deserializedLoreLine = MiniMessage.miniMessage().deserialize(s); - return LegacyComponentSerializer.legacySection().serialize(deserializedLoreLine); - }); - cyclingChoicesTranslation.lore().forEach(builder::addLoreLines); - return builder; - } -} \ No newline at end of file diff --git a/src/main/java/xyz/ineanto/nicko/language/CustomLanguage.java b/src/main/java/xyz/ineanto/nicko/language/CustomLanguage.java deleted file mode 100644 index 23e5c7e..0000000 --- a/src/main/java/xyz/ineanto/nicko/language/CustomLanguage.java +++ /dev/null @@ -1,61 +0,0 @@ -package xyz.ineanto.nicko.language; - -import com.github.jsixface.YamlConfig; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.version.Version; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; - -public class CustomLanguage { - private static final File directory = new File(Nicko.getInstance().getDataFolder(), "/locale/"); - private static final File file = new File(directory, "locale.yml"); - - private final String version; - private final Version versionObject; - private final YamlConfig yamlFile; - - public CustomLanguage() throws IOException { - this.yamlFile = new YamlConfig(new FileInputStream(file)); - this.version = yamlFile.getString("version"); - this.versionObject = Version.fromString(version); - } - - public static void dumpIntoFile(Language language) throws IOException { - if (language == Language.CUSTOM) return; - if (file.exists()) return; - if (!directory.exists()) directory.mkdirs(); - - final String localeFileName = language.getCode() + ".yml"; - try { - final InputStream resource = Nicko.getInstance().getResource(localeFileName); - Files.copy(resource, file.toPath()); - resource.close(); - } catch (IOException e) { - Nicko.getInstance().getLogger().severe("Unable to dump Locale: " + language.getCode() + "!"); - } - } - - public String getVersion() { - return version; - } - - public Version getVersionObject() { - return versionObject; - } - - public YamlConfig getYamlFile() { - return yamlFile; - } - - public File getDirectory() { - return directory; - } - - public File getFile() { - return file; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/language/LanguageKey.java b/src/main/java/xyz/ineanto/nicko/language/LanguageKey.java deleted file mode 100644 index 06c258f..0000000 --- a/src/main/java/xyz/ineanto/nicko/language/LanguageKey.java +++ /dev/null @@ -1,154 +0,0 @@ -package xyz.ineanto.nicko.language; - -public class LanguageKey { - public static final String PREFIX = "prefix"; - public static final String WHOOSH = "whoosh"; - public static final String OOPS = "oops"; - - public static class Error { - public static final String ERROR_KEY = "error."; - - public static final String PERMISSION = ERROR_KEY + "permission"; - public static final String CACHE = ERROR_KEY + "cache"; - public static final String MOJANG = ERROR_KEY + "mojang"; - } - - public static class Event { - private static final String EVENT_KEY = "event."; - - public static class Admin { - private static final String ADMIN_KEY = EVENT_KEY + "admin."; - - public static class Cache { - private static final String CACHE_KEY = ADMIN_KEY + "cache."; - - public static final String INVALIDATE_CACHE = CACHE_KEY + "invalidate_cache"; - public static final String INVALIDATE_ENTRY = CACHE_KEY + "invalidate_entry"; - - } - - public static class Check { - private static final String CHECK_KEY = ADMIN_KEY + "check."; - - public static final String REMOVE_SKIN = CHECK_KEY + "remove_skin"; - } - } - - public static class Settings { - private static final String SETTINGS_KEY = EVENT_KEY + "settings."; - - public static final String ERROR = SETTINGS_KEY + "error"; - } - - public static class Appearance { - private static final String APPEARANCE_KEY = EVENT_KEY + "appearance."; - - public static class Set { - public static final String SET_KEY = APPEARANCE_KEY + "set."; - - public static final String OK = SET_KEY + "ok"; - public static final String ERROR = SET_KEY + "error"; - - public static final String CHAT_PROMPT_NAME = SET_KEY + "chat_prompt_name"; - public static final String CHAT_PROMPT_SKIN = SET_KEY + "chat_prompt_skin"; - } - - public static class Remove { - private static final String REMOVE_KEY = APPEARANCE_KEY + "remove."; - - public static final String OK = REMOVE_KEY + "ok"; - public static final String MISSING = REMOVE_KEY + "missing"; - public static final String ERROR = REMOVE_KEY + "error"; - } - - public static class Restore { - private static final String RESTORE_KEY = APPEARANCE_KEY + "restore."; - - public static final String OK = RESTORE_KEY + "ok"; - public static final String ERROR = RESTORE_KEY + "error"; - } - } - } - - public static class GUI { - private static final String GUI_KEY = "gui."; - - public static final String EXIT = GUI_KEY + "exit"; - public static final String GO_BACK = GUI_KEY + "go_back"; - public static final String UNAVAILABLE = GUI_KEY + "unavailable"; - public static final String LOADING = GUI_KEY + "loading"; - public static final String SCROLL_UP = GUI_KEY + "scroll_up"; - public static final String SCROLL_DOWN = GUI_KEY + "scroll_down"; - public static final String NEW_SKIN = GUI_KEY + "new_skin"; - public static final String NEW_NAME = GUI_KEY + "new_name"; - - public static class Titles { - public static final String TITLE_KEY = GUI_KEY + "title."; - - public static final String HOME = TITLE_KEY + "home"; - public static final String SETTINGS = TITLE_KEY + "settings"; - public static final String ADMIN = TITLE_KEY + "admin"; - public static final String CHECK = TITLE_KEY + "check"; - public static final String CONFIRM = TITLE_KEY + "confirm"; - public static final String CACHE = TITLE_KEY + "cache"; - public static final String INVALIDATE_SKIN = TITLE_KEY + "invalidate_skin"; - public static final String FAVORITES = TITLE_KEY + "favorites"; - } - - public static class Choice { - private static final String CHOICE_KEY = GUI_KEY + "choice."; - - public static final String CONFIRM = CHOICE_KEY + "confirm"; - public static final String CHOOSE = CHOICE_KEY + "choose"; - public static final String CANCEL = CHOICE_KEY + "cancel"; - } - - public static class Home { - private static final String HOME_KEY = GUI_KEY + "home."; - - public static final String ADMIN = HOME_KEY + "admin"; - public static final String CHANGE_NAME = HOME_KEY + "change_name"; - public static final String CHANGE_SKIN = HOME_KEY + "change_skin"; - public static final String CHANGE_BOTH = HOME_KEY + "change_both"; - public static final String RESET = HOME_KEY + "reset"; - public static final String RANDOM_SKIN = HOME_KEY + "random_skin"; - public static final String SETTINGS = HOME_KEY + "settings"; - public static final String FAVORITES = HOME_KEY + "favorites"; - } - - public static class Settings { - private static final String SETTINGS_KEY = GUI_KEY + "settings."; - - public static final String CYCLING_CHOICES = SETTINGS_KEY + "cycling_choices"; - public static final String TOGGLEABLE_BUTTON = SETTINGS_KEY + "toggleable_button"; - - public static final String LANGUAGE = SETTINGS_KEY + "language"; - public static final String RANDOM_SKIN = SETTINGS_KEY + "random_skin"; - } - - public static class Admin { - private static final String ADMIN_KEY = GUI_KEY + "admin."; - - public static final String MANAGE_CACHE = ADMIN_KEY + "manage_cache"; - public static final String MANAGE_PLAYER = ADMIN_KEY + "manage_player"; - public static final String CHECK = ADMIN_KEY + "check"; - - public static class Cache { - private static final String CACHE_KEY = ADMIN_KEY + "cache."; - - public static final String STATISTICS = CACHE_KEY + "statistics"; - public static final String INVALIDATE_CACHE = CACHE_KEY + "invalidate_cache"; - public static final String INVALIDATE_SKIN = CACHE_KEY + "invalidate_skin"; - public static final String ENTRY = CACHE_KEY + "entry"; - } - } - - public static class Favorites { - private static final String FAVORITES_KEY = GUI_KEY + "favorites."; - - public static final String ADD = FAVORITES_KEY + "add"; - public static final String REMOVE = FAVORITES_KEY + "remove"; - public static final String ENTRY = FAVORITES_KEY + "entry"; - } - } -} diff --git a/src/main/java/xyz/ineanto/nicko/language/PlayerLanguage.java b/src/main/java/xyz/ineanto/nicko/language/PlayerLanguage.java deleted file mode 100644 index 68b9e8a..0000000 --- a/src/main/java/xyz/ineanto/nicko/language/PlayerLanguage.java +++ /dev/null @@ -1,183 +0,0 @@ -package xyz.ineanto.nicko.language; - -import com.github.jsixface.YamlConfig; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.xenondevs.invui.item.builder.AbstractItemBuilder; - -import java.io.InputStream; -import java.text.MessageFormat; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class PlayerLanguage { - private final MessageFormat formatter = new MessageFormat(""); - private final Nicko instance = Nicko.getInstance(); - private final Pattern replacementPattern = Pattern.compile("(?ms)\\{\\d+}"); - private final YamlConfig yamlConfig; - private final Language playerLanguage; - - public PlayerLanguage(Player player) { - final Optional optionalProfile = NickoProfile.get(player); - this.playerLanguage = optionalProfile.map(NickoProfile::getLocale).orElse(Language.ENGLISH); - this.yamlConfig = getYamlConfig(); - } - - public PlayerLanguage(Language language) { - this.playerLanguage = language; - this.yamlConfig = getYamlConfig(); - } - - public AbstractItemBuilder translateItem(AbstractItemBuilder item, String key, Object... args) { - final Translation translation = translateAndReplace(key, args); - - // Name serialization - final Component deserializedName = MiniMessage.miniMessage().deserialize(translation.name()); - final String serializedName = LegacyComponentSerializer.legacySection().serialize(deserializedName); - - // Lore serialization - translation.lore().replaceAll(s -> { - final Component deserializedLoreLine = MiniMessage.miniMessage().deserialize(s); - return LegacyComponentSerializer.legacySection().serialize(deserializedLoreLine); - }); - - item.setDisplayName(serializedName); - translation.lore().forEach(item::addLoreLines); - return item; - } - - public Translation translateAndReplace(String key, Object... args) { - final String nameKey = key + ".name"; - final String loreKey = key + ".lore"; - final String name = readString(nameKey); - final ArrayList lore = readList(loreKey); - - if (name == null && lore == null) { - Nicko.getInstance().getLogger().warning(nameKey + " doesn't exists! Is your language file outdated?"); - return new Translation(nameKey, new ArrayList<>(List.of(loreKey))); - } - - // Add all elements to a list - final ArrayList toTranslate = new ArrayList<>(); - if (name != null) { - toTranslate.add(name); - } - if (lore != null && !lore.isEmpty()) { - toTranslate.addAll(lore); - } - - // Set starting index to 0 - int lineIndex = 0; - int replacementIndex = 0; - - // While iterator next value exists/isn't null - final Iterator iterator = toTranslate.iterator(); - while (iterator.hasNext() && iterator.next() != null) { - // Get the current line - final String currentLine = toTranslate.get(lineIndex); - - // If the line doesn't contain {i}, skip it - final Matcher matcher = replacementPattern.matcher(currentLine); - if (!matcher.find()) { - lineIndex++; - continue; - } - - // If it does, replace the content with the args at position replacementIndex - if (replacementIndex < args.length && args[replacementIndex] != null) { - // Replace it with the corresponding varargs index - toTranslate.set(lineIndex, currentLine.replace("{" + replacementIndex + "}", args[replacementIndex].toString())); - replacementIndex++; - } - - // Increment the index - lineIndex++; - } - - if (name == null && !lore.isEmpty()) { - // Empty name, valid lore - return new Translation(null, toTranslate); - } else if (name != null && (lore == null || lore.isEmpty())) { - // Valid name, empty lore - return new Translation(toTranslate.getFirst(), new ArrayList<>(Collections.emptyList())); - } else { - // Valid name, valid lore - return new Translation(toTranslate.getFirst(), new ArrayList<>(toTranslate.subList(1, toTranslate.size()))); - } - } - - public String translate(String key, boolean prefix, Object... arguments) { - final String translation = readStringWithMiniMessage(key); - - try { - formatter.applyPattern(translation); - return (prefix ? getPrefix() + " " : "") + formatter.format(arguments); - } catch (Exception e) { - return (prefix ? getPrefix() + " " : "") + key; - } - } - - public String translateWithWhoosh(String key, Object... arguments) { - final String translation = readStringWithMiniMessage(key); - - try { - formatter.applyPattern(translation); - return getWhoosh() + " " + formatter.format(arguments); - } catch (Exception e) { - return getWhoosh() + " " + key; - } - } - - public String translateWithOops(String key, Object... arguments) { - final String translation = readStringWithMiniMessage(key); - - try { - formatter.applyPattern(translation); - return getOops() + " " + formatter.format(arguments); - } catch (Exception e) { - return getOops() + " " + key; - } - } - - public Component getPrefixComponent() { - return MiniMessage.miniMessage().deserialize(readString(LanguageKey.PREFIX)); - } - - private String readString(String key) { - return yamlConfig.getString(key); - } - - private String getPrefix() { - return LegacyComponentSerializer.legacySection().serialize(getPrefixComponent()); - } - - private String readStringWithMiniMessage(String key) { - return LegacyComponentSerializer.legacySection().serialize(MiniMessage.miniMessage().deserialize(readString(key))); - } - - private ArrayList readList(String key) { - return yamlConfig.getStringList(key); - } - - private String getWhoosh() { - return readStringWithMiniMessage(LanguageKey.WHOOSH); - } - - private String getOops() { - return readStringWithMiniMessage(LanguageKey.OOPS); - } - - private YamlConfig getYamlConfig() { - if (playerLanguage == Language.CUSTOM) { - return instance.getCustomLocale().getYamlFile(); - } else { - final InputStream resource = instance.getResource(playerLanguage.getCode() + ".yml"); - return new YamlConfig(resource); - } - } -} diff --git a/src/main/java/xyz/ineanto/nicko/language/Translation.java b/src/main/java/xyz/ineanto/nicko/language/Translation.java deleted file mode 100644 index 13d295b..0000000 --- a/src/main/java/xyz/ineanto/nicko/language/Translation.java +++ /dev/null @@ -1,5 +0,0 @@ -package xyz.ineanto.nicko.language; - -import java.util.ArrayList; - -public record Translation(String name, ArrayList lore) {} \ No newline at end of file diff --git a/src/main/java/xyz/ineanto/nicko/loader/NickoPluginLoader.java b/src/main/java/xyz/ineanto/nicko/loader/NickoPluginLoader.java deleted file mode 100644 index 11762da..0000000 --- a/src/main/java/xyz/ineanto/nicko/loader/NickoPluginLoader.java +++ /dev/null @@ -1,21 +0,0 @@ -package xyz.ineanto.nicko.loader; - -import io.papermc.paper.plugin.loader.PluginClasspathBuilder; -import io.papermc.paper.plugin.loader.PluginLoader; -import io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver; -import org.eclipse.aether.artifact.DefaultArtifact; -import org.eclipse.aether.graph.Dependency; -import org.eclipse.aether.repository.RemoteRepository; - -@SuppressWarnings("UnstableApiUsage") -public class NickoPluginLoader implements PluginLoader { - @Override - public void classloader(PluginClasspathBuilder pluginClasspathBuilder) { - final MavenLibraryResolver resolver = new MavenLibraryResolver(); - - resolver.addRepository(new RemoteRepository.Builder("xenondevs", "default", "https://repo.xenondevs.xyz/releases/").build()); - resolver.addDependency(new Dependency(new DefaultArtifact("xyz.xenondevs.invui:invui:pom:1.45"), null)); - - pluginClasspathBuilder.addLibrary(resolver); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/migration/ConfigurationMigrator.java b/src/main/java/xyz/ineanto/nicko/migration/ConfigurationMigrator.java deleted file mode 100644 index 9a47db5..0000000 --- a/src/main/java/xyz/ineanto/nicko/migration/ConfigurationMigrator.java +++ /dev/null @@ -1,41 +0,0 @@ -package xyz.ineanto.nicko.migration; - -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.config.Configuration; -import xyz.ineanto.nicko.config.ConfigurationManager; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; - -public class ConfigurationMigrator implements Migrator { - private final Nicko instance; - - public ConfigurationMigrator(Nicko instance) { - this.instance = instance; - } - - @Override - public void migrate() { - final Configuration configuration = instance.getNickoConfig(); - final ConfigurationManager configurationManager = instance.getConfigurationManager(); - - // Migrate configuration (1.0.8-RC1) - if (configuration.getVersion() == null - || configuration.getVersion().isEmpty() - || configuration.getVersionObject().compareTo(Configuration.VERSION) != 0) { - instance.getLogger().info("Migrating configuration file to match the current version..."); - try { - Files.copy(configurationManager.getFile().toPath(), configurationManager.getBackupFile().toPath(), StandardCopyOption.REPLACE_EXISTING); - if (configurationManager.getFile().delete()) { - configurationManager.saveDefaultConfig(); - instance.getLogger().info("Successfully migrated your configuration file!"); - } else { - instance.getLogger().severe("Failed to migrate your configuration!"); - } - } catch (IOException e) { - instance.getLogger().severe("Failed to migrate your configuration!"); - } - } - } -} diff --git a/src/main/java/xyz/ineanto/nicko/migration/CustomLocaleMigrator.java b/src/main/java/xyz/ineanto/nicko/migration/CustomLocaleMigrator.java deleted file mode 100644 index 073df5f..0000000 --- a/src/main/java/xyz/ineanto/nicko/migration/CustomLocaleMigrator.java +++ /dev/null @@ -1,47 +0,0 @@ -package xyz.ineanto.nicko.migration; - -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.language.CustomLanguage; -import xyz.ineanto.nicko.language.Language; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.time.Instant; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; - -public class CustomLocaleMigrator implements Migrator { - private final Nicko instance; - private final CustomLanguage customLanguage; - - public CustomLocaleMigrator(Nicko instance, CustomLanguage customLanguage) { - this.instance = instance; - this.customLanguage = customLanguage; - } - - @Override - public void migrate() { - // Migrate custom locale (1.1.0-RC1) - if (customLanguage.getVersionObject() == null - || customLanguage.getVersion().isEmpty() - || customLanguage.getVersionObject().compareTo(Language.VERSION) != 0) { - instance.getLogger().info("Migrating the custom locale (" + customLanguage.getVersion() + ") to match the current version (" + Language.VERSION + ")..."); - - final String date = Instant.now().atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("dd-MM-yyyy")); - final File backupFile = new File(customLanguage.getDirectory(), "locale-" + date + ".yml"); - - try { - Files.copy(customLanguage.getFile().toPath(), backupFile.toPath()); - if (customLanguage.getFile().delete()) { - CustomLanguage.dumpIntoFile(Language.ENGLISH); - instance.getLogger().info("Successfully migrated the custom locale."); - } else { - instance.getLogger().severe("Failed to migrate the custom locale!"); - } - } catch (IOException e) { - instance.getLogger().severe("Failed to migrate the custom locale!"); - } - } - } -} diff --git a/src/main/java/xyz/ineanto/nicko/migration/Migrator.java b/src/main/java/xyz/ineanto/nicko/migration/Migrator.java deleted file mode 100644 index 89bb64e..0000000 --- a/src/main/java/xyz/ineanto/nicko/migration/Migrator.java +++ /dev/null @@ -1,5 +0,0 @@ -package xyz.ineanto.nicko.migration; - -public interface Migrator { - void migrate(); -} diff --git a/src/main/java/xyz/ineanto/nicko/mojang/MojangAPI.java b/src/main/java/xyz/ineanto/nicko/mojang/MojangAPI.java deleted file mode 100644 index cecf8c9..0000000 --- a/src/main/java/xyz/ineanto/nicko/mojang/MojangAPI.java +++ /dev/null @@ -1,162 +0,0 @@ -package xyz.ineanto.nicko.mojang; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; - -import javax.annotation.Nonnull; -import javax.net.ssl.HttpsURLConnection; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URI; -import java.net.URL; -import java.util.HashMap; -import java.util.Optional; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.logging.Logger; - -public class MojangAPI { - public static final String URL_NAME = "https://api.mojang.com/users/profiles/minecraft/{name}"; - public static final String URL_SKIN = "https://sessionserver.mojang.com/session/minecraft/profile/{uuid}?unsigned=false"; - - private final Logger logger = Logger.getLogger("MojangAPI"); - private final HashMap uuidToName = new HashMap<>(); - private final ExecutorService worker = Executors.newFixedThreadPool(6); - - private final CacheLoader> skinLoader = new CacheLoader<>() { - @Nonnull - public Optional load(@Nonnull String uuid) throws Exception { - return getSkinFromMojang(uuid); - } - }; - - private final LoadingCache> skinCache = CacheBuilder - .newBuilder() - .recordStats() - .expireAfterWrite(24, TimeUnit.HOURS) - .build(skinLoader); - - private final CacheLoader> uuidLoader = new CacheLoader<>() { - @Nonnull - public Optional load(@Nonnull String name) throws Exception { - return getUUIDFromMojang(name); - } - }; - - private final LoadingCache> uuidCache = CacheBuilder - .newBuilder() - .expireAfterWrite(2, TimeUnit.DAYS) - .build(uuidLoader); - - public Optional getSkin(String uuid) throws IOException, ExecutionException { - return skinCache.get(uuid); - } - - public Optional getSkinWithoutCaching(String uuid) throws IOException, ExecutionException, InterruptedException { - return getSkinFromMojang(uuid); - } - - public Optional getUUID(String name) throws IOException, ExecutionException { - return uuidCache.get(name); - } - - private Optional getUUIDFromMojang(String name) throws ExecutionException, InterruptedException { - final String parametrizedUrl = URL_NAME.replace("{name}", name); - final JsonObject object = getRequestToUrl(parametrizedUrl); - if (hasNoError(object)) { - final JsonElement idObject = object.get("id"); - final String uuid = idObject.getAsString(); - final Optional uuidOptional = Optional.of(uuid); - uuidCache.put(name, uuidOptional); - uuidToName.put(uuid, name); - return uuidOptional; - } - return Optional.empty(); - } - - public void eraseFromCache(String uuid) { - skinCache.invalidate(uuid); - uuidToName.remove(uuid); - uuidCache.invalidate(uuid); - } - - private Optional getSkinFromMojang(String uuid) throws ExecutionException, InterruptedException { - final String parametrizedUrl = URL_SKIN.replace("{uuid}", uuid); - final JsonObject object = getRequestToUrl(parametrizedUrl); - if (hasNoError(object)) { - final MojangSkin skin = MojangSkin.buildFromJson(object); - return Optional.of(skin); - } - return Optional.empty(); - } - - public String getUUIDName(String uuid) { - return uuidToName.get(uuid); - } - - private JsonObject getRequestToUrl(String parametrizedUrl) throws ExecutionException, InterruptedException { - return worker.submit(() -> { - final URL url = URI.create(parametrizedUrl).toURL(); - final HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); - - connection.setDoInput(true); - connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0"); - connection.setRequestMethod("GET"); - - switch (connection.getResponseCode()) { - case 403: - logger.warning("The Mojang API denied the request. This should not happen."); - logger.warning("Nicko is NOT responsible for this error. Try again in a few minutes or hours."); - logger.warning("See https://bugs.mojang.com/browse/WEB/issues/WEB-7591 for more info."); - return getErrorObject(); - case 404: - case 400: - logger.warning("Failed to parse request: Invalid Name"); - return getErrorObject(); - case 429: - logger.warning("Failed to parse request: The connection is throttled."); - return getErrorObject(); - case 200: - final BufferedReader input = new BufferedReader(new InputStreamReader(connection.getInputStream())); - final StringBuilder builder = new StringBuilder(); - String line; - while ((line = input.readLine()) != null) { - builder.append(line); - } - - try { - final JsonElement jsonElt = JsonParser.parseString(builder.toString()); - return jsonElt.getAsJsonObject(); - } catch (JsonParseException | IllegalStateException exception) { - logger.warning("Failed to parse request (" + parametrizedUrl + ")!"); - return getErrorObject(); - } - default: - logger.warning("Unhandled response code from Mojang: " + connection.getResponseCode()); - return getErrorObject(); - } - }).get(); - } - - private JsonObject getErrorObject() { - final JsonObject errorObject = new JsonObject(); - errorObject.addProperty("error", "An error occurred."); - return errorObject; - } - - private boolean hasNoError(JsonObject object) { - return object.get("error") == null; - } - - public LoadingCache> getSkinCache() { - return skinCache; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/packet/PacketSender.java b/src/main/java/xyz/ineanto/nicko/packet/PacketSender.java deleted file mode 100644 index 28bb695..0000000 --- a/src/main/java/xyz/ineanto/nicko/packet/PacketSender.java +++ /dev/null @@ -1,17 +0,0 @@ -package xyz.ineanto.nicko.packet; - -import xyz.ineanto.nicko.appearance.ActionResult; - -public interface PacketSender { - void sendEntityRespawn(); - - ActionResult updatePlayerProfile(String name); - - ActionResult updatePlayerProfileProperties(); - - void sendEntityMetadataUpdate(); - - void sendPlayerRespawn(); - - void sendTabListUpdate(String displayName); -} diff --git a/src/main/java/xyz/ineanto/nicko/packet/PaperPacketSender.java b/src/main/java/xyz/ineanto/nicko/packet/PaperPacketSender.java deleted file mode 100644 index ded8f5e..0000000 --- a/src/main/java/xyz/ineanto/nicko/packet/PaperPacketSender.java +++ /dev/null @@ -1,167 +0,0 @@ -package xyz.ineanto.nicko.packet; - -import com.destroystokyo.paper.profile.CraftPlayerProfile; -import com.destroystokyo.paper.profile.PlayerProfile; -import it.unimi.dsi.fastutil.ints.IntList; -import net.minecraft.Optionull; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.network.chat.RemoteChatSession; -import net.minecraft.network.chat.contents.PlainTextContents; -import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.*; -import net.minecraft.network.syncher.EntityDataAccessor; -import net.minecraft.network.syncher.EntityDataSerializers; -import net.minecraft.network.syncher.SynchedEntityData; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.phys.Vec3; -import org.bukkit.Bukkit; -import org.bukkit.craftbukkit.entity.CraftPlayer; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.appearance.ActionResult; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.mojang.MojangAPI; -import xyz.ineanto.nicko.mojang.MojangSkin; -import xyz.ineanto.nicko.profile.NickoProfile; - -import java.io.IOException; -import java.util.EnumSet; -import java.util.List; -import java.util.Optional; -import java.util.Random; -import java.util.concurrent.ExecutionException; - -/** - * Look at this Mojang. - * I want you to really stare at this code. - * You made me do this. - */ -public class PaperPacketSender implements PacketSender { - private final Player player; - private final NickoProfile profile; - - public PaperPacketSender(Player player, NickoProfile profile) { - this.player = player; - this.profile = profile; - } - - @Override - public void sendEntityRespawn() { - if (!profile.hasData()) return; - - final ClientboundRemoveEntitiesPacket destroy = new ClientboundRemoveEntitiesPacket(IntList.of(player.getEntityId())); - final ClientboundAddEntityPacket add = new ClientboundAddEntityPacket( - new Random().nextInt(9999), - player.getUniqueId(), - player.getX(), - player.getY(), - player.getZ(), - player.getPitch(), - player.getYaw(), - EntityType.PLAYER, - 0, - Vec3.ZERO, - player.getBodyYaw() - ); - - Bukkit.getOnlinePlayers().stream().filter(receiver -> receiver.getUniqueId() != player.getUniqueId()).forEach(receiver -> { - sendPacket(destroy, player); - sendPacket(add, player); - }); - } - - @Override - public ActionResult updatePlayerProfile(String name) { - final PlayerProfile playerProfile = new CraftPlayerProfile(player.getUniqueId(), name); - // Copy previous properties to preserve skin - playerProfile.setProperties(playerProfile.getProperties()); - player.setPlayerProfile(playerProfile); - return ActionResult.ok(); - } - - @Override - public ActionResult updatePlayerProfileProperties() { - final PlayerProfile playerProfile = new CraftPlayerProfile(player.getUniqueId(), profile.getName() == null ? player.getName() : profile.getName()); - - try { - final MojangAPI mojangAPI = Nicko.getInstance().getMojangAPI(); - - final Optional uuid = mojangAPI.getUUID(profile.getSkin()); - if (uuid.isEmpty()) { - return ActionResult.error(LanguageKey.Error.MOJANG); - } - - final Optional skin = mojangAPI.getSkin(uuid.get()); - if (skin.isEmpty()) { - return ActionResult.error(LanguageKey.Error.MOJANG); - } - - final MojangSkin skinResult = skin.get(); - playerProfile.setProperties(skinResult.asProfileProperties()); - player.setPlayerProfile(playerProfile); - return ActionResult.ok(); - } catch (ExecutionException | IOException e) { - return ActionResult.error(LanguageKey.Error.CACHE); - } - } - - @Override - public void sendEntityMetadataUpdate() { - final SynchedEntityData.DataValue dataValueComponent = - new SynchedEntityData.DataItem<>( - new EntityDataAccessor<>(17, EntityDataSerializers.BYTE), - (byte) 0x7f - ).value(); - - final ClientboundSetEntityDataPacket data = new ClientboundSetEntityDataPacket(player.getEntityId(), List.of(dataValueComponent)); - sendPacket(data, player); - } - - @Override - public void sendPlayerRespawn() { - final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); - final ServerLevel level = serverPlayer.serverLevel(); - - final ClientboundRespawnPacket respawn = new ClientboundRespawnPacket(serverPlayer.createCommonSpawnInfo(level), (byte) 0x03); - sendPacket(respawn, player); - } - - @Override - public void sendTabListUpdate(String displayName) { - final ServerPlayer serverPlayer = (((CraftPlayer) player)).getHandle(); - - final EnumSet actions = EnumSet.of( - ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, - ClientboundPlayerInfoUpdatePacket.Action.INITIALIZE_CHAT, - ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED, - ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME, - ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, - ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY); - - final List entries = List.of(new ClientboundPlayerInfoUpdatePacket.Entry( - serverPlayer.getUUID(), - serverPlayer.gameProfile, - true, - serverPlayer.connection.latency(), - serverPlayer.gameMode.getGameModeForPlayer(), - MutableComponent.create(new PlainTextContents.LiteralContents(displayName)), - true, - serverPlayer.getTabListOrder(), - Optionull.map(serverPlayer.getChatSession(), RemoteChatSession::asData) - )); - - final ClientboundPlayerInfoUpdatePacket update = new ClientboundPlayerInfoUpdatePacket(actions, entries); - final ClientboundPlayerInfoRemovePacket remove = new ClientboundPlayerInfoRemovePacket(List.of(player.getUniqueId())); - - Bukkit.getOnlinePlayers().forEach(onlinePlayer -> { - sendPacket(remove, onlinePlayer); - sendPacket(update, onlinePlayer); - }); - } - - private void sendPacket(Packet packet, Player player) { - (((CraftPlayer) player).getHandle()).connection.send(packet); - } -} \ No newline at end of file diff --git a/src/main/java/xyz/ineanto/nicko/packet/debug/RespawnPacketListener.java b/src/main/java/xyz/ineanto/nicko/packet/debug/RespawnPacketListener.java deleted file mode 100644 index 5222dd0..0000000 --- a/src/main/java/xyz/ineanto/nicko/packet/debug/RespawnPacketListener.java +++ /dev/null @@ -1,82 +0,0 @@ -package xyz.ineanto.nicko.packet.debug; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.ListeningWhitelist; -import com.comphenix.protocol.events.PacketEvent; -import com.comphenix.protocol.events.PacketListener; -import com.comphenix.protocol.wrappers.MinecraftKey; -import net.kyori.adventure.text.Component; -import org.bukkit.Bukkit; -import org.bukkit.NamespacedKey; -import org.bukkit.World; -import org.bukkit.plugin.Plugin; -import xyz.ineanto.nicko.Nicko; - -import java.util.Arrays; -import java.util.Optional; - -public class RespawnPacketListener implements PacketListener { - @Override - public void onPacketSending(PacketEvent event) { - try { - final Optional world = getWorld(event); - if (world.isEmpty()) { - Bukkit.broadcast(Component.text("did not find the world the player was in")); - return; - } - - Bukkit.broadcast(Component.text("found " + world.get().getName() + "!")); - } catch (Throwable e) { - throw new RuntimeException(e); - } - } - - @Override - public void onPacketReceiving(PacketEvent event) { - - } - - @Override - public ListeningWhitelist getSendingWhitelist() { - return ListeningWhitelist.newBuilder().types(PacketType.Play.Server.RESPAWN).build(); - } - - @Override - public ListeningWhitelist getReceivingWhitelist() { - return ListeningWhitelist.EMPTY_WHITELIST; - } - - @Override - public Plugin getPlugin() { - return Nicko.getInstance(); - } - - private boolean keysEquals(MinecraftKey wrappedKey, NamespacedKey bukkitKey) { - // compare bukkit minecraft key and NMS wrapped minecraft key - return wrappedKey.getPrefix().equals(bukkitKey.getNamespace()) && wrappedKey.getKey().equals(bukkitKey.getKey()); - } - - public Optional getWorld(PacketEvent event) throws Throwable { - // access CommonPlayerSpawnInfo, first field of that type in the Respawn / Login packets - final Object packetHandle = event.getPacket().getHandle(); - final Object commonSpawnData = packetHandle.getClass().getRecordComponents()[0].getAccessor().invoke(packetHandle); - - Arrays.stream(commonSpawnData.getClass().getRecordComponents()).forEach(component -> { - component.getAccessor().setAccessible(true); - System.out.println("-=-=-=-=-=-=-=-=-=-=-=-=-=-"); - System.out.println(component.getName()); - System.out.println(component.getType().getSimpleName()); - component.getAccessor().setAccessible(false); - } - ); - - // get the key of the level the player is joining. Second field in the object. First of type ResourceKey - final MinecraftKey key = MinecraftKey.fromHandle(commonSpawnData.getClass().getRecordComponents()[1]); - for (World world : Bukkit.getWorlds()) { - if (keysEquals(key, world.getKey())) { - return Optional.of(world); - } - } - return Optional.empty(); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/packet/wrapper/AbstractPacket.java b/src/main/java/xyz/ineanto/nicko/packet/wrapper/AbstractPacket.java deleted file mode 100644 index 42e138b..0000000 --- a/src/main/java/xyz/ineanto/nicko/packet/wrapper/AbstractPacket.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * PacketWrapper - ProtocolLib wrappers for Minecraft packets - * Copyright (C) dmulloy2 - * Copyright (C) Kristian S. Strangeland - *

- * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - *

- * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *

- * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package xyz.ineanto.nicko.packet.wrapper; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.ProtocolLibrary; -import com.comphenix.protocol.events.PacketContainer; -import com.google.common.base.Objects; -import org.bukkit.entity.Player; - -public abstract class AbstractPacket { - protected PacketContainer handle; - - /** - * Constructs a new strongly typed wrapper for the given packet. - * - * @param handle - handle to the raw packet data. - * @param type - the packet type. - */ - protected AbstractPacket(PacketContainer handle, PacketType type) { - // Make sure we're given a valid packet - if (handle == null) - throw new IllegalArgumentException("Packet handle cannot be NULL."); - if (!Objects.equal(handle.getType(), type)) - throw new IllegalArgumentException(handle.getHandle() - + " is not a packet of type " + type); - - this.handle = handle; - } - - /** - * Retrieve a handle to the raw packet data. - * - * @return Raw packet data. - */ - public PacketContainer getHandle() { - return handle; - } - - /** - * Send the current packet to the given receiver. - * - * @param receiver - the receiver. - * @throws RuntimeException If the packet cannot be sent. - */ - public void sendPacket(Player receiver) { - ProtocolLibrary.getProtocolManager().sendServerPacket(receiver, - getHandle()); - } - - /** - * Send the current packet to all online players. - */ - public void broadcastPacket() { - ProtocolLibrary.getProtocolManager().broadcastServerPacket(getHandle()); - } -} \ No newline at end of file diff --git a/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayServerEntityDestroy.java b/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayServerEntityDestroy.java deleted file mode 100644 index a23f1a6..0000000 --- a/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayServerEntityDestroy.java +++ /dev/null @@ -1,32 +0,0 @@ -package xyz.ineanto.nicko.packet.wrapper; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.wrappers.Converters; -import it.unimi.dsi.fastutil.ints.IntList; - -/** - * Sent by the server to the client to remove one or more entities. - */ -public class WrapperPlayServerEntityDestroy extends AbstractPacket { - - /** - * The packet type that is wrapped by this wrapper. - */ - public static final PacketType TYPE = PacketType.Play.Server.ENTITY_DESTROY; - - public WrapperPlayServerEntityDestroy() { - super(new PacketContainer(TYPE), TYPE); - handle.getModifier().writeDefaults(); - } - - /** - * Sets the list of entity ids to remove - * - * @param value New value for field 'entityIds' - */ - public void setEntityIds(IntList value) { - this.handle.getModifier().withType(IntList.class, Converters.passthrough(IntList.class)).writeSafely(0, value); - } - -} \ No newline at end of file diff --git a/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayServerRespawn.java b/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayServerRespawn.java deleted file mode 100644 index f746450..0000000 --- a/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayServerRespawn.java +++ /dev/null @@ -1,147 +0,0 @@ -package xyz.ineanto.nicko.packet.wrapper; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.InternalStructure; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.reflect.FuzzyReflection; -import com.comphenix.protocol.reflect.StructureModifier; -import com.comphenix.protocol.reflect.accessors.Accessors; -import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; -import com.comphenix.protocol.utility.MinecraftReflection; -import com.comphenix.protocol.utility.MinecraftVersion; -import com.comphenix.protocol.wrappers.BukkitConverters; -import com.comphenix.protocol.wrappers.EnumWrappers; -import com.comphenix.protocol.wrappers.MinecraftKey; -import com.google.common.hash.Hashing; -import org.bukkit.Bukkit; -import org.bukkit.GameMode; -import org.bukkit.World; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.RecordComponent; -import java.util.Arrays; - -/** - * PacketPlayServerRespawn Wrapper class (1.20.X to 1.21.X) - *

- * In 1.20.2, all the fields were merged inside a - * single "CommonPlayerSpawnInfo" record. - * - * @author inenato (w/ additional help from lukalt), based on work from dmulloy2 and Kristian S. Strangeland - */ -public class WrapperPlayServerRespawn extends AbstractPacket { - public static final PacketType TYPE = PacketType.Play.Server.RESPAWN; - - private InternalStructure spawnInfoStructure = null; - - public WrapperPlayServerRespawn() { - super(new PacketContainer(TYPE), TYPE); - handle.getModifier().writeDefaults(); - if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) { - spawnInfoStructure = handle.getStructures().read(0); - } - } - - public void setDimension(World value) { - final MinecraftVersion v1_20_5 = new MinecraftVersion(1, 20, 5); - - if (!MinecraftVersion.getCurrentVersion().isAtLeast(v1_20_5)) { - // 1.20 - 1.20.4 - final StructureModifier structureModifier = spawnInfoStructure == null ? - handle.getStructures() : spawnInfoStructure.getStructures(); - - final StructureModifier worldStructureModifier = spawnInfoStructure == null ? - handle.getWorldKeys() : spawnInfoStructure.getWorldKeys(); - - final InternalStructure dimensionType = structureModifier.read(0); - dimensionType.getMinecraftKeys().writeSafely(0, new MinecraftKey("minecraft", "dimension_type")); - dimensionType.getMinecraftKeys().writeSafely(1, new MinecraftKey("minecraft", "overworld")); - structureModifier.writeSafely(0, dimensionType); - worldStructureModifier.writeSafely(0, value); - } else { - // 1.20.5 to 1.21.1 - - try { - final Class spawnInfoClass = MinecraftReflection.getMinecraftClass("network.protocol.game.CommonPlayerSpawnInfo"); - - Class[] componentTypes = Arrays.stream(spawnInfoClass.getRecordComponents()) - .map(RecordComponent::getType) - .toArray(Class[]::new); - final Constructor spawnInfoConstructor = spawnInfoClass.getDeclaredConstructor(componentTypes); - - /** - * Holder dimensionType, - * ResourceKey dimension, - * long seed, - * GameType gameType, - * GameType previousGameType, - * boolean isDebug, - * boolean isFlat, - * Optional lastDeathLocation, - * int portalCooldown - */ - - final World world = Bukkit.getWorld("world"); - - FuzzyReflection.fromClass(spawnInfoClass).getConstructor( - FuzzyMethodContract - .newBuilder() - .build() - ); - - final Object spawnInfo = spawnInfoConstructor.newInstance( - BukkitConverters.getDimensionConverter().getGeneric(world), - BukkitConverters.getWorldKeyConverter().getGeneric(world), - world.getSeed(), - EnumWrappers.getGameModeConverter().getGeneric(EnumWrappers.NativeGameMode.fromBukkit(GameMode.SURVIVAL)), - EnumWrappers.getGameModeConverter().getGeneric(EnumWrappers.NativeGameMode.fromBukkit(GameMode.SURVIVAL)), - false, - false, - BukkitConverters.getSectionPositionConverter() - ); - - final Field commonSpawnDataField = Accessors.getFieldAccessor(TYPE.getPacketClass(), spawnInfoClass, true).getField(); - commonSpawnDataField.set(spawnInfoStructure.getHandle(), spawnInfo); - } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException | - InstantiationException e) { - throw new RuntimeException(); - } - } - } - - public void setGameMode(GameMode value) { - if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) { - // 1.20 to 1.20.1 - handle.getGameModes().writeSafely(0, EnumWrappers.NativeGameMode.fromBukkit(value)); - return; - } - - spawnInfoStructure.getGameModes().writeSafely(0, EnumWrappers.NativeGameMode.fromBukkit(value)); - } - - public void setPreviousGameMode(GameMode value) { - if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) { - // 1.20 to 1.20.1 - handle.getGameModes().writeSafely(1, EnumWrappers.NativeGameMode.fromBukkit(value)); - return; - } - - spawnInfoStructure.getGameModes().writeSafely(1, EnumWrappers.NativeGameMode.fromBukkit(value)); - } - - public void setCopyMetadata(boolean value) { - if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) return; - - // 1.20 to 1.20.1 - handle.getBooleans().writeSafely(0, value); - } - - public void setSeed(long value) { - if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) { - // 1.20 to 1.20.1 - handle.getLongs().writeSafely(0, Hashing.sha256().hashLong(value).asLong()); - } - } -} diff --git a/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayServerSpawnEntity.java b/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayServerSpawnEntity.java deleted file mode 100644 index 0512717..0000000 --- a/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayServerSpawnEntity.java +++ /dev/null @@ -1,103 +0,0 @@ -package xyz.ineanto.nicko.packet.wrapper; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketContainer; -import org.bukkit.Location; -import org.bukkit.entity.EntityType; - -import javax.annotation.Nonnull; -import java.util.UUID; - -/** - * This packet is sent by the server when a player comes into visible range, not when a player joins. - */ -public class WrapperPlayServerSpawnEntity extends AbstractPacket { - /** - * The packet type that is wrapped by this wrapper. - */ - public static final PacketType TYPE = PacketType.Play.Server.SPAWN_ENTITY; - - /** - * Constructors a new wrapper for the specified packet - */ - public WrapperPlayServerSpawnEntity() { - super(new PacketContainer(TYPE), TYPE); - handle.getModifier().writeDefaults(); - } - - /** - * Sets the entity id of the player - * - * @param value New value for field 'entityId' - */ - public void setEntityId(int value) { - this.handle.getIntegers().writeSafely(0, value); - this.handle.getEntityTypeModifier().writeSafely(0, EntityType.PLAYER); - } - - /** - * Sets the unique id of the player - * - * @param value New value for field 'playerId' - */ - public void setPlayerId(UUID value) { - this.handle.getUUIDs().writeSafely(0, value); - } - - /** - * Sets the value of field 'x' - * - * @param value New value for field 'x' - */ - public void setX(double value) { - this.handle.getDoubles().writeSafely(0, value); - } - - /** - * Sets the value of field 'y' - * - * @param value New value for field 'y' - */ - public void setY(double value) { - this.handle.getDoubles().writeSafely(1, value); - } - - /** - * Sets the value of field 'z' - * - * @param value New value for field 'z' - */ - public void setZ(double value) { - this.handle.getDoubles().write(2, value); - } - - /** - * Sets the discrete rotation around the y-axis (yaw) - * - * @param value New value for field 'yRot' - */ - public void setYRotRaw(byte value) { - this.handle.getBytes().writeSafely(0, value); - } - - /** - * Sets the discrete rotation around the x-axis (pitch) - * - * @param value New value for field 'xRot' - */ - public void setXRotRaw(byte value) { - this.handle.getBytes().writeSafely(1, value); - } - - public void setLocation(@Nonnull Location location) { - setX(location.getX()); - setY(location.getY()); - setZ(location.getZ()); - setYRotRaw(degreesToAngle(location.getYaw())); - setXRotRaw(degreesToAngle(location.getPitch())); - } - - private byte degreesToAngle(float degree) { - return (byte)((int)(degree * 256.0F / 360.0F)); - } -} \ No newline at end of file diff --git a/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayerServerPlayerInfo.java b/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayerServerPlayerInfo.java deleted file mode 100644 index ca37dc0..0000000 --- a/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayerServerPlayerInfo.java +++ /dev/null @@ -1,37 +0,0 @@ -package xyz.ineanto.nicko.packet.wrapper; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.utility.MinecraftVersion; -import com.comphenix.protocol.wrappers.EnumWrappers; -import com.comphenix.protocol.wrappers.PlayerInfoData; - -import java.util.List; -import java.util.Set; - -/** - * Up-to-date version of the Wrapper class - * for the PlayerServerPlayerInfo. - * - * @author ineanto, based on work from dmulloy2 and Kristian S. Strangeland - */ -public class WrapperPlayerServerPlayerInfo extends AbstractPacket { - public static final PacketType TYPE = PacketType.Play.Server.PLAYER_INFO; - - public WrapperPlayerServerPlayerInfo() { - super(new PacketContainer(TYPE), TYPE); - handle.getModifier().writeDefaults(); - } - - public void setActions(Set value) { - if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) { - handle.getPlayerInfoActions().writeSafely(0, value); - } else { - handle.getPlayerInfoAction().writeSafely(0, value.stream().iterator().next()); // Get the first Value. - } - } - - public void setData(List value) { - handle.getPlayerInfoDataLists().writeSafely(1, value); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayerServerPlayerInfoRemove.java b/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayerServerPlayerInfoRemove.java deleted file mode 100644 index 2273c48..0000000 --- a/src/main/java/xyz/ineanto/nicko/packet/wrapper/WrapperPlayerServerPlayerInfoRemove.java +++ /dev/null @@ -1,27 +0,0 @@ -package xyz.ineanto.nicko.packet.wrapper; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketContainer; - -import java.util.List; -import java.util.UUID; - -/** - * Up-to-date version of the Wrapper class - * for the PlayerServerPlayerInfoRemove. - * - * @author ineanto, based on work from dmulloy2 and Kristian S. Strangeland - */ - -public class WrapperPlayerServerPlayerInfoRemove extends AbstractPacket { - public static final PacketType TYPE = PacketType.Play.Server.PLAYER_INFO_REMOVE; - - public WrapperPlayerServerPlayerInfoRemove() { - super(new PacketContainer(TYPE), TYPE); - handle.getModifier().writeDefaults(); - } - - public void setUUIDs(List value) { - handle.getUUIDLists().writeSafely(0, value); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/profile/NickoProfile.java b/src/main/java/xyz/ineanto/nicko/profile/NickoProfile.java deleted file mode 100644 index b1a78bb..0000000 --- a/src/main/java/xyz/ineanto/nicko/profile/NickoProfile.java +++ /dev/null @@ -1,103 +0,0 @@ -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( - new Appearance(null, null), - Language.ENGLISH, - true, - Collections.emptyList() - ); - - private static final Nicko instance = Nicko.getInstance(); - private static final PlayerDataStore dataStore = instance.getDataStore(); - - private Appearance appearance; - private Language language; - private boolean randomSkin; - private List favorites; - - 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) { - return dataStore.getData(player.getUniqueId()); - } - - public static Optional get(UUID uuid) { - return dataStore.getData(uuid); - } - - public Appearance getAppearance() { - return appearance; - } - - public boolean hasData() { - return appearance.name() != null || appearance.skin() != null; - } - - public String getName() { - return appearance.name(); - } - - public void setName(String name) { - this.appearance = new Appearance(name, appearance.skin() == null ? null : appearance.skin()); - } - - public String getSkin() { - return appearance.skin(); - } - - public void setSkin(String 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() { - return language; - } - - public void setLocale(Language language) { - this.language = language; - } - - public boolean isRandomSkin() { - return randomSkin; - } - - public void setRandomSkin(boolean randomSkin) { - this.randomSkin = randomSkin; - } - - @Override - public NickoProfile clone() { - Object o; - try { - o = super.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - return (NickoProfile) o; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/prompt/Prompt.java b/src/main/java/xyz/ineanto/nicko/prompt/Prompt.java deleted file mode 100644 index 5940993..0000000 --- a/src/main/java/xyz/ineanto/nicko/prompt/Prompt.java +++ /dev/null @@ -1,63 +0,0 @@ -package xyz.ineanto.nicko.prompt; - -import org.bukkit.Sound; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.appearance.ActionResult; -import xyz.ineanto.nicko.appearance.AppearanceManager; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.language.PlayerLanguage; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.ineanto.nicko.storage.PlayerDataStore; - -import javax.annotation.Nullable; -import java.util.Optional; - -public abstract class Prompt { - private final Player player; - private final AppearanceManager appearanceManager; - private final NickoProfile profile; - private final PlayerDataStore dataStore = Nicko.getInstance().getDataStore(); - - protected final PlayerLanguage playerLanguage; - - public Prompt(Player player) { - this.player = player; - this.appearanceManager = new AppearanceManager(player); - this.playerLanguage = new PlayerLanguage(player); - - final Optional optionalProfile = dataStore.getData(player.getUniqueId()); - this.profile = optionalProfile.orElse(NickoProfile.EMPTY_PROFILE.clone()); - } - - public abstract void displayNameThenSkinPrompt(); - - public abstract void displaySkinPrompt(); - - public abstract void displayNamePrompt(); - - public void update(@Nullable String name, @Nullable String skin, boolean skinChange) { - if (name != null && !name.isBlank()) { - profile.setName(name); - } - - if (skin != null && !skin.isBlank()) { - profile.setSkin(skin); - } - - dataStore.updateCache(player.getUniqueId(), profile); - - final ActionResult actionResult = appearanceManager.update(skinChange); - if (!actionResult.isError()) { - player.sendMessage(playerLanguage.translateWithWhoosh(LanguageKey.Event.Appearance.Set.OK)); - player.playSound(player.getLocation(), Sound.BLOCK_WOODEN_BUTTON_CLICK_ON, 1f, 1f); - } else { - player.sendMessage( - playerLanguage.translateWithOops( - LanguageKey.Event.Appearance.Set.ERROR, - playerLanguage.translate(actionResult.getErrorKey(), false) - )); - player.playSound(player.getLocation(), Sound.BLOCK_ANVIL_PLACE, 1f, 1f); - } - } -} diff --git a/src/main/java/xyz/ineanto/nicko/prompt/PromptManager.java b/src/main/java/xyz/ineanto/nicko/prompt/PromptManager.java deleted file mode 100644 index bbd5a9e..0000000 --- a/src/main/java/xyz/ineanto/nicko/prompt/PromptManager.java +++ /dev/null @@ -1,24 +0,0 @@ -package xyz.ineanto.nicko.prompt; - -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.prompt.conversation.ConversationPrompt; - -public class PromptManager { - private final Prompt prompt; - - public PromptManager(Player player) { - this.prompt = new ConversationPrompt(player); - } - - public void displayNameThenSkinPrompt() { - prompt.displayNameThenSkinPrompt(); - } - - public void displaySkinPrompt() { - prompt.displaySkinPrompt(); - } - - public void displayNamePromptThenUpdate() { - prompt.displayNamePrompt(); - } -} \ No newline at end of file diff --git a/src/main/java/xyz/ineanto/nicko/prompt/anvil/AnvilPrompt.java b/src/main/java/xyz/ineanto/nicko/prompt/anvil/AnvilPrompt.java deleted file mode 100644 index 85e6d28..0000000 --- a/src/main/java/xyz/ineanto/nicko/prompt/anvil/AnvilPrompt.java +++ /dev/null @@ -1,105 +0,0 @@ -package xyz.ineanto.nicko.prompt.anvil; - -import net.kyori.adventure.text.Component; -import net.wesjd.anvilgui.AnvilGUI; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.prompt.Prompt; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.mojang.MojangUtils; - -import java.util.Collections; - -/** - * This is currently unused, I'm waiting on AnvilGUI - * to be compiled against Paper mappings. - */ -// TODO (Ineanto, 16/05/2025): Do some validation on the inputs -public class AnvilPrompt extends Prompt { - private final Player player; - - private String name; - private String skin; - - public AnvilPrompt(Player player) { - super(player); - this.player = player; - } - - @Override - public void displayNameThenSkinPrompt() { - new AnvilGUI.Builder() - .plugin(Nicko.getInstance()) - .itemLeft(getLeftItem(false)) - .interactableSlots(AnvilGUI.Slot.OUTPUT) - .onClick((slot, snapshot) -> { - if (slot == AnvilGUI.Slot.OUTPUT) { - if (MojangUtils.isUsernameInvalid(snapshot.getText())) { - return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!")); - } else { - // Praying that it works. This is untested code! - name = snapshot.getText(); - displaySkinPrompt(); - } - } - return Collections.emptyList(); - }) - .text("New name...").open(player); - } - - @Override - public void displaySkinPrompt() { - new AnvilGUI.Builder() - .plugin(Nicko.getInstance()) - .itemLeft(getLeftItem(true)) - .interactableSlots(AnvilGUI.Slot.OUTPUT) - .onClick((slot, snapshot) -> { - if (slot == AnvilGUI.Slot.OUTPUT) { - if (MojangUtils.isUsernameInvalid(snapshot.getText())) { - return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!")); - } else { - skin = snapshot.getText(); - update(name == null ? null : name, skin, true); - return Collections.singletonList(AnvilGUI.ResponseAction.close()); - } - } - return Collections.emptyList(); - }) - .text("New skin...").open(player); - } - - @Override - public void displayNamePrompt() { - new AnvilGUI.Builder() - .plugin(Nicko.getInstance()) - .itemLeft(getLeftItem(false)) - .interactableSlots(AnvilGUI.Slot.OUTPUT) - .onClick((slot, snapshot) -> { - if (slot == AnvilGUI.Slot.OUTPUT) { - if (MojangUtils.isUsernameInvalid(snapshot.getText())) { - return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!")); - } else { - update(snapshot.getText(), null, false); - return Collections.singletonList(AnvilGUI.ResponseAction.close()); - } - } - return Collections.emptyList(); - }) - .text("New name...").open(player); - } - - private ItemStack getLeftItem(boolean skin) { - final ItemStack item = new ItemStack(Material.PAPER); - final ItemMeta meta = item.getItemMeta(); - - if (meta != null) { - meta.displayName(Component.text(playerLanguage.translate(skin ? LanguageKey.GUI.NEW_SKIN : LanguageKey.GUI.NEW_NAME, false))); - } - - item.setItemMeta(meta); - return item; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/prompt/conversation/ConversationPrompt.java b/src/main/java/xyz/ineanto/nicko/prompt/conversation/ConversationPrompt.java deleted file mode 100644 index 701ce6c..0000000 --- a/src/main/java/xyz/ineanto/nicko/prompt/conversation/ConversationPrompt.java +++ /dev/null @@ -1,89 +0,0 @@ -package xyz.ineanto.nicko.prompt.conversation; - -import org.bukkit.conversations.ConversationContext; -import org.bukkit.conversations.ConversationFactory; -import org.bukkit.conversations.StringPrompt; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.prompt.Prompt; -import xyz.ineanto.nicko.language.LanguageKey; - -import java.util.Map; -import java.util.Objects; - -public class ConversationPrompt extends Prompt { - private final String changeBothTag = "changeBoth"; - private final Player player; - private final ConversationFactory conversationFactory = new ConversationFactory(Nicko.getInstance()) - .withTimeout(30) - .withModality(false) - .withEscapeSequence("EXIT") - .withLocalEcho(false) - .thatExcludesNonPlayersWithMessage("Player only"); - - private String name; - - public ConversationPrompt(Player player) { - super(player); - this.player = player; - } - - @Override - public void displayNameThenSkinPrompt() { - conversationFactory - .withFirstPrompt(new ChangeNameConversation()) - .withInitialSessionData(Map.of(changeBothTag, true)) - .buildConversation(player) - .begin(); - } - - @Override - public void displaySkinPrompt() { - conversationFactory - .withFirstPrompt(new ChangeSkinConversation()) - .buildConversation(player) - .begin(); - } - - @Override - public void displayNamePrompt() { - conversationFactory - .withFirstPrompt(new ChangeNameConversation()) - .buildConversation(player) - .begin(); - } - - private class ChangeNameConversation extends StringPrompt { - @Override - public @NotNull String getPromptText(@NotNull ConversationContext context) { - return playerLanguage.translate(LanguageKey.Event.Appearance.Set.CHAT_PROMPT_NAME, true); - } - - @Override - public @Nullable org.bukkit.conversations.Prompt acceptInput(@NotNull ConversationContext context, @Nullable String input) { - if (Objects.equals(context.getSessionData(changeBothTag), true)) { - name = input; - return new ChangeSkinConversation(); - } - - update(input, null, false); - return END_OF_CONVERSATION; - } - } - - private class ChangeSkinConversation extends StringPrompt { - @Override - public @NotNull String getPromptText(@NotNull ConversationContext context) { - return playerLanguage.translate(LanguageKey.Event.Appearance.Set.CHAT_PROMPT_SKIN, true); - } - - @Override - public @Nullable org.bukkit.conversations.Prompt acceptInput(@NotNull ConversationContext context, @Nullable String input) { - update(name != null ? name : null, input, true); - name = null; - return END_OF_CONVERSATION; - } - } -} diff --git a/src/main/java/xyz/ineanto/nicko/storage/PlayerDataStore.java b/src/main/java/xyz/ineanto/nicko/storage/PlayerDataStore.java deleted file mode 100644 index 2ba648f..0000000 --- a/src/main/java/xyz/ineanto/nicko/storage/PlayerDataStore.java +++ /dev/null @@ -1,106 +0,0 @@ -package xyz.ineanto.nicko.storage; - -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.appearance.ActionResult; -import xyz.ineanto.nicko.config.Configuration; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.mojang.MojangAPI; -import xyz.ineanto.nicko.mojang.MojangUtils; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.ineanto.nicko.storage.json.JSONStorage; -import xyz.ineanto.nicko.storage.map.MapCache; -import xyz.ineanto.nicko.storage.mariadb.MariaDBStorage; -import xyz.ineanto.nicko.storage.mysql.MySQLStorage; -import xyz.ineanto.nicko.storage.redis.RedisCache; - -import java.io.IOException; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.ExecutionException; - -public class PlayerDataStore { - private final MojangAPI mojangAPI; - - private Storage storage; - private Cache cache; - - public PlayerDataStore(MojangAPI mojangAPI, Configuration configuration) { - this.mojangAPI = mojangAPI; - this.storage = configuration.getSqlConfiguration().isEnabled() ? - configuration.getSqlConfiguration().isMariadb() ? new MariaDBStorage(configuration) : new MySQLStorage(configuration) - : new JSONStorage(); - this.cache = configuration.getRedisConfiguration().isEnabled() ? new RedisCache(configuration) : new MapCache(); - } - - public ActionResult updateCache(UUID uuid, NickoProfile profile) { - if (storage.isError() || cache.isError()) { - return ActionResult.error(LanguageKey.Error.CACHE); - } - - getCache().cache(uuid, profile); - return ActionResult.ok(); - } - - public Optional getData(UUID uuid) { - if (storage.isError() || cache.isError()) { - return Optional.empty(); - } - - if (cache.isCached(uuid)) { - return cache.retrieve(uuid); - } else if (storage.isStored(uuid)) { - final Optional retrievedProfile = storage.retrieve(uuid); - retrievedProfile.ifPresent(profile -> cache.cache(uuid, profile)); - return retrievedProfile; - } else { - final NickoProfile newProfile = NickoProfile.EMPTY_PROFILE.clone(); - cache.cache(uuid, newProfile); - return Optional.of(newProfile); - } - } - - public Optional getOfflineData(String name) { - if (storage.isError() || cache.isError()) { - return Optional.empty(); - } - - try { - final Optional uuidTrimmed = mojangAPI.getUUID(name); - if (uuidTrimmed.isPresent()) { - final UUID uuid = MojangUtils.fromTrimmed(uuidTrimmed.get()); - return getData(uuid); - } - return Optional.empty(); - } catch (IOException | ExecutionException e) { - return Optional.empty(); - } - } - - public ActionResult saveData(Player player) { - if (storage.isError()) return ActionResult.error(); - if (cache.isError()) return ActionResult.error(); - if (!cache.isCached(player.getUniqueId())) return ActionResult.error(); - - final Optional cachedProfile = cache.retrieve(player.getUniqueId()); - if (cachedProfile.isEmpty()) return ActionResult.error(); - - cache.delete(player.getUniqueId()); - return storage.store(player.getUniqueId(), cachedProfile.get()); - } - - public Storage getStorage() { - return storage; - } - - public void setStorage(Storage storage) { - this.storage = storage; - } - - public Cache getCache() { - return cache; - } - - public void setCache(Cache cache) { - this.cache = cache; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/storage/map/MapCache.java b/src/main/java/xyz/ineanto/nicko/storage/map/MapCache.java deleted file mode 100644 index ebc3a88..0000000 --- a/src/main/java/xyz/ineanto/nicko/storage/map/MapCache.java +++ /dev/null @@ -1,51 +0,0 @@ -package xyz.ineanto.nicko.storage.map; - -import xyz.ineanto.nicko.appearance.ActionResult; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.ineanto.nicko.storage.Cache; -import xyz.ineanto.nicko.storage.CacheProvider; - -import java.util.HashMap; -import java.util.Optional; -import java.util.UUID; - -public class MapCache extends Cache { - private MapCacheProvider provider; - - @Override - public CacheProvider getProvider() { - if (provider == null) { - provider = new MapCacheProvider(); - } - return provider; - } - - @Override - public ActionResult cache(UUID uuid, NickoProfile profile) { - final HashMap profiles = provider.getMap(); - profiles.put(uuid, profile); - return ActionResult.ok(); - } - - @Override - public boolean isCached(UUID uuid) { - final HashMap profiles = provider.getMap(); - return profiles.containsKey(uuid); - } - - @Override - public Optional retrieve(UUID uuid) { - final HashMap profiles = provider.getMap(); - if (isCached(uuid)) { - return Optional.of(profiles.get(uuid)); - } - return Optional.empty(); - } - - @Override - public ActionResult delete(UUID uuid) { - final HashMap profiles = provider.getMap(); - profiles.remove(uuid); - return ActionResult.ok(); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/storage/map/MapCacheProvider.java b/src/main/java/xyz/ineanto/nicko/storage/map/MapCacheProvider.java deleted file mode 100644 index bfb85bd..0000000 --- a/src/main/java/xyz/ineanto/nicko/storage/map/MapCacheProvider.java +++ /dev/null @@ -1,28 +0,0 @@ -package xyz.ineanto.nicko.storage.map; - -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.ineanto.nicko.storage.CacheProvider; - -import java.util.HashMap; -import java.util.UUID; - -public class MapCacheProvider implements CacheProvider { - private HashMap profiles; - - @Override - public boolean init() { - if (profiles == null) { - profiles = new HashMap<>(); - } - return true; - } - - @Override - public boolean close() { - return true; - } - - public HashMap getMap() { - return profiles; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/storage/mysql/MySQLStorage.java b/src/main/java/xyz/ineanto/nicko/storage/mysql/MySQLStorage.java deleted file mode 100644 index ae74d5f..0000000 --- a/src/main/java/xyz/ineanto/nicko/storage/mysql/MySQLStorage.java +++ /dev/null @@ -1,152 +0,0 @@ -package xyz.ineanto.nicko.storage.mysql; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; -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; -import xyz.ineanto.nicko.storage.Storage; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.UUID; -import java.util.logging.Logger; - -public class MySQLStorage extends Storage { - private final Logger logger = Logger.getLogger("SQLStorage"); - private final Configuration configuration; - private final Gson gson = new GsonBuilder().serializeNulls().create(); - - private MySQLStorageProvider provider; - - public MySQLStorage(Configuration configuration) { - this.configuration = configuration; - } - - @Override - public MySQLStorageProvider getProvider() { - if (provider == null) { - provider = new MySQLStorageProvider(configuration); - } - return provider; - } - - @Override - public ActionResult store(UUID uuid, NickoProfile profile) { - final Connection connection = getProvider().getConnection(); - if (connection == null) return ActionResult.error(); - - try { - final PreparedStatement statement = isStored(uuid) ? - getUpdateStatement(connection, uuid, profile) : getInsertStatement(connection, uuid, profile); - statement.executeUpdate(); - return ActionResult.ok(); - } catch (SQLException e) { - logger.warning("Couldn't send SQL Request: " + e.getMessage()); - return ActionResult.error(); - } - } - - @Override - public boolean isStored(UUID uuid) { - final Connection connection = getProvider().getConnection(); - if (connection == null) return false; - - try { - final String sql = "SELECT uuid FROM nicko.DATA WHERE uuid = ?"; - - final PreparedStatement statement = connection.prepareStatement(sql); - statement.setString(1, uuid.toString()); - - final ResultSet resultSet = statement.executeQuery(); - return resultSet.next(); - } catch (SQLException e) { - logger.warning("Couldn't check if data is present: " + e.getMessage()); - return false; - } - } - - @Override - public Optional retrieve(UUID uuid) { - final Connection connection = getProvider().getConnection(); - if (connection == null) return Optional.empty(); - if (!isStored(uuid)) return Optional.empty(); - - try { - final String sql = "SELECT * FROM nicko.DATA WHERE uuid = ?"; - - final PreparedStatement statement = connection.prepareStatement(sql); - statement.setString(1, uuid.toString()); - - final ResultSet resultSet = statement.executeQuery(); - String name = ""; - String skin = ""; - String locale = ""; - boolean randomSkin = false; - List favorites = Collections.emptyList(); - while (resultSet.next()) { - name = resultSet.getString("name"); - skin = resultSet.getString("skin"); - locale = resultSet.getString("locale"); - randomSkin = resultSet.getBoolean("randomskin"); - favorites = gson.fromJson(resultSet.getString("favorites"), new TypeToken>() { }.getType()); - } - - final NickoProfile profile = new NickoProfile(new Appearance(name, skin), Language.fromCode(locale), randomSkin, favorites); - return Optional.of(profile); - } catch (SQLException e) { - logger.warning("Couldn't fetch profile: " + e.getMessage()); - return Optional.empty(); - } - } - - @Override - public ActionResult delete(UUID uuid) { - final Connection connection = getProvider().getConnection(); - if (connection == null) return ActionResult.error(); - - try { - final String sql = "DELETE FROM nicko.DATA WHERE uuid = ?"; - final PreparedStatement statement = connection.prepareStatement(sql); - statement.setString(1, uuid.toString()); - int rows = statement.executeUpdate(); - return (rows == 1 ? ActionResult.ok() : ActionResult.error()); - } catch (SQLException e) { - logger.warning("Couldn't delete profile: " + e.getMessage()); - return ActionResult.error(); - } - } - - private PreparedStatement getInsertStatement(Connection connection, UUID uuid, NickoProfile profile) throws SQLException { - final String sql = "INSERT IGNORE INTO nicko.DATA (`uuid`, `name`, `skin`, `locale`, `randomskin`, `favorites`) VALUES (?, ?, ?, ?, ?, ?)"; - final PreparedStatement statement = connection.prepareStatement(sql); - statement.setString(1, uuid.toString()); - statement.setString(2, profile.getName() == null ? null : profile.getName()); - statement.setString(3, profile.getSkin() == null ? null : profile.getSkin()); - statement.setString(4, profile.getLocale().getCode()); - statement.setBoolean(5, profile.isRandomSkin()); - - // Ineanto, 08/06/2025: this will never fucking work - statement.setString(6, gson.toJson(profile.getFavorites(), new TypeToken>() { }.getRawType())); - return statement; - } - - private PreparedStatement getUpdateStatement(Connection connection, UUID uuid, NickoProfile profile) throws SQLException { - 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()); - statement.setString(3, profile.getLocale().getCode()); - statement.setBoolean(4, profile.isRandomSkin()); - statement.setString(5, uuid.toString()); - return statement; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/storage/mysql/MySQLStorageProvider.java b/src/main/java/xyz/ineanto/nicko/storage/mysql/MySQLStorageProvider.java deleted file mode 100644 index 41faea2..0000000 --- a/src/main/java/xyz/ineanto/nicko/storage/mysql/MySQLStorageProvider.java +++ /dev/null @@ -1,84 +0,0 @@ -package xyz.ineanto.nicko.storage.mysql; - -import com.mysql.cj.jdbc.MysqlDataSource; -import xyz.ineanto.nicko.config.Configuration; -import xyz.ineanto.nicko.config.DataSourceConfiguration; -import xyz.ineanto.nicko.storage.StorageProvider; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.logging.Logger; - -public class MySQLStorageProvider implements StorageProvider { - private final Logger logger = Logger.getLogger("MySQLStorageProvider"); - private final Configuration configuration; - - private Connection connection; - - private final String schemaName = "nicko"; - - public MySQLStorageProvider(Configuration configuration) { - this.configuration = configuration; - } - - @Override - public boolean init() { - try { - final MysqlDataSource dataSource = new MysqlDataSource(); - final DataSourceConfiguration sqlConfiguration = configuration.getSqlConfiguration(); - dataSource.setUrl("jdbc:mysql://" + sqlConfiguration.getAddress() + ":" + sqlConfiguration.getPort()); - dataSource.setUser(sqlConfiguration.getUsername()); - dataSource.setPassword(sqlConfiguration.getPassword()); - connection = dataSource.getConnection(); - connection.setAutoCommit(true); - final boolean initialized = connection != null && !connection.isClosed(); - - if (!initialized) return false; - - createDatabase(); - createTable(); - return true; - } catch (SQLException e) { - logger.severe("Couldn't establish a connection to the MySQL database: " + e.getMessage()); - return false; - } - } - - @Override - public boolean close() { - if (connection == null) { return true; } - try { - connection.close(); - return connection.isClosed(); - } catch (SQLException e) { - return false; - } - } - - private void createTable() throws SQLException { - final Connection connection = getConnection(); - final String query = "CREATE TABLE IF NOT EXISTS %s.DATA ".replace("%s", schemaName) + - "(uuid varchar(36) NOT NULL," + - "name varchar(16)," + - "skin varchar(16)," + - "locale char(2) NOT NULL," + - "bungeecord boolean NOT NULL," + - "PRIMARY KEY (uuid))"; - - final PreparedStatement statement = connection.prepareStatement(query); - statement.executeUpdate(); - } - - private void createDatabase() throws SQLException { - final Connection connection = getConnection(); - final String query = "CREATE DATABASE IF NOT EXISTS %s".replace("%s", schemaName); - - final PreparedStatement statement = connection.prepareStatement(query); - statement.executeUpdate(); - } - - public Connection getConnection() { - return connection; - } -} diff --git a/src/main/java/xyz/ineanto/nicko/storage/redis/RedisCache.java b/src/main/java/xyz/ineanto/nicko/storage/redis/RedisCache.java deleted file mode 100644 index b40367e..0000000 --- a/src/main/java/xyz/ineanto/nicko/storage/redis/RedisCache.java +++ /dev/null @@ -1,79 +0,0 @@ -package xyz.ineanto.nicko.storage.redis; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.exceptions.JedisException; -import xyz.ineanto.nicko.appearance.ActionResult; -import xyz.ineanto.nicko.config.Configuration; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.profile.NickoProfile; -import xyz.ineanto.nicko.storage.Cache; -import xyz.ineanto.nicko.storage.CacheProvider; - -import java.util.Optional; -import java.util.UUID; - -public class RedisCache extends Cache { - private final Gson gson = new GsonBuilder() - .serializeNulls() - .setPrettyPrinting() - .create(); - private final Configuration configuration; - private RedisCacheProvider provider; - - public RedisCache(Configuration configuration) { - this.configuration = configuration; - } - - @Override - public CacheProvider getProvider() { - if (provider == null) { - provider = new RedisCacheProvider(configuration); - } - return provider; - } - - @Override - public ActionResult cache(UUID uuid, NickoProfile profile) { - try (Jedis jedis = provider.getJedis()) { - jedis.set("nicko:" + uuid.toString(), gson.toJson(profile)); - return ActionResult.ok(); - } catch (JedisException exception) { - return ActionResult.error(LanguageKey.Error.CACHE); - } - } - - @Override - public boolean isCached(UUID uuid) { - try (Jedis jedis = provider.getJedis()) { - return jedis.exists("nicko:" + uuid.toString()); - } catch (JedisException exception) { - return false; - } - } - - @Override - public Optional retrieve(UUID uuid) { - try (Jedis jedis = provider.getJedis()) { - // 06/07/25: like, for real. what the f is that comment about...? - // 08/29/23: what the fuck was I talking about? - // old_todo (Ineanto, 05/20/23): Check if cached before because Jedis returns a bulk reply so this is unsafe - final String data = jedis.get("nicko:" + uuid.toString()); - final NickoProfile profile = gson.fromJson(data, NickoProfile.class); - return Optional.of(profile); - } catch (JedisException exception) { - return Optional.empty(); - } - } - - @Override - public ActionResult delete(UUID uuid) { - try (Jedis jedis = provider.getJedis()) { - jedis.del("nicko:" + uuid.toString()); - return ActionResult.ok(); - } catch (JedisException exception) { - return ActionResult.error(LanguageKey.Error.CACHE); - } - } -} diff --git a/src/main/java/xyz/ineanto/nicko/storage/redis/RedisCacheProvider.java b/src/main/java/xyz/ineanto/nicko/storage/redis/RedisCacheProvider.java deleted file mode 100644 index 7408326..0000000 --- a/src/main/java/xyz/ineanto/nicko/storage/redis/RedisCacheProvider.java +++ /dev/null @@ -1,38 +0,0 @@ -package xyz.ineanto.nicko.storage.redis; - -import redis.clients.jedis.exceptions.JedisConnectionException; -import xyz.ineanto.nicko.config.Configuration; -import xyz.ineanto.nicko.config.DataSourceConfiguration; -import xyz.ineanto.nicko.storage.CacheProvider; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; - -public class RedisCacheProvider implements CacheProvider { - private final Configuration configuration; - private JedisPool pool; - - public RedisCacheProvider(Configuration configuration) { - this.configuration = configuration; - } - - @Override - public boolean init() { - final DataSourceConfiguration redisConfiguration = configuration.getRedisConfiguration(); - pool = new JedisPool(redisConfiguration.getAddress(), redisConfiguration.getPort()); - try { - return !pool.isClosed() && pool.getResource() != null; - } catch (JedisConnectionException exception) { - return false; - } - } - - @Override - public boolean close() { - pool.close(); - return pool.isClosed(); - } - - public Jedis getJedis() { - return pool.getResource(); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/version/Version.java b/src/main/java/xyz/ineanto/nicko/version/Version.java deleted file mode 100644 index 5b95ac0..0000000 --- a/src/main/java/xyz/ineanto/nicko/version/Version.java +++ /dev/null @@ -1,33 +0,0 @@ -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 @NotNull 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/en.yml b/src/main/resources/en.yml deleted file mode 100644 index afa9439..0000000 --- a/src/main/resources/en.yml +++ /dev/null @@ -1,175 +0,0 @@ -# Nicko ${version} - Language File: - -# Specifies the configuration version, don't change. -version: "1.4.0" - -prefix: "NICKO" -whoosh: "WHOOSH!" -oops: "OOPS!" - -error: - permission: "You're missing the permission to do that." - invalid_username: "This is an invalid Minecraft username." - mojang: "Something went wrong while fetching data from Mojang." - cache: "Unable to get data from the cache." - -event: - settings: - error: "Wasn''t able to update your settings! ({0})" - appearance: - set: - error: "Wasn''t able to apply your disguise! ({0})" - ok: "You''re now disguised." - chat_prompt_name: "Please enter your new name in chat (type \"EXIT\" to cancel)." - chat_prompt_skin: "Please enter your new skin in chat (type \"EXIT\" to cancel)." - restore: - error: "Wasn''t able to apply the previous disguise! ({0})" - ok: "Previous disguise restored." - remove: - error: "Wasn''t able to remove your disguise!." - missing: "You''re not currently disguised." - ok: "Undisguised successfully." - admin: - cache: - invalidate_cache: "Cache purged." - invalidate_entry: "{0} was purged." - check: - remove_skin: "Skin removed from player." - -gui: - title: - home: "Nicko" - settings: "Settings" - admin: "Administration" - check: "Player Management" - confirm: "Are you sure?" - cache: "Cache Management" - invalidate_skin: "Purge cache..." - favorites: "Favorites" - - exit: - name: "Exit" - go_back: - name: "Back" - unavailable: - name: "Unavailable" - lore: - - "This button is disabled." - error: - name: "Error!" - lore: - - "The item failed to load, but it might still work." - loading: - name: "Loading..." - choice: - confirm: - name: "Confirm" - choose: - name: "Choose an option..." - cancel: - name: "Cancel" - scroll_up: - name: "Scroll up" - lore: - - "(You can't scroll any higher.)" - scroll_down: - name: "Scroll down" - lore: - - "(You can't scroll any further down.)" - new_skin: - name: "New skin..." - new_name: - name: "New name..." - home: - admin: - name: "Administration panel" - lore: - - "Configure and manage Nicko." - settings: - name: "Settings" - lore: - - "Fine tune your experience with Nicko." - change_name: - name: "Change your nickname" - change_skin: - name: "Change your skin" - change_both: - name: "Change both" - random_skin: - name: "I feel lucky!" - reset: - name: "Reset appearance" - lore: - - "Completely remove your disguise." - favorites: - name: "Favorites" - lore: - - "List all your favorites appearances." - admin: - manage_cache: - name: "Manage the skin cache..." - lore: - - "View and manage the skin cache." - manage_player: - name: "Inspect a player..." - lore: - - "See players' disguise information." - check: - name: "{0}" - lore: - - "Nicked: {1}" - - "Name: {2}" - - "Skin: {3}" - - " " - - "Click to remove skin!" - cache: - statistics: - name: "Statistics" - lore: - - "Request count: {0}" - - "Number of skin cached: {1}" - - "Cache is cleared every 24 hours." - invalidate_cache: - name: "Invalidate cache" - lore: - - "NOT RECOMMENDED" - - "Invalidate the entirety of the skin cache." - - "This doesn't reset player's disguises." - invalidate_skin: - name: "Invalidate a skin..." - lore: - - "Select a specific skin to invalidate." - - "Useful if a skin has been recently updated." - entry: - name: "{0}" - lore: - - "Click to invalidate..." - settings: - toggleable_button: - lore: - - "{0} Disabled" - - "{1} Enabled" - cycling_choices: - lore: - - "Cycle through the values" - - "by left or right clicking." - language: - name: "Language" - random_skin: - name: "Random skin on login" - favorites: - add: - name: "Add a favorite" - lore: - - "Add a new favorite appearance to the list." - - "Hold SHIFT to add your current disguise!" - remove: - name: "Toggle deletion mode" - lore: - - "Clicking on any disguise as this mode is" - - "active will remove it from your favorites." - entry: - name: "Favorite" - lore: - - "Name: {0}" - - "Skin: {1}" \ No newline at end of file diff --git a/src/main/resources/fr.yml b/src/main/resources/fr.yml deleted file mode 100644 index 109a887..0000000 --- a/src/main/resources/fr.yml +++ /dev/null @@ -1,177 +0,0 @@ -# Nicko ${version} - Fichier de langue: - -# Précise la version de la configuration, ne pas changer. -version: "1.4.0" - -prefix: "NICKO" -whoosh: "WHOOSH!" -oops: "OOPS!" - -error: - permission: "Vous n'avez pas la permission de faire cela." - invalid_username: "Nom d'utilisateur Minecraft invalide." - mojang: "Une erreur est surevenue en récupérant les information depuis Mojang." - cache: "Impossible de récupérer les données depuis le cache." - -event: - settings: - error: "Impossible de mettre à jour vos paramètres ! ({0})" - appearance: - set: - error: "Impossible d''appliquer votre déguisement ! ({0})" - ok: "Déguisement appliqué avec succès." - chat_prompt_name: "Veuillez entrer votre nouveau pseudo (saisissez \"EXIT\" pour arrêter)." - chat_prompt_skin: "Veuillez entrer votre nouveau skin (saisissez \"EXIT\" pour arrêter)." - restore: - error: "Impossible d''appliquer le précédent déguisement ! ({0})" - ok: "Votre précédent déguisement a été appliqué." - remove: - error: "Impossible de retirer votre déguisement." - missing: "Vous n''avez pas de déguisement." - ok: "Déguisement retiré." - admin: - cache: - invalidate_cache: "Cache complet invalidé." - invalidate_entry: "{0} a été invalidé." - check: - remove_skin: "Déguisement retiré au joueur." - -gui: - title: - home: "Nicko" - settings: "Paramètres" - admin: "Administration" - check: "Gestion des Joueurs" - confirm: "Êtes-vous sûr ?" - cache: "Gestion du Cache" - invalidate_skin: "Purge du cache..." - favorites: "Favoris" - - exit: - name: "Quitter" - go_back: - name: "Retour" - unavailable: - name: "Indisponible" - lore: - - "Ce boutton est désactivé." - error: - name: "Erreur !" - lore: - - "La texture de l'objet n'a pas chargé" - - "correctement mais il fonctionne encore." - loading: - name: "Chargement..." - choice: - confirm: - name: "Confirmer" - choose: - name: "Choisissez une option..." - cancel: - name: "Annuler" - scroll_up: - name: "Défiler vers le haut" - lore: - - "(Impossible de défiler plus haut.)" - scroll_down: - name: "Défiler vers le bas" - lore: - - "(Impossible de défiler plus bas.)" - new_skin: - name: "Nouveau skin..." - new_name: - name: "Nouveau nom..." - home: - admin: - name: "Panel d'administration" - lore: - - "Configurez et gérez Nicko." - settings: - name: "Paramètres" - lore: - - "Gérez votre expérience avec Nicko." - change_name: - name: "Changer le pseudo" - change_skin: - name: "Changer le skin" - change_both: - name: "Changer les deux" - random_skin: - name: "Je me sens chanceux !" - reset: - name: "Réinitialiser l'apparence" - lore: - - "Supprime complètement votre déguisement." - favorites: - name: "Favoris" - lore: - - "Listez toutes vos apparences favorites." - admin: - manage_cache: - name: "Gérer le cache de skin..." - lore: - - "Consultez et gérez le cache de skin." - manage_player: - name: "Vérifier un joueur..." - lore: - - "Vérifiez les informations de déguisement d'un joueur." - check: - name: "{0}" - lore: - - "Déguisé: {1}" - - "Nom: {2}" - - "Skin: {3}" - - " " - - "Cliquez pour retirer le skin !" - cache: - statistics: - name: "Statistiques" - lore: - - "Nombre de requêtes: {0}" - - "Nb. de skin dans le cache: {1}" - - "Le cache est vidé toutes les 24 heures." - invalidate_cache: - name: "Purger le cache" - lore: - - "DÉCONSEILLÉ" - - "Purge l'entièreté du cache des skin." - - "Ne retire pas les déguisements des joueurs." - invalidate_skin: - name: "Invalider un skin..." - lore: - - "Sélectionnez une apparence spécifique à" - - "invalider. Utile dans le cas où un skin" - - "a récemment été mis à jour." - entry: - name: "{0}" - lore: - - "Cliquez pour invalider..." - settings: - toggleable_button: - lore: - - "{0} Désactivé" - - "{1} Activé" - cycling_choices: - lore: - - "Parcourez les valeurs" - - "avec un clique gauche/droit." - language: - name: "Langage" - random_skin: - name: "Apparence aléatoire à la connexion" - favorites: - add: - name: "Ajouter un favori" - lore: - - "Ajoute une nouvelle apparence favorite." - - "Maintenez SHIFT pour ajouter votre apparence actuelle !" - remove: - name: "Activer la suppression" - lore: - - "Cliquer sur un déguisement lorsque le mode est" - - "actif le supprimera de vos favoris." - entry: - name: "Favori" - lore: - - "Nom: {0}" - - "Skin: {1}" \ No newline at end of file diff --git a/src/main/resources/names.txt b/src/main/resources/names.txt deleted file mode 100644 index 0d54e92..0000000 --- a/src/main/resources/names.txt +++ /dev/null @@ -1,490 +0,0 @@ -w4nderlost -TooParanoids -Der_OG_31er -9xxDaRkShAdOwxx9 -giiiaan_ -Jqstinnn -Tillysboy92 -AlwaysCello -SyndrexG0D -Peypeycake -ThePerjurer -Tioe -Elternbaum -BarkersRover_16 -pebsso -cyrus6950 -Bigest_guy -RV0REU -R379 -Shetell -_HEAPASS_ -Iamaloner21 -TheFardoxGamerHD -Flyboi43 -Cha0smusik -kat00 -Infreat -Crummymoofin -MijnVriend -momsrightkidney -dmacrado -Elephantman321 -ii_hamoudi_YT -FaurePavane -ambiezzz -XD_Bandit695_XD -Nabingo -Cyl0re -ku5 -SrAragon -StarlightDream9 -CJ5370 -rainbees -KeroTheWolf -Andrews9722 -cursed_Assyrian -yamateni -ProgramEXE -exprso -harrypanda -LookerMD -migykins -Wintrous -ZzGaBi -Flayber -Grenixal -maeve_wells -Creeper10fr -10Chairs -2525lock -Shqipe -XenitsuZen -Berno17_ -wolle1313 -HalfDogHalfCat1 -NachoGarcia -popsicoal -NemesiSevil2006 -AnywayOj -Tanko12345 -Samdweck -LYRECODE123 -Resulten -SirBastii -Maku056 -ItzArnizzz -Brsh3620 -Masonita -kapplanium -shoezo -Mansur203 -Waterboy15217 -redragonne -ghko325 -HopePVP_tw7 -xtka -NimwenxZ -Hiro0408 -PanderaWz -Shesu -_Aniste_NY_ -Besceste -3ee3 -ArcticGalaxy123 -snooze_mingo -LizzyLomnh -FaZeChulupa -LineZeeK -liabilaty -BlackSheep1610 -Simif69 -Aficionado -riekin -XLuggas -MathExams -6fq -Marveel -lolme51 -TaioSayUwU -Fonklift -blvw -Po1204 -Pierre_Rabbit -mifimasters -MrRidge1 -arnqen -Nick_cage102 -Geo_9918 -SSShudder -Nicolas_Mom -WolfMatrix101 -frictionless_ -Gughik -gold_dragon_4 -nealxero -ClonedPickle -SourWatermelonxX -devilunion -Daryifuny -joaomarcos11 -Dekeef -PadfootTheDog -DarkScopez80 -flowers220 -Gaiiya -The_Yeet_ -juuuuwu -MrMafioz -Surpasses -TypicalOwen -0lober -Zerfixy -Sunny3803b -neostanley -Creeper_Kart -minestin -Goldenfredster -Vju -MrArchI_YT -Casper1709 -Backiii_ -DrCreate -Nova_Lux1 -Jayvin_Blanco -ShadowMan1770 -0KOPO40K -Silk_Altermann69 -Alu____ -Honey_MilkExe112 -T0XEI -CB_13 -Paragorn -HaGiang -Shivendra8i -Mayflower47 -Not_Someguy -raxx111bg -IceyGlaceon -grasseffect -PoopTNTpvp -codecyber -Shrumpkin13 -Shqdown -Cmartin82 -KTCXD_5p0tty -rockesalt -goldjeong -TheRealAKD -NamiSwaaan -yaaratol -Dikiy_Flexer -PoLecker24 -_SakuraTree -PikkOgP0rno -HawksFang -BlueWinterWolf -hskmerk -gurmaw -Lunggor -clashfield -Zelaste -ACommonMouse -TJ_Mystery -Dizzy3312 -Raindropsss -minecraftxiaoju -sachilovebbh -Celina_LaZyCxt -firered6 -55000000 -Illunarnati -Jedidiah2003 -setomz1 -basically_e -TommyGreif02 -Bongrip42069 -Coco_Keopi -Lt_Colt -Kuurotta -GqmeKnight -WinCo_Foods -FKDLZ -IanPumpkin -tastywtf -natedawg0 -ZQLFenyx -GamerMaster110 -papajobi9 -Yucaroon -Xion_69 -AirJaw -funfun321234 -khalleesii -Pozisch -thorso15 -kyumisoup -Leonqrd -BmwDreamer -TehRos -pitplayer69 -_ve0 -Miss_Yuka -I_am_a_Bucket -nicolas_bean -xHorizonGC -RTX3060_ -Borec188 -c8to -megan3groCENG -ventriloquize -galczin -Scorch3dEarth3d -nightstarLP -VittyGam3r -GLaDOS__ -hydrelo -JustACarter -MikeDropperPlay -NorthernWest -_Skelesam_ -ZX_Style -Tamas_Boi -taylub -VyacheslavO_O -trippyaubs -udtpic -Lunarglow -Stoolman -legendary_meow -Loganii -CaptainStain56 -FoofieGeto -Judgeavapl -OneBigDigger -Sorem13 -Raisonneur -IlkoalI -Naxa -craigbabyonebay -NicholasG04 -UtopianCrisi -CalamarPasGenti -ryluh_ -Aceslimz -Howardygh99927 -brandon257 -MarcoswildHD -x_XSkylerX_x -Bronco09 -That_Kookie_ -Danigtz -Ricky_lol -999keyt -Thilow15 -Difesito -ostehovl -isacano_12 -big_esra -secretbaguette -MrCommunism -Jekube -GrandeMaster -DrGrip -TheArnek -JacsMars -Lliam14 -MrGameandWatch84 -Rinzap -XrazzeD -ukknown -ZohanPrent -Naspo -Rajem -VepiGHG -matoureal -BrianChen87 -Jrocky -stivo999 -Des_cole -ReqGames -Kingja1912 -issssyy -Apache424 -Nick_Zockt_ -Mr_Haider_10 -ValentineBun -fedorPro228 -xTilz -blockbuster02k -4ck -FrostedTree -VegasTortoise -ZBellaV12121 -paypales -qDread -itsRiven -i8oreo -_kimcream_ -Phisuss -Oscargray -Elsiff -callofdutydog00 -BruhTheMoment -Pazmaa -MythicalOak -komuchi33 -awf4 -Jacobsaurus21 -itsjohannaa -Jello12 -Adrien183 -jajazzywazzy -Jorjie22 -SuperBrawlr5788 -KaraageV -_Hanime_tv -Padilhao -Tikkas -ordinaryducky -Mothytix95 -renopotouwu -1000voices -niclas05 -Felipstein -DoutorBauer -FireballPlaysMC -vapelordsheep -Aboain -ImGrexy -Aivokolo -SuperAmazing101 -va75a77a -_DawYans_ -AceT1223 -Livvyboo7 -Saaaaaaaaaaaaans -okaychill -AwesomeBro1122 -absolutesnek -jogie5000 -curtainSenpai -gabbyisaloser -gamergurl6969 -ZeusDk -FranaRibas -Discoboss -SYZ_1 -Nakoe -FIU_Captive -xSiFan_ -ilyDeathxz -da_fipsi -Lochy -The_Lavie37 -Tonion -vnvrchy -xX0meg4Xx -haohxtheone -VtTronic -xDaniGum -tikkelill -DatYoshi -eyehamstewpit -nicholas460_ -Memsly445 -nugunugu -AndreSlayz -jashik1 -Qweenoftheocean -Coltable -treblaclef -Kisaxx -69Dxddy69 -RaulCuh -3Wheat -_OscarTheGrouch -oIsqk -Blockbusterweng -AntoineDegauss -ValeIsTheBest -SwaggyCrabby -DieOfSanity -SirenMC -Jade_Jewel -Tropic44 -666splendor -TallnessTallness -breadgang9827 -Muffin_Worlds -DedicatedVeggie -Gonzalox_7 -datrandomasian -Chasemon01 -Nyavix -Lonely_Summers -_RoveN -ok_kyro -LN_hunter -saharsabz -Roselilianna -Gadx -xtytan -RoRo_levosgien88 -Bowsesqe_21 -Bennett528 -TheShipSailsWest -KaiserGaming -Layna_Shinozaki -OP_greatly -D3rpTaco -Loufink -Jorlmungus -Snichol1801 -Ludixeo -Imoeto -MarshallNebunu -crazycrystals -Parapatus -HahaDani -MrQuaring -DonTurnt -SailorRoberts101 -FluffieBear -TripleThick -KingSparta -MummysHome -Cooga3 -Technosista -Youmerstudios -SkyyRaine -criss102 -mrfailt -CraftingBasic -qnxkdifh -Igorex2k20 -LaLisette -ReBoredGamer -warlordwest -ExoTemporal -KingLonmc -666Horus -IslandCity2 -TheBigSavage1 -Trishke2003 -skyrowin -Krissy3D -AntonWTobias -SaddyWasTaken -Ahoy_Peko_Hao_Yo -T4nTr1Ss -aleciolike -ninja_shenley -Lordmord1337 -eatmypoopfather -Ktanner -The42OWeedGOD -CooperBee -_MikuMiku_ -althume -Tr3bba93 \ No newline at end of file diff --git a/src/main/resources/paper-plugin.yml b/src/main/resources/paper-plugin.yml deleted file mode 100644 index f515c80..0000000 --- a/src/main/resources/paper-plugin.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Nicko -main: xyz.ineanto.nicko.Nicko -loader: xyz.ineanto.nicko.loader.NickoPluginLoader -version: ${version} -author: Ineanto -description: "The feature packed, next generation disguise plugin for Minecraft." -api-version: "1.21" -softdepend: [ PlaceholderAPI ] -depend: - - ProtocolLib - -dependencies: - server: - ProtocolLib: - load: BEFORE - join-classpath: true - -permissions: - nicko.*: - default: op - children: - - nicko.use - nicko.use: - default: false \ No newline at end of file diff --git a/v1_13_R1/pom.xml b/v1_13_R1/pom.xml new file mode 100644 index 0000000..6aa987b --- /dev/null +++ b/v1_13_R1/pom.xml @@ -0,0 +1,28 @@ + + + + nicko-parent + net.artelnatif + 1.0-SNAPSHOT + + 4.0.0 + + v1_13_R1 + 1.0-SNAPSHOT + + + + org.spigotmc + spigot + 1.13-R0.1-SNAPSHOT + provided + + + net.artelnatif + core + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/v1_13_R1/src/main/java/net/artelnatif/nicko/impl/v1_13_R1.java b/v1_13_R1/src/main/java/net/artelnatif/nicko/impl/v1_13_R1.java new file mode 100644 index 0000000..013ddd4 --- /dev/null +++ b/v1_13_R1/src/main/java/net/artelnatif/nicko/impl/v1_13_R1.java @@ -0,0 +1,128 @@ +package net.artelnatif.nicko.impl; + +import com.google.common.collect.Lists; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.server.v1_13_R1.*; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_13_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +public class v1_13_R1 implements Internals { + @Override + public void updateSelf(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final WorldServer worldServer = entityPlayer.getWorldServer(); + final PacketPlayOutRespawn respawn = new PacketPlayOutRespawn(worldServer.dimension, + worldServer.getDifficulty(), + worldServer.worldData.getType(), + entityPlayer.playerInteractManager.getGameMode()); + + final boolean wasFlying = player.isFlying(); + entityPlayer.playerConnection.sendPacket(respawn); + player.setFlying(wasFlying); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final PacketPlayOutEntityDestroy destroy = new PacketPlayOutEntityDestroy(entityPlayer.getBukkitEntity().getEntityId()); + final PacketPlayOutNamedEntitySpawn spawn = new PacketPlayOutNamedEntitySpawn(entityPlayer); + + final DataWatcher dataWatcher = entityPlayer.getDataWatcher(); + final DataWatcherObject displayedSkinPartDataWatcher = new DataWatcherObject<>(17, DataWatcherRegistry.a); + dataWatcher.set(displayedSkinPartDataWatcher, (byte) 0x7f); + final PacketPlayOutEntityMetadata entityMetadata = new PacketPlayOutEntityMetadata(entityPlayer.getBukkitEntity().getEntityId(), dataWatcher, true); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + if (onlineEntityPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineEntityPlayer.playerConnection.sendPacket(destroy); + onlineEntityPlayer.playerConnection.sendPacket(spawn); + } + onlineEntityPlayer.playerConnection.sendPacket(entityMetadata); + }); + } + + @Override + public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { + final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); + final String profileName = profile.getName() == null ? player.getName() : profile.getName(); + + final CraftPlayer craftPlayer = (CraftPlayer) player; + final EntityPlayer entityPlayer = craftPlayer.getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profileName); + + if (skinChange || changeOnlyName) { + final ActionResult skinFetch = fetchSkinTextures(profile, reset); + if (!skinFetch.isError()) { + final MojangSkin skin = skinFetch.getResult(); + final PropertyMap properties = gameProfile.getProperties(); + properties.removeAll("textures"); + properties.put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + updateSelf(player); + } + } + + final PacketPlayOutPlayerInfo remove = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, entityPlayer); + final PacketPlayOutPlayerInfo add = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER); + final IChatBaseComponent name = new ChatComponentText(profileName); + + final Object infoData = yes( + add, + gameProfile, + entityPlayer.ping, + EnumGamemode.getById(player.getGameMode().ordinal()), + name + ); + spoofPlayerInfoPacket(add, Lists.newArrayList(infoData)); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + onlineEntityPlayer.playerConnection.sendPacket(remove); + onlineEntityPlayer.playerConnection.sendPacket(add); + }); + updateOthers(player); + return new ActionResult<>(); + } + + private void spoofPlayerInfoPacket(Object object, Object newValue) { + try { + final Field field = object.getClass().getDeclaredField("b"); + field.setAccessible(true); + field.set(object, newValue); + } catch (NoSuchFieldException | IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to spoof packet, that's bad! (" + e.getMessage() + ")"); + } + } + + public Object yes(PacketPlayOutPlayerInfo packet, GameProfile gameProfile, int ping, EnumGamemode gamemode, IChatBaseComponent name) { + try { + final Class clazz = Class.forName("net.minecraft.server.v1_13_R1.PacketPlayOutPlayerInfo$PlayerInfoData"); + final Constructor infoConstructor = clazz.getDeclaredConstructor( + PacketPlayOutPlayerInfo.class, + GameProfile.class, + int.class, + EnumGamemode.class, + IChatBaseComponent.class + ); + return infoConstructor.newInstance(packet, gameProfile, ping, gamemode, name); + } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | InstantiationException | + IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to instantiate PlayerInfoData, that's bad! (" + e.getMessage() + ")"); + return null; + } + } +} diff --git a/v1_13_R2/pom.xml b/v1_13_R2/pom.xml new file mode 100644 index 0000000..1e1606a --- /dev/null +++ b/v1_13_R2/pom.xml @@ -0,0 +1,28 @@ + + + + nicko-parent + net.artelnatif + 1.0-SNAPSHOT + + 4.0.0 + + v1_13_R2 + 1.0-SNAPSHOT + + + + org.spigotmc + spigot + 1.13.2-R0.1-SNAPSHOT + provided + + + net.artelnatif + core + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/v1_13_R2/src/main/java/net/artelnatif/nicko/impl/v1_13_R2.java b/v1_13_R2/src/main/java/net/artelnatif/nicko/impl/v1_13_R2.java new file mode 100644 index 0000000..ada42dc --- /dev/null +++ b/v1_13_R2/src/main/java/net/artelnatif/nicko/impl/v1_13_R2.java @@ -0,0 +1,128 @@ +package net.artelnatif.nicko.impl; + +import com.google.common.collect.Lists; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.server.v1_13_R2.*; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +public class v1_13_R2 implements Internals { + @Override + public void updateSelf(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final WorldServer worldServer = entityPlayer.getWorldServer(); + final PacketPlayOutRespawn respawn = new PacketPlayOutRespawn(worldServer.dimension, + worldServer.getDifficulty(), + worldServer.S(), + entityPlayer.playerInteractManager.getGameMode()); + + final boolean wasFlying = player.isFlying(); + entityPlayer.playerConnection.sendPacket(respawn); + player.setFlying(wasFlying); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final PacketPlayOutEntityDestroy destroy = new PacketPlayOutEntityDestroy(entityPlayer.getBukkitEntity().getEntityId()); + final PacketPlayOutNamedEntitySpawn spawn = new PacketPlayOutNamedEntitySpawn(entityPlayer); + + final DataWatcher dataWatcher = entityPlayer.getDataWatcher(); + final DataWatcherObject displayedSkinPartDataWatcher = new DataWatcherObject<>(17, DataWatcherRegistry.a); + dataWatcher.set(displayedSkinPartDataWatcher, (byte) 0x7f); + final PacketPlayOutEntityMetadata entityMetadata = new PacketPlayOutEntityMetadata(entityPlayer.getBukkitEntity().getEntityId(), dataWatcher, true); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + if (onlineEntityPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineEntityPlayer.playerConnection.sendPacket(destroy); + onlineEntityPlayer.playerConnection.sendPacket(spawn); + } + onlineEntityPlayer.playerConnection.sendPacket(entityMetadata); + }); + } + + @Override + public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { + final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); + final String profileName = profile.getName() == null ? player.getName() : profile.getName(); + + final CraftPlayer craftPlayer = (CraftPlayer) player; + final EntityPlayer entityPlayer = craftPlayer.getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profileName); + + if (skinChange || changeOnlyName) { + final ActionResult skinFetch = fetchSkinTextures(profile, reset); + if (!skinFetch.isError()) { + final MojangSkin skin = skinFetch.getResult(); + final PropertyMap properties = gameProfile.getProperties(); + properties.removeAll("textures"); + properties.put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + updateSelf(player); + } + } + + final PacketPlayOutPlayerInfo remove = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, entityPlayer); + final PacketPlayOutPlayerInfo add = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER); + final IChatBaseComponent name = new ChatComponentText(profileName); + + final Object infoData = yes( + add, + gameProfile, + entityPlayer.ping, + EnumGamemode.getById(player.getGameMode().ordinal()), + name + ); + spoofPlayerInfoPacket(add, Lists.newArrayList(infoData)); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + onlineEntityPlayer.playerConnection.sendPacket(remove); + onlineEntityPlayer.playerConnection.sendPacket(add); + }); + updateOthers(player); + return new ActionResult<>(); + } + + private void spoofPlayerInfoPacket(Object object, Object newValue) { + try { + final Field field = object.getClass().getDeclaredField("b"); + field.setAccessible(true); + field.set(object, newValue); + } catch (NoSuchFieldException | IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to spoof packet, that's bad! (" + e.getMessage() + ")"); + } + } + + public Object yes(PacketPlayOutPlayerInfo packet, GameProfile gameProfile, int ping, EnumGamemode gamemode, IChatBaseComponent name) { + try { + final Class clazz = Class.forName("net.minecraft.server.v1_13_R2.PacketPlayOutPlayerInfo$PlayerInfoData"); + final Constructor infoConstructor = clazz.getDeclaredConstructor( + PacketPlayOutPlayerInfo.class, + GameProfile.class, + int.class, + EnumGamemode.class, + IChatBaseComponent.class + ); + return infoConstructor.newInstance(packet, gameProfile, ping, gamemode, name); + } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | InstantiationException | + IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to instantiate PlayerInfoData, that's bad! (" + e.getMessage() + ")"); + return null; + } + } +} diff --git a/v1_14_R1/pom.xml b/v1_14_R1/pom.xml new file mode 100644 index 0000000..f69fa93 --- /dev/null +++ b/v1_14_R1/pom.xml @@ -0,0 +1,28 @@ + + + + nicko-parent + net.artelnatif + 1.0-SNAPSHOT + + 4.0.0 + + v1_14_R1 + 1.0-SNAPSHOT + + + + org.spigotmc + spigot + 1.14.4-R0.1-SNAPSHOT + provided + + + net.artelnatif + core + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/v1_14_R1/src/main/java/net/artelnatif/nicko/impl/v1_14_R1.java b/v1_14_R1/src/main/java/net/artelnatif/nicko/impl/v1_14_R1.java new file mode 100644 index 0000000..8cb1887 --- /dev/null +++ b/v1_14_R1/src/main/java/net/artelnatif/nicko/impl/v1_14_R1.java @@ -0,0 +1,126 @@ +package net.artelnatif.nicko.impl; + +import com.google.common.collect.Lists; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.server.v1_14_R1.*; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +public class v1_14_R1 implements Internals { + @Override + public void updateSelf(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final PacketPlayOutRespawn respawn = new PacketPlayOutRespawn(entityPlayer.getWorldServer().getWorldProvider().getDimensionManager(), + entityPlayer.getWorld().P(), + entityPlayer.playerInteractManager.getGameMode()); + + final boolean wasFlying = player.isFlying(); + entityPlayer.playerConnection.sendPacket(respawn); + player.setFlying(wasFlying); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final PacketPlayOutEntityDestroy destroy = new PacketPlayOutEntityDestroy(entityPlayer.getBukkitEntity().getEntityId()); + final PacketPlayOutNamedEntitySpawn spawn = new PacketPlayOutNamedEntitySpawn(entityPlayer); + + final DataWatcher dataWatcher = entityPlayer.getDataWatcher(); + final DataWatcherObject displayedSkinPartDataWatcher = new DataWatcherObject<>(17, DataWatcherRegistry.a); + dataWatcher.set(displayedSkinPartDataWatcher, (byte) 0x7f); + final PacketPlayOutEntityMetadata entityMetadata = new PacketPlayOutEntityMetadata(entityPlayer.getBukkitEntity().getEntityId(), dataWatcher, true); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + if (onlineEntityPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineEntityPlayer.playerConnection.sendPacket(destroy); + onlineEntityPlayer.playerConnection.sendPacket(spawn); + } + onlineEntityPlayer.playerConnection.sendPacket(entityMetadata); + }); + } + + @Override + public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { + final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); + final String profileName = profile.getName() == null ? player.getName() : profile.getName(); + + final CraftPlayer craftPlayer = (CraftPlayer) player; + final EntityPlayer entityPlayer = craftPlayer.getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profileName); + + if (skinChange || changeOnlyName) { + final ActionResult skinFetch = fetchSkinTextures(profile, reset); + if (!skinFetch.isError()) { + final MojangSkin skin = skinFetch.getResult(); + final PropertyMap properties = gameProfile.getProperties(); + properties.removeAll("textures"); + properties.put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + updateSelf(player); + } + } + + final PacketPlayOutPlayerInfo remove = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, entityPlayer); + final PacketPlayOutPlayerInfo add = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER); + final IChatBaseComponent name = new ChatComponentText(profileName); + + final Object infoData = yes( + add, + gameProfile, + entityPlayer.ping, + EnumGamemode.getById(player.getGameMode().ordinal()), + name + ); + spoofPlayerInfoPacket(add, Lists.newArrayList(infoData)); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + onlineEntityPlayer.playerConnection.sendPacket(remove); + onlineEntityPlayer.playerConnection.sendPacket(add); + }); + updateOthers(player); + return new ActionResult<>(); + } + + private void spoofPlayerInfoPacket(Object object, Object newValue) { + try { + final Field field = object.getClass().getDeclaredField("b"); + field.setAccessible(true); + field.set(object, newValue); + } catch (NoSuchFieldException | IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to spoof packet, that's bad! (" + e.getMessage() + ")"); + } + } + + public Object yes(PacketPlayOutPlayerInfo packet, GameProfile gameProfile, int ping, EnumGamemode gamemode, IChatBaseComponent name) { + try { + final Class clazz = Class.forName("net.minecraft.server.v1_14_R1.PacketPlayOutPlayerInfo$PlayerInfoData"); + final Constructor infoConstructor = clazz.getDeclaredConstructor( + PacketPlayOutPlayerInfo.class, + GameProfile.class, + int.class, + EnumGamemode.class, + IChatBaseComponent.class + ); + return infoConstructor.newInstance(packet, gameProfile, ping, gamemode, name); + } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | InstantiationException | + IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to instantiate PlayerInfoData, that's bad! (" + e.getMessage() + ")"); + return null; + } + } +} diff --git a/v1_15_R1/pom.xml b/v1_15_R1/pom.xml new file mode 100644 index 0000000..783358e --- /dev/null +++ b/v1_15_R1/pom.xml @@ -0,0 +1,28 @@ + + + + nicko-parent + net.artelnatif + 1.0-SNAPSHOT + + 4.0.0 + + v1_15_R1 + 1.0-SNAPSHOT + + + + org.spigotmc + spigot + 1.15.2-R0.1-SNAPSHOT + provided + + + net.artelnatif + core + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/v1_15_R1/src/main/java/net/artelnatif/nicko/impl/v1_15_R1.java b/v1_15_R1/src/main/java/net/artelnatif/nicko/impl/v1_15_R1.java new file mode 100644 index 0000000..1bffee0 --- /dev/null +++ b/v1_15_R1/src/main/java/net/artelnatif/nicko/impl/v1_15_R1.java @@ -0,0 +1,129 @@ +package net.artelnatif.nicko.impl; + +import com.google.common.collect.Lists; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.server.v1_15_R1.*; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +public class v1_15_R1 implements Internals { + @Override + public void updateSelf(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final CraftWorld world = entityPlayer.getWorld().getWorld(); + final PacketPlayOutRespawn respawn = new PacketPlayOutRespawn(entityPlayer.getWorldServer().getWorldProvider().getDimensionManager(), + world.getSeed(), + entityPlayer.getWorld().P(), + entityPlayer.playerInteractManager.getGameMode()); + + final boolean wasFlying = player.isFlying(); + entityPlayer.playerConnection.sendPacket(respawn); + player.setFlying(wasFlying); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final PacketPlayOutEntityDestroy destroy = new PacketPlayOutEntityDestroy(entityPlayer.getBukkitEntity().getEntityId()); + final PacketPlayOutNamedEntitySpawn spawn = new PacketPlayOutNamedEntitySpawn(entityPlayer); + + final DataWatcher dataWatcher = entityPlayer.getDataWatcher(); + final DataWatcherObject displayedSkinPartDataWatcher = new DataWatcherObject<>(17, DataWatcherRegistry.a); + dataWatcher.set(displayedSkinPartDataWatcher, (byte) 0x7f); + final PacketPlayOutEntityMetadata entityMetadata = new PacketPlayOutEntityMetadata(entityPlayer.getBukkitEntity().getEntityId(), dataWatcher, true); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + if (onlineEntityPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineEntityPlayer.playerConnection.sendPacket(destroy); + onlineEntityPlayer.playerConnection.sendPacket(spawn); + } + onlineEntityPlayer.playerConnection.sendPacket(entityMetadata); + }); + } + + @Override + public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { + final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); + final String profileName = profile.getName() == null ? player.getName() : profile.getName(); + + final CraftPlayer craftPlayer = (CraftPlayer) player; + final EntityPlayer entityPlayer = craftPlayer.getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profileName); + + if (skinChange || changeOnlyName) { + final ActionResult skinFetch = fetchSkinTextures(profile, reset); + if (!skinFetch.isError()) { + final MojangSkin skin = skinFetch.getResult(); + final PropertyMap properties = gameProfile.getProperties(); + properties.removeAll("textures"); + properties.put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + updateSelf(player); + } + } + + final PacketPlayOutPlayerInfo remove = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, entityPlayer); + final PacketPlayOutPlayerInfo add = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER); + final IChatBaseComponent name = new ChatComponentText(profileName); + + final Object infoData = yes( + add, + gameProfile, + entityPlayer.ping, + EnumGamemode.getById(player.getGameMode().ordinal()), + name + ); + spoofPlayerInfoPacket(add, Lists.newArrayList(infoData)); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + onlineEntityPlayer.playerConnection.sendPacket(remove); + onlineEntityPlayer.playerConnection.sendPacket(add); + }); + updateOthers(player); + return new ActionResult<>(); + } + + private void spoofPlayerInfoPacket(Object object, Object newValue) { + try { + final Field field = object.getClass().getDeclaredField("b"); + field.setAccessible(true); + field.set(object, newValue); + } catch (NoSuchFieldException | IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to spoof packet, that's bad! (" + e.getMessage() + ")"); + } + } + + public Object yes(PacketPlayOutPlayerInfo packet, GameProfile gameProfile, int ping, EnumGamemode gamemode, IChatBaseComponent name) { + try { + final Class clazz = Class.forName("net.minecraft.server.v1_15_R1.PacketPlayOutPlayerInfo$PlayerInfoData"); + final Constructor infoConstructor = clazz.getDeclaredConstructor( + PacketPlayOutPlayerInfo.class, + GameProfile.class, + int.class, + EnumGamemode.class, + IChatBaseComponent.class + ); + return infoConstructor.newInstance(packet, gameProfile, ping, gamemode, name); + } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | InstantiationException | + IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to instantiate PlayerInfoData, that's bad! (" + e.getMessage() + ")"); + return null; + } + } +} diff --git a/v1_16_R1/pom.xml b/v1_16_R1/pom.xml new file mode 100644 index 0000000..032d146 --- /dev/null +++ b/v1_16_R1/pom.xml @@ -0,0 +1,28 @@ + + + + nicko-parent + net.artelnatif + 1.0-SNAPSHOT + + 4.0.0 + + v1_16_R1 + 1.0-SNAPSHOT + + + + org.spigotmc + spigot + 1.16.1-R0.1-SNAPSHOT + provided + + + net.artelnatif + core + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/v1_16_R1/src/main/java/net/artelnatif/nicko/impl/v1_16_R1.java b/v1_16_R1/src/main/java/net/artelnatif/nicko/impl/v1_16_R1.java new file mode 100644 index 0000000..c7939c1 --- /dev/null +++ b/v1_16_R1/src/main/java/net/artelnatif/nicko/impl/v1_16_R1.java @@ -0,0 +1,132 @@ +package net.artelnatif.nicko.impl; + +import com.google.common.collect.Lists; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.server.v1_16_R1.*; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_16_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +public class v1_16_R1 implements Internals { + @Override + public void updateSelf(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final ResourceKey levelResourceKey = entityPlayer.getWorld().getDimensionKey(); + final CraftWorld world = entityPlayer.getWorld().getWorld(); + final PacketPlayOutRespawn respawn = new PacketPlayOutRespawn(entityPlayer.getWorld().getTypeKey(), + levelResourceKey, world.getSeed(), + entityPlayer.playerInteractManager.c(), entityPlayer.playerInteractManager.getGameMode(), + false, + false, + false); + + final boolean wasFlying = player.isFlying(); + entityPlayer.playerConnection.sendPacket(respawn); + player.setFlying(wasFlying); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final PacketPlayOutEntityDestroy destroy = new PacketPlayOutEntityDestroy(entityPlayer.getBukkitEntity().getEntityId()); + final PacketPlayOutNamedEntitySpawn spawn = new PacketPlayOutNamedEntitySpawn(entityPlayer); + + final DataWatcher dataWatcher = entityPlayer.getDataWatcher(); + final DataWatcherObject displayedSkinPartDataWatcher = new DataWatcherObject<>(17, DataWatcherRegistry.a); + dataWatcher.set(displayedSkinPartDataWatcher, (byte) 0x7f); + final PacketPlayOutEntityMetadata entityMetadata = new PacketPlayOutEntityMetadata(entityPlayer.getBukkitEntity().getEntityId(), dataWatcher, true); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + if (onlineEntityPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineEntityPlayer.playerConnection.sendPacket(destroy); + onlineEntityPlayer.playerConnection.sendPacket(spawn); + } + onlineEntityPlayer.playerConnection.sendPacket(entityMetadata); + }); + } + + @Override + public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { + final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); + final String profileName = profile.getName() == null ? player.getName() : profile.getName(); + + final CraftPlayer craftPlayer = (CraftPlayer) player; + final EntityPlayer entityPlayer = craftPlayer.getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profileName); + + if (skinChange || changeOnlyName) { + final ActionResult skinFetch = fetchSkinTextures(profile, reset); + if (!skinFetch.isError()) { + final MojangSkin skin = skinFetch.getResult(); + final PropertyMap properties = gameProfile.getProperties(); + properties.removeAll("textures"); + properties.put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + updateSelf(player); + } + } + + final PacketPlayOutPlayerInfo remove = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, entityPlayer); + final PacketPlayOutPlayerInfo add = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER); + final IChatBaseComponent name = new ChatComponentText(profileName); + + final Object infoData = yes( + add, + gameProfile, + entityPlayer.ping, + EnumGamemode.getById(player.getGameMode().ordinal()), + name + ); + spoofPlayerInfoPacket(add, Lists.newArrayList(infoData)); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + onlineEntityPlayer.playerConnection.sendPacket(remove); + onlineEntityPlayer.playerConnection.sendPacket(add); + }); + updateOthers(player); + return new ActionResult<>(); + } + + private void spoofPlayerInfoPacket(Object object, Object newValue) { + try { + final Field field = object.getClass().getDeclaredField("b"); + field.setAccessible(true); + field.set(object, newValue); + } catch (NoSuchFieldException | IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to spoof packet, that's bad! (" + e.getMessage() + ")"); + } + } + + public Object yes(PacketPlayOutPlayerInfo packet, GameProfile gameProfile, int ping, EnumGamemode gamemode, IChatBaseComponent name) { + try { + final Class clazz = Class.forName("net.minecraft.server.v1_16_R1.PacketPlayOutPlayerInfo$PlayerInfoData"); + final Constructor infoConstructor = clazz.getDeclaredConstructor( + PacketPlayOutPlayerInfo.class, + GameProfile.class, + int.class, + EnumGamemode.class, + IChatBaseComponent.class + ); + return infoConstructor.newInstance(packet, gameProfile, ping, gamemode, name); + } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | InstantiationException | + IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to instantiate PlayerInfoData, that's bad! (" + e.getMessage() + ")"); + return null; + } + } +} diff --git a/v1_16_R2/pom.xml b/v1_16_R2/pom.xml new file mode 100644 index 0000000..658413d --- /dev/null +++ b/v1_16_R2/pom.xml @@ -0,0 +1,28 @@ + + + + nicko-parent + net.artelnatif + 1.0-SNAPSHOT + + 4.0.0 + + v1_16_R2 + 1.0-SNAPSHOT + + + + org.spigotmc + spigot + 1.16.3-R0.1-SNAPSHOT + provided + + + net.artelnatif + core + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/v1_16_R2/src/main/java/net/artelnatif/nicko/impl/v1_16_R2.java b/v1_16_R2/src/main/java/net/artelnatif/nicko/impl/v1_16_R2.java new file mode 100644 index 0000000..244970a --- /dev/null +++ b/v1_16_R2/src/main/java/net/artelnatif/nicko/impl/v1_16_R2.java @@ -0,0 +1,132 @@ +package net.artelnatif.nicko.impl; + +import com.google.common.collect.Lists; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.server.v1_16_R2.*; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_16_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +public class v1_16_R2 implements Internals { + @Override + public void updateSelf(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final ResourceKey levelResourceKey = entityPlayer.getWorld().getDimensionKey(); + final CraftWorld world = entityPlayer.getWorld().getWorld(); + final PacketPlayOutRespawn respawn = new PacketPlayOutRespawn(entityPlayer.getWorld().getDimensionManager(), + levelResourceKey, world.getSeed(), + entityPlayer.playerInteractManager.c(), entityPlayer.playerInteractManager.getGameMode(), + false, + false, + false); + + final boolean wasFlying = player.isFlying(); + entityPlayer.playerConnection.sendPacket(respawn); + player.setFlying(wasFlying); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final PacketPlayOutEntityDestroy destroy = new PacketPlayOutEntityDestroy(entityPlayer.getBukkitEntity().getEntityId()); + final PacketPlayOutNamedEntitySpawn spawn = new PacketPlayOutNamedEntitySpawn(entityPlayer); + + final DataWatcher dataWatcher = entityPlayer.getDataWatcher(); + final DataWatcherObject displayedSkinPartDataWatcher = new DataWatcherObject<>(17, DataWatcherRegistry.a); + dataWatcher.set(displayedSkinPartDataWatcher, (byte) 0x7f); + final PacketPlayOutEntityMetadata entityMetadata = new PacketPlayOutEntityMetadata(entityPlayer.getBukkitEntity().getEntityId(), dataWatcher, true); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + if (onlineEntityPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineEntityPlayer.playerConnection.sendPacket(destroy); + onlineEntityPlayer.playerConnection.sendPacket(spawn); + } + onlineEntityPlayer.playerConnection.sendPacket(entityMetadata); + }); + } + + @Override + public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { + final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); + final String profileName = profile.getName() == null ? player.getName() : profile.getName(); + + final CraftPlayer craftPlayer = (CraftPlayer) player; + final EntityPlayer entityPlayer = craftPlayer.getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profileName); + + if (skinChange || changeOnlyName) { + final ActionResult skinFetch = fetchSkinTextures(profile, reset); + if (!skinFetch.isError()) { + final MojangSkin skin = skinFetch.getResult(); + final PropertyMap properties = gameProfile.getProperties(); + properties.removeAll("textures"); + properties.put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + updateSelf(player); + } + } + + final PacketPlayOutPlayerInfo remove = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, entityPlayer); + final PacketPlayOutPlayerInfo add = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER); + final IChatBaseComponent name = new ChatComponentText(profileName); + + final Object infoData = yes( + add, + gameProfile, + entityPlayer.ping, + EnumGamemode.getById(player.getGameMode().ordinal()), + name + ); + spoofPlayerInfoPacket(add, Lists.newArrayList(infoData)); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + onlineEntityPlayer.playerConnection.sendPacket(remove); + onlineEntityPlayer.playerConnection.sendPacket(add); + }); + updateOthers(player); + return new ActionResult<>(); + } + + private void spoofPlayerInfoPacket(Object object, Object newValue) { + try { + final Field field = object.getClass().getDeclaredField("b"); + field.setAccessible(true); + field.set(object, newValue); + } catch (NoSuchFieldException | IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to spoof packet, that's bad! (" + e.getMessage() + ")"); + } + } + + public Object yes(PacketPlayOutPlayerInfo packet, GameProfile gameProfile, int ping, EnumGamemode gamemode, IChatBaseComponent name) { + try { + final Class clazz = Class.forName("net.minecraft.server.v1_16_R2.PacketPlayOutPlayerInfo$PlayerInfoData"); + final Constructor infoConstructor = clazz.getDeclaredConstructor( + PacketPlayOutPlayerInfo.class, + GameProfile.class, + int.class, + EnumGamemode.class, + IChatBaseComponent.class + ); + return infoConstructor.newInstance(packet, gameProfile, ping, gamemode, name); + } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | InstantiationException | + IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to instantiate PlayerInfoData, that's bad! (" + e.getMessage() + ")"); + return null; + } + } +} diff --git a/v1_16_R3/pom.xml b/v1_16_R3/pom.xml new file mode 100644 index 0000000..055dbe2 --- /dev/null +++ b/v1_16_R3/pom.xml @@ -0,0 +1,28 @@ + + + + nicko-parent + net.artelnatif + 1.0-SNAPSHOT + + 4.0.0 + + v1_16_R3 + 1.0-SNAPSHOT + + + + org.spigotmc + spigot + 1.16.5-R0.1-SNAPSHOT + provided + + + net.artelnatif + core + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/v1_16_R3/src/main/java/net/artelnatif/nicko/impl/v1_16_R3.java b/v1_16_R3/src/main/java/net/artelnatif/nicko/impl/v1_16_R3.java new file mode 100644 index 0000000..b0744ea --- /dev/null +++ b/v1_16_R3/src/main/java/net/artelnatif/nicko/impl/v1_16_R3.java @@ -0,0 +1,136 @@ +package net.artelnatif.nicko.impl; + +import com.google.common.collect.Lists; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.server.v1_16_R3.*; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_16_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +public class v1_16_R3 implements Internals { + @Override + public void updateSelf(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final ResourceKey levelResourceKey = entityPlayer.getWorld().getDimensionKey(); + final CraftWorld world = entityPlayer.getWorld().getWorld(); + final PacketPlayOutRespawn respawn = new PacketPlayOutRespawn(entityPlayer.getWorld().getDimensionManager(), + levelResourceKey, world.getSeed(), + entityPlayer.playerInteractManager.c(), entityPlayer.playerInteractManager.getGameMode(), + false, + false, + false); + + final boolean wasFlying = player.isFlying(); + entityPlayer.playerConnection.sendPacket(respawn); + player.setFlying(wasFlying); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final PacketPlayOutEntityDestroy destroy = new PacketPlayOutEntityDestroy(entityPlayer.getBukkitEntity().getEntityId()); + final PacketPlayOutNamedEntitySpawn spawn = new PacketPlayOutNamedEntitySpawn(entityPlayer); + + final DataWatcher dataWatcher = entityPlayer.getDataWatcher(); + final DataWatcherObject displayedSkinPartDataWatcher = new DataWatcherObject<>(17, DataWatcherRegistry.a); + dataWatcher.set(displayedSkinPartDataWatcher, (byte) 0x7f); + final PacketPlayOutEntityMetadata entityMetadata = new PacketPlayOutEntityMetadata(entityPlayer.getBukkitEntity().getEntityId(), dataWatcher, true); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + if (onlineEntityPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineEntityPlayer.playerConnection.sendPacket(destroy); + onlineEntityPlayer.playerConnection.sendPacket(spawn); + } + onlineEntityPlayer.playerConnection.sendPacket(entityMetadata); + }); + } + + @Override + public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { + final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); + final String profileName = profile.getName() == null ? player.getName() : profile.getName(); + + final CraftPlayer craftPlayer = (CraftPlayer) player; + final EntityPlayer entityPlayer = craftPlayer.getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profileName); + + if (skinChange || changeOnlyName) { + final ActionResult skinFetch = fetchSkinTextures(profile, reset); + if (!skinFetch.isError()) { + final MojangSkin skin = skinFetch.getResult(); + final PropertyMap properties = gameProfile.getProperties(); + properties.removeAll("textures"); + properties.put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + updateSelf(player); + } + } + + final PacketPlayOutPlayerInfo remove = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, entityPlayer); + final PacketPlayOutPlayerInfo add = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER); + final IChatBaseComponent name = new ChatComponentText(profileName); + + // ok so what the actual fuck + // the PlayerDataInfo inner class is NOT static + // thus the compiler can't access it when compiling?????? + // i swear this is so dumb that I have to resort to doing this + final Object infoData = yes( + add, + gameProfile, + entityPlayer.ping, + EnumGamemode.getById(player.getGameMode().ordinal()), + name + ); + spoofPlayerInfoPacket(add, Lists.newArrayList(infoData)); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + onlineEntityPlayer.playerConnection.sendPacket(remove); + onlineEntityPlayer.playerConnection.sendPacket(add); + }); + updateOthers(player); + return new ActionResult<>(); + } + + private void spoofPlayerInfoPacket(Object object, Object newValue) { + try { + final Field field = object.getClass().getDeclaredField("b"); + field.setAccessible(true); + field.set(object, newValue); + } catch (NoSuchFieldException | IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to spoof packet, that's bad! (" + e.getMessage() + ")"); + } + } + + public Object yes(PacketPlayOutPlayerInfo packet, GameProfile gameProfile, int ping, EnumGamemode gamemode, IChatBaseComponent name) { + try { + final Class clazz = Class.forName("net.minecraft.server.v1_16_R3.PacketPlayOutPlayerInfo$PlayerInfoData"); + final Constructor infoConstructor = clazz.getDeclaredConstructor( + PacketPlayOutPlayerInfo.class, + GameProfile.class, + int.class, + EnumGamemode.class, + IChatBaseComponent.class + ); + return infoConstructor.newInstance(packet, gameProfile, ping, gamemode, name); + } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | InstantiationException | + IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to instantiate PlayerInfoData, that's bad! (" + e.getMessage() + ")"); + return null; + } + } +} diff --git a/v1_17_R1/pom.xml b/v1_17_R1/pom.xml new file mode 100644 index 0000000..7ca2642 --- /dev/null +++ b/v1_17_R1/pom.xml @@ -0,0 +1,28 @@ + + + + nicko-parent + net.artelnatif + 1.0-SNAPSHOT + + 4.0.0 + + v1_17_R1 + 1.0-SNAPSHOT + + + + org.spigotmc + spigot + 1.17.1-R0.1-SNAPSHOT + provided + + + net.artelnatif + core + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/v1_17_R1/src/main/java/net/artelnatif/nicko/impl/v1_17_R1.java b/v1_17_R1/src/main/java/net/artelnatif/nicko/impl/v1_17_R1.java new file mode 100644 index 0000000..24cd2a0 --- /dev/null +++ b/v1_17_R1/src/main/java/net/artelnatif/nicko/impl/v1_17_R1.java @@ -0,0 +1,101 @@ +package net.artelnatif.nicko.impl; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.network.chat.IChatBaseComponent; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.DataWatcher; +import net.minecraft.network.syncher.DataWatcherObject; +import net.minecraft.network.syncher.DataWatcherRegistry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.EntityPlayer; +import net.minecraft.world.level.EnumGamemode; +import net.minecraft.world.level.World; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; + +public class v1_17_R1 implements Internals { + @Override + public void updateSelf(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final ResourceKey levelResourceKey = entityPlayer.getWorld().getDimensionKey(); + final CraftWorld world = entityPlayer.getWorld().getWorld(); + final PacketPlayOutRespawn respawn = new PacketPlayOutRespawn(entityPlayer.getWorld().getDimensionManager(), + levelResourceKey, world.getSeed(), + entityPlayer.c.getGamemode(), entityPlayer.d.c(), + false, + false, + false); + + final boolean wasFlying = player.isFlying(); + entityPlayer.b.sendPacket(respawn); + player.setFlying(wasFlying); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final PacketPlayOutEntityDestroy destroy = new PacketPlayOutEntityDestroy(entityPlayer.getBukkitEntity().getEntityId()); + final PacketPlayOutNamedEntitySpawn spawn = new PacketPlayOutNamedEntitySpawn(entityPlayer); + + final DataWatcher dataWatcher = entityPlayer.getDataWatcher(); + final DataWatcherObject displayedSkinPartDataWatcher = new DataWatcherObject<>(17, DataWatcherRegistry.a); + dataWatcher.set(displayedSkinPartDataWatcher, (byte) 0x7f); + final PacketPlayOutEntityMetadata entityMetadata = new PacketPlayOutEntityMetadata(entityPlayer.getBukkitEntity().getEntityId(), dataWatcher, true); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + if (onlineEntityPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineEntityPlayer.b.sendPacket(destroy); + onlineEntityPlayer.b.sendPacket(spawn); + } + onlineEntityPlayer.b.sendPacket(entityMetadata); + }); + } + + @Override + public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { + final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); + final String profileName = profile.getName() == null ? player.getName() : profile.getName(); + + final CraftPlayer craftPlayer = (CraftPlayer) player; + final EntityPlayer entityPlayer = craftPlayer.getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profileName); + + if (skinChange || changeOnlyName) { + final ActionResult skinFetch = fetchSkinTextures(profile, reset); + if (!skinFetch.isError()) { + final MojangSkin skin = skinFetch.getResult(); + final PropertyMap properties = gameProfile.getProperties(); + properties.removeAll("textures"); + properties.put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + updateSelf(player); + } + } + + final PacketPlayOutPlayerInfo remove = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.e, entityPlayer); + final PacketPlayOutPlayerInfo add = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.a); + + add.b().clear(); + add.b().add(new PacketPlayOutPlayerInfo.PlayerInfoData(gameProfile, + player.getPing(), + EnumGamemode.getById(player.getGameMode().ordinal()), IChatBaseComponent.a(profileName))); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + onlineEntityPlayer.b.sendPacket(remove); + onlineEntityPlayer.b.sendPacket(add); + }); + updateOthers(player); + return new ActionResult<>(); + } +} diff --git a/v1_18_R1/pom.xml b/v1_18_R1/pom.xml new file mode 100644 index 0000000..d842952 --- /dev/null +++ b/v1_18_R1/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + + net.artelnatif + nicko-parent + 1.0-SNAPSHOT + + + v1_18_R1 + 1.0-SNAPSHOT + + + + org.spigotmc + spigot + 1.18.1-R0.1-SNAPSHOT + provided + + + net.artelnatif + core + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/v1_18_R1/src/main/java/net/artelnatif/nicko/impl/v1_18_R1.java b/v1_18_R1/src/main/java/net/artelnatif/nicko/impl/v1_18_R1.java new file mode 100644 index 0000000..1f1b7b7 --- /dev/null +++ b/v1_18_R1/src/main/java/net/artelnatif/nicko/impl/v1_18_R1.java @@ -0,0 +1,103 @@ +package net.artelnatif.nicko.impl; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.network.chat.IChatBaseComponent; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.DataWatcher; +import net.minecraft.network.syncher.DataWatcherObject; +import net.minecraft.network.syncher.DataWatcherRegistry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.EntityPlayer; +import net.minecraft.world.level.EnumGamemode; +import net.minecraft.world.level.World; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_18_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; + +public class v1_18_R1 implements Internals { + @Override + public void updateSelf(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final ResourceKey levelResourceKey = entityPlayer.x().aa(); + final CraftWorld world = entityPlayer.W().getWorld(); + // again, wtf is that + // f*ck obfuscation + final PacketPlayOutRespawn respawn = new PacketPlayOutRespawn(entityPlayer.W().q_(), + levelResourceKey, world.getSeed(), + entityPlayer.d.b(), entityPlayer.d.c(), + false, + false, + false); + + final boolean wasFlying = player.isFlying(); + entityPlayer.b.a(respawn); + player.setFlying(wasFlying); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final PacketPlayOutEntityDestroy destroy = new PacketPlayOutEntityDestroy(entityPlayer.getBukkitEntity().getEntityId()); + final PacketPlayOutNamedEntitySpawn spawn = new PacketPlayOutNamedEntitySpawn(entityPlayer); + + final DataWatcher dataWatcher = entityPlayer.ai(); + final DataWatcherObject displayedSkinPartDataWatcher = new DataWatcherObject<>(17, DataWatcherRegistry.a); + dataWatcher.b(displayedSkinPartDataWatcher, (byte) 0x7f); + final PacketPlayOutEntityMetadata entityMetadata = new PacketPlayOutEntityMetadata(entityPlayer.getBukkitEntity().getEntityId(), dataWatcher, true); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + if (onlineEntityPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineEntityPlayer.b.a(destroy); + onlineEntityPlayer.b.a(spawn); + } + onlineEntityPlayer.b.a(entityMetadata); + }); + } + + @Override + public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { + final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); + final String profileName = profile.getName() == null ? player.getName() : profile.getName(); + + final CraftPlayer craftPlayer = (CraftPlayer) player; + final EntityPlayer entityPlayer = craftPlayer.getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profileName); + + if (skinChange || changeOnlyName) { + final ActionResult skinFetch = fetchSkinTextures(profile, reset); + if (!skinFetch.isError()) { + final MojangSkin skin = skinFetch.getResult(); + final PropertyMap properties = gameProfile.getProperties(); + properties.removeAll("textures"); + properties.put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + updateSelf(player); + } + } + + final PacketPlayOutPlayerInfo remove = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.e, entityPlayer); + final PacketPlayOutPlayerInfo add = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.a); + + add.b().clear(); + add.b().add(new PacketPlayOutPlayerInfo.PlayerInfoData(gameProfile, + player.getPing(), + EnumGamemode.a(player.getGameMode().ordinal()), IChatBaseComponent.a(profileName))); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + onlineEntityPlayer.b.a(remove); + onlineEntityPlayer.b.a(add); + }); + updateOthers(player); + return new ActionResult<>(); + } +} diff --git a/v1_18_R2/pom.xml b/v1_18_R2/pom.xml new file mode 100644 index 0000000..4eb30ad --- /dev/null +++ b/v1_18_R2/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + + net.artelnatif + nicko-parent + 1.0-SNAPSHOT + + + v1_18_R2 + 1.0-SNAPSHOT + + + + org.spigotmc + spigot + 1.18.2-R0.1-SNAPSHOT + provided + + + net.artelnatif + core + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/v1_18_R2/src/main/java/net/artelnatif/nicko/impl/v1_18_R2.java b/v1_18_R2/src/main/java/net/artelnatif/nicko/impl/v1_18_R2.java new file mode 100644 index 0000000..1f085f6 --- /dev/null +++ b/v1_18_R2/src/main/java/net/artelnatif/nicko/impl/v1_18_R2.java @@ -0,0 +1,103 @@ +package net.artelnatif.nicko.impl; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.core.Holder; +import net.minecraft.network.chat.IChatBaseComponent; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.DataWatcher; +import net.minecraft.network.syncher.DataWatcherObject; +import net.minecraft.network.syncher.DataWatcherRegistry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.EntityPlayer; +import net.minecraft.world.level.EnumGamemode; +import net.minecraft.world.level.World; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; + +public class v1_18_R2 implements Internals { + @Override + public void updateSelf(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final ResourceKey levelResourceKey = entityPlayer.x().aa(); + final CraftWorld world = entityPlayer.s.getWorld(); + // wtf is that ? + final PacketPlayOutRespawn respawn = new PacketPlayOutRespawn(Holder.a(entityPlayer.s.q_()), + levelResourceKey, world.getSeed(), + entityPlayer.d.b(), entityPlayer.d.c(), + false, + false, + false); + + final boolean wasFlying = player.isFlying(); + entityPlayer.b.a(respawn); + player.setFlying(wasFlying); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final PacketPlayOutEntityDestroy destroy = new PacketPlayOutEntityDestroy(entityPlayer.getBukkitEntity().getEntityId()); + final PacketPlayOutNamedEntitySpawn spawn = new PacketPlayOutNamedEntitySpawn(entityPlayer); + + final DataWatcher dataWatcher = entityPlayer.ai(); + final DataWatcherObject displayedSkinPartDataWatcher = new DataWatcherObject<>(17, DataWatcherRegistry.a); + dataWatcher.b(displayedSkinPartDataWatcher, (byte) 0x7f); + final PacketPlayOutEntityMetadata entityMetadata = new PacketPlayOutEntityMetadata(entityPlayer.getBukkitEntity().getEntityId(), dataWatcher, true); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + if (onlineEntityPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineEntityPlayer.b.a(destroy); + onlineEntityPlayer.b.a(spawn); + } + onlineEntityPlayer.b.a(entityMetadata); + }); + } + + @Override + public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { + final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); + final String profileName = profile.getName() == null ? player.getName() : profile.getName(); + + final CraftPlayer craftPlayer = (CraftPlayer) player; + final EntityPlayer entityPlayer = craftPlayer.getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profileName); + + if (skinChange || changeOnlyName) { + final ActionResult skinFetch = fetchSkinTextures(profile, reset); + if (!skinFetch.isError()) { + final MojangSkin skin = skinFetch.getResult(); + final PropertyMap properties = gameProfile.getProperties(); + properties.removeAll("textures"); + properties.put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + updateSelf(player); + } + } + + final PacketPlayOutPlayerInfo remove = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.e, entityPlayer); + final PacketPlayOutPlayerInfo add = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.a); + + add.b().clear(); + add.b().add(new PacketPlayOutPlayerInfo.PlayerInfoData(gameProfile, + player.getPing(), + EnumGamemode.a(player.getGameMode().ordinal()), IChatBaseComponent.a(profileName))); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + onlineEntityPlayer.b.a(remove); + onlineEntityPlayer.b.a(add); + }); + updateOthers(player); + return new ActionResult<>(); + } +} \ No newline at end of file diff --git a/v1_19_R1/pom.xml b/v1_19_R1/pom.xml new file mode 100644 index 0000000..ccdb982 --- /dev/null +++ b/v1_19_R1/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + + net.artelnatif + nicko-parent + 1.0-SNAPSHOT + + + v1_19_R1 + 1.0-SNAPSHOT + + + + org.spigotmc + spigot + 1.19.2-R0.1-SNAPSHOT + provided + + + net.artelnatif + core + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/v1_19_R1/src/main/java/net/artelnatif/nicko/impl/v1_19_R1.java b/v1_19_R1/src/main/java/net/artelnatif/nicko/impl/v1_19_R1.java new file mode 100644 index 0000000..fe98d37 --- /dev/null +++ b/v1_19_R1/src/main/java/net/artelnatif/nicko/impl/v1_19_R1.java @@ -0,0 +1,107 @@ +package net.artelnatif.nicko.impl; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.network.chat.IChatBaseComponent; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.DataWatcher; +import net.minecraft.network.syncher.DataWatcherObject; +import net.minecraft.network.syncher.DataWatcherRegistry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.EntityPlayer; +import net.minecraft.world.entity.player.ProfilePublicKey; +import net.minecraft.world.level.EnumGamemode; +import net.minecraft.world.level.World; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; + +import java.util.Optional; + +public class v1_19_R1 implements Internals { + @Override + public void updateSelf(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final ResourceKey levelResourceKey = entityPlayer.x().ab(); + final PacketPlayOutRespawn respawn = new PacketPlayOutRespawn(entityPlayer.x().Z(), + levelResourceKey, entityPlayer.s.getWorld().getSeed(), + entityPlayer.d.b(), entityPlayer.d.c(), + false, + false, + false, + Optional.empty()); + + final boolean wasFlying = player.isFlying(); + entityPlayer.b.a(respawn); + player.setFlying(wasFlying); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final PacketPlayOutEntityDestroy destroy = new PacketPlayOutEntityDestroy(entityPlayer.getBukkitEntity().getEntityId()); + final PacketPlayOutNamedEntitySpawn spawn = new PacketPlayOutNamedEntitySpawn(entityPlayer); + + final DataWatcher dataWatcher = entityPlayer.ai(); + final DataWatcherObject displayedSkinPartDataWatcher = new DataWatcherObject<>(17, DataWatcherRegistry.a); + dataWatcher.b(displayedSkinPartDataWatcher, (byte) 0x7f); + final PacketPlayOutEntityMetadata entityMetadata = new PacketPlayOutEntityMetadata(entityPlayer.getBukkitEntity().getEntityId(), dataWatcher, true); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + if (onlineEntityPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineEntityPlayer.b.a(destroy); + onlineEntityPlayer.b.a(spawn); + } + onlineEntityPlayer.b.a(entityMetadata); + }); + } + + @Override + public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { + final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); + final String profileName = profile.getName() == null ? player.getName() : profile.getName(); + + final CraftPlayer craftPlayer = (CraftPlayer) player; + final EntityPlayer entityPlayer = craftPlayer.getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profileName); + + if (skinChange || changeOnlyName) { + final ActionResult skinFetch = fetchSkinTextures(profile, reset); + if (!skinFetch.isError()) { + final MojangSkin skin = skinFetch.getResult(); + final PropertyMap properties = gameProfile.getProperties(); + properties.removeAll("textures"); + properties.put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + updateSelf(player); + } + } + + final PacketPlayOutPlayerInfo add = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.a); + final PacketPlayOutPlayerInfo remove = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.e, entityPlayer); + // "It's a Surprise Tool That Will Help Us Later!" + final ProfilePublicKey.a key = remove.b().get(0).e(); + + add.b().clear(); + add.b().add(new PacketPlayOutPlayerInfo.PlayerInfoData(gameProfile, + player.getPing(), + EnumGamemode.a(player.getGameMode().ordinal()), + IChatBaseComponent.a(profileName), + key)); // f mojang + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + onlineEntityPlayer.b.a(remove); + onlineEntityPlayer.b.a(add); + }); + updateOthers(player); + return new ActionResult<>(); + } +} diff --git a/v1_19_R2/pom.xml b/v1_19_R2/pom.xml new file mode 100644 index 0000000..385efc4 --- /dev/null +++ b/v1_19_R2/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + + net.artelnatif + nicko-parent + 1.0-SNAPSHOT + + + v1_19_R2 + 1.0-SNAPSHOT + + + + + net.md-5 + specialsource-maven-plugin + 1.2.4 + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:1.19.3-R0.1-SNAPSHOT:txt:maps-mojang + true + org.spigotmc:spigot:1.19.3-R0.1-SNAPSHOT:jar:remapped-mojang + true + remapped-obf + + + + package + + remap + + remap-spigot + + ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar + org.spigotmc:minecraft-server:1.19.3-R0.1-SNAPSHOT:csrg:maps-spigot + org.spigotmc:spigot:1.19.3-R0.1-SNAPSHOT:jar:remapped-obf + + + + + + + + + + org.spigotmc + spigot + 1.19.3-R0.1-SNAPSHOT + remapped-mojang + provided + + + net.artelnatif + core + 1.0-SNAPSHOT + + + \ 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 new file mode 100644 index 0000000..4059960 --- /dev/null +++ b/v1_19_R2/src/main/java/net/artelnatif/nicko/impl/v1_19_R2.java @@ -0,0 +1,132 @@ +package net.artelnatif.nicko.impl; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.RemoteChatSession; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.ProfilePublicKey; +import net.minecraft.world.level.Level; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; + +import java.lang.reflect.Field; +import java.util.*; + +public class v1_19_R2 implements Internals { + @Override + public void updateSelf(Player player) { + final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + final ServerLevel level = serverPlayer.getLevel(); + final ResourceKey levelResourceKey = serverPlayer.getLevel().dimension(); + final ClientboundRespawnPacket respawn = new ClientboundRespawnPacket(serverPlayer.level.dimensionTypeId(), + levelResourceKey, level.getWorld().getSeed(), + serverPlayer.gameMode.getGameModeForPlayer(), serverPlayer.gameMode.getPreviousGameModeForPlayer(), + level.isDebug(), + level.isFlat(), + (byte) 0x00, + Optional.empty()); + + final boolean wasFlying = player.isFlying(); + serverPlayer.connection.send(respawn); + player.setFlying(wasFlying); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + final ClientboundRemoveEntitiesPacket remove = new ClientboundRemoveEntitiesPacket(serverPlayer.getBukkitEntity().getEntityId()); + final ClientboundAddEntityPacket add = new ClientboundAddEntityPacket(serverPlayer); + + final SynchedEntityData entityData = serverPlayer.getEntityData(); + final EntityDataAccessor skinPartAccessor = new EntityDataAccessor<>(17, EntityDataSerializers.BYTE); + entityData.set(skinPartAccessor, (byte) 0x7f); + final ClientboundSetEntityDataPacket entityMetadata = new ClientboundSetEntityDataPacket(serverPlayer.getBukkitEntity().getEntityId(), entityData.getNonDefaultValues()); + + Bukkit.getOnlinePlayers().forEach(online -> { + final ServerPlayer onlineServerPlayer = ((CraftPlayer) online).getHandle(); + if (onlineServerPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineServerPlayer.connection.send(remove); + onlineServerPlayer.connection.send(add); + } + onlineServerPlayer.connection.send(entityMetadata); + }); + } + + @Override + public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { + final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); + final String profileName = profile.getName() == null ? player.getName() : profile.getName(); + + final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profileName); + + if (skinChange || changeOnlyName) { + final ActionResult skinFetch = fetchSkinTextures(profile, reset); + if (!skinFetch.isError()) { + final MojangSkin skin = skinFetch.getResult(); + final PropertyMap properties = gameProfile.getProperties(); + properties.removeAll("textures"); + properties.put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + updateSelf(player); + } + } + + final ClientboundPlayerInfoUpdatePacket init = ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(Collections.singletonList(serverPlayer)); + final ClientboundPlayerInfoRemovePacket remove = new ClientboundPlayerInfoRemovePacket(Collections.singletonList(player.getUniqueId())); + + RemoteChatSession chatSession; + if (serverPlayer.getChatSession() == null) { + NickoBukkit.getInstance().getLogger().warning("Chat Session of " + serverPlayer.displayName + " is null!"); + NickoBukkit.getInstance().getLogger().warning("If your server is in offline mode/under BungeeCord you can safely ignore this message."); + chatSession = null; + } else { + final UUID uuid = serverPlayer.getChatSession().sessionId(); + final ProfilePublicKey ppk = serverPlayer.getChatSession().profilePublicKey(); + chatSession = new RemoteChatSession(uuid, ppk); + } + + spoofPlayerInfoPacket(init, Collections.singletonList(new ClientboundPlayerInfoUpdatePacket.Entry( + player.getUniqueId(), + gameProfile, + true, + serverPlayer.latency, + serverPlayer.gameMode.getGameModeForPlayer(), + Component.literal(profileName), + chatSession == null ? null : chatSession.asData() + ))); + + Bukkit.getOnlinePlayers().forEach(online -> { + final ServerPlayer onlinePlayer = ((CraftPlayer) online).getHandle(); + onlinePlayer.connection.send(remove); + onlinePlayer.connection.send(init); + }); + updateOthers(player); + return new ActionResult<>(); + } + + private void spoofPlayerInfoPacket(Object object, Object newValue) { + try { + final Field field = object.getClass().getDeclaredField("b"); + field.setAccessible(true); + field.set(object, newValue); + } catch (NoSuchFieldException | IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to spoof packet, that's bad! (" + e.getMessage() + ")"); + } + } +} diff --git a/v1_19_R3/pom.xml b/v1_19_R3/pom.xml new file mode 100644 index 0000000..eefd19d --- /dev/null +++ b/v1_19_R3/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + + net.artelnatif + nicko-parent + 1.0-SNAPSHOT + + + v1_19_R3 + 1.0-SNAPSHOT + + + + + net.md-5 + specialsource-maven-plugin + 1.2.4 + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:1.19.4-R0.1-SNAPSHOT:txt:maps-mojang + true + org.spigotmc:spigot:1.19.4-R0.1-SNAPSHOT:jar:remapped-mojang + true + remapped-obf + + + + package + + remap + + remap-spigot + + ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar + org.spigotmc:minecraft-server:1.19.4-R0.1-SNAPSHOT:csrg:maps-spigot + org.spigotmc:spigot:1.19.4-R0.1-SNAPSHOT:jar:remapped-obf + + + + + + + + + + org.spigotmc + spigot + 1.19.4-R0.1-SNAPSHOT + remapped-mojang + provided + + + net.artelnatif + core + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/v1_19_R3/src/main/java/net/artelnatif/nicko/impl/v1_19_R3.java b/v1_19_R3/src/main/java/net/artelnatif/nicko/impl/v1_19_R3.java new file mode 100644 index 0000000..d0ef0eb --- /dev/null +++ b/v1_19_R3/src/main/java/net/artelnatif/nicko/impl/v1_19_R3.java @@ -0,0 +1,140 @@ +package net.artelnatif.nicko.impl; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.RemoteChatSession; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.ProfilePublicKey; +import net.minecraft.world.level.Level; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.Optional; +import java.util.UUID; +import java.util.logging.Logger; + +public class v1_19_R3 implements Internals { + final Logger logger = Bukkit.getLogger(); + + @Override + public void updateSelf(Player player) { + final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + final ServerLevel level = serverPlayer.getLevel(); + final ResourceKey levelResourceKey = serverPlayer.getLevel().dimension(); + final ClientboundRespawnPacket respawn = new ClientboundRespawnPacket(serverPlayer.level.dimensionTypeId(), + levelResourceKey, level.getWorld().getSeed(), + serverPlayer.gameMode.getGameModeForPlayer(), serverPlayer.gameMode.getPreviousGameModeForPlayer(), + level.isDebug(), + level.isFlat(), + (byte) 0x00, + Optional.empty()); + + final boolean wasFlying = player.isFlying(); + serverPlayer.connection.send(respawn); + player.setFlying(wasFlying); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + final ClientboundRemoveEntitiesPacket remove = new ClientboundRemoveEntitiesPacket(serverPlayer.getBukkitEntity().getEntityId()); + final ClientboundAddEntityPacket add = new ClientboundAddEntityPacket(serverPlayer); + + final SynchedEntityData entityData = serverPlayer.getEntityData(); + final EntityDataAccessor skinPartAccessor = new EntityDataAccessor<>(17, EntityDataSerializers.BYTE); + entityData.set(skinPartAccessor, (byte) 0x7f); + final ClientboundSetEntityDataPacket entityMetadata = new ClientboundSetEntityDataPacket(serverPlayer.getBukkitEntity().getEntityId(), entityData.getNonDefaultValues()); + + Bukkit.getOnlinePlayers().forEach(online -> { + final ServerPlayer onlineServerPlayer = ((CraftPlayer) online).getHandle(); + if (onlineServerPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineServerPlayer.connection.send(remove); + onlineServerPlayer.connection.send(add); + } + onlineServerPlayer.connection.send(entityMetadata); + }); + } + + @Override + public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { + final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); + final String profileName = profile.getName() == null ? player.getName() : profile.getName(); + + final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profileName); + + if (skinChange || changeOnlyName) { + final ActionResult skinFetch = fetchSkinTextures(profile, reset); + if (!skinFetch.isError()) { + final MojangSkin skin = skinFetch.getResult(); + final PropertyMap properties = gameProfile.getProperties(); + properties.removeAll("textures"); + properties.put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + updateSelf(player); + } + } + + final ClientboundPlayerInfoUpdatePacket init = ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(Collections.singletonList(serverPlayer)); + final ClientboundPlayerInfoRemovePacket remove = new ClientboundPlayerInfoRemovePacket(Collections.singletonList(player.getUniqueId())); + + RemoteChatSession chatSession; + if (serverPlayer.getChatSession() == null) { + NickoBukkit.getInstance().getLogger().warning("Chat Session of " + serverPlayer.displayName + " is null!"); + NickoBukkit.getInstance().getLogger().warning("If your server is in offline mode/under BungeeCord you can safely ignore this message."); + chatSession = null; + } else { + final UUID uuid = serverPlayer.getChatSession().sessionId(); + final ProfilePublicKey ppk = serverPlayer.getChatSession().profilePublicKey(); + chatSession = new RemoteChatSession(uuid, ppk); + } + + spoofPlayerInfoPacket(init, Collections.singletonList(new ClientboundPlayerInfoUpdatePacket.Entry( + player.getUniqueId(), + gameProfile, + true, + serverPlayer.latency, + serverPlayer.gameMode.getGameModeForPlayer(), + Component.literal(profileName), + chatSession == null ? null : chatSession.asData() + ))); + + + Bukkit.getOnlinePlayers().forEach(online -> { + final ServerPlayer onlinePlayer = ((CraftPlayer) online).getHandle(); + onlinePlayer.connection.send(remove); + onlinePlayer.connection.send(init); + }); + // TODO: 3/17/23 Update signature to avoid duplicate for loop + updateOthers(player); + return new ActionResult<>(); + } + + private void spoofPlayerInfoPacket(Object object, Object newValue) { + try { + final Field field = object.getClass().getDeclaredField("b"); + field.setAccessible(true); + field.set(object, newValue); + } catch (NoSuchFieldException | IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to spoof packet, that's bad! (" + e.getMessage() + ")"); + } + } +}