diff --git a/.gitignore b/.gitignore index 27d5dde..df446fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # User-specific stuff .idea/ +.vscode/ *.iml *.ipr @@ -8,10 +9,10 @@ # IntelliJ out/ -# Compiled class file +# Compiled class files *.class -# Log file +# Log files *.log # BlueJ files @@ -25,12 +26,10 @@ out/ *.tar.gz *.rar -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +# Virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file +# Temporary files which can be created if a process still has a handle open on a deleted file .fuse_hidden* # KDE directory preferences @@ -42,7 +41,7 @@ hs_err_pid* # .nfs files are created when an open file is removed but is still being accessed .nfs* -# General +# General system files .DS_Store .AppleDouble .LSOverride @@ -75,10 +74,10 @@ Thumbs.db:encryptable ehthumbs.db ehthumbs_vista.db -# Dump file +# Dump files *.stackdump -# Folder config file +# Folder config files [Dd]esktop.ini # Recycle Bin used on file shares @@ -94,8 +93,10 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk +# Maven target directory target/ +# Maven build files and backup files pom.xml.tag pom.xml.releaseBackup pom.xml.versionsBackup diff --git a/Code/pom.xml b/Code/pom.xml index 9c3d5cf..cd1d8df 100644 --- a/Code/pom.xml +++ b/Code/pom.xml @@ -6,7 +6,7 @@ com.github.happyuky7 SepareWorldItems - 1.2.22 + 1.2.23-DEV jar SepareWorldItems @@ -27,6 +27,14 @@ sonatype https://oss.sonatype.org/content/groups/public/ + + essentials-releases + https://repo.essentialsx.net/releases/ + + + paper-repo + https://papermc.io/repo/repository/maven-public/ + @@ -42,6 +50,12 @@ json 20240303 + + net.essentialsx + EssentialsX + 2.20.1 + provided + @@ -92,4 +106,31 @@ + + + + + + org.checkerframework + checker-qual + 3.33.0 + + + org.spigotmc + spigot-api + 1.21-R0.1-SNAPSHOT + + + com.google.errorprone + error_prone_annotations + 2.18.0 + + + org.yaml + snakeyaml + 2.2 + + + + diff --git a/Code/src/main/java/com/github/happyuky7/separeworlditems/SepareWorldItems.java b/Code/src/main/java/com/github/happyuky7/separeworlditems/SepareWorldItems.java index d57b0e9..e811468 100644 --- a/Code/src/main/java/com/github/happyuky7/separeworlditems/SepareWorldItems.java +++ b/Code/src/main/java/com/github/happyuky7/separeworlditems/SepareWorldItems.java @@ -9,7 +9,8 @@ import com.github.happyuky7.separeworlditems.commands.SepareWorldItemsCMD; import com.github.happyuky7.separeworlditems.filemanagers.FileManager; -import com.github.happyuky7.separeworlditems.listeners.WorldChangeEvent; +import com.github.happyuky7.separeworlditems.listeners.Integration.EssentialsX.HomeEvent; +import com.github.happyuky7.separeworlditems.listeners.base.WorldChangeEvent; import com.github.happyuky7.separeworlditems.utils.ConvertTime; import com.github.happyuky7.separeworlditems.utils.DownloadTranslations; import com.github.happyuky7.separeworlditems.utils.BackupManager; @@ -179,7 +180,7 @@ private void logPluginShutdownDetails() { * does not match the required version. */ private void verifyConfigVersion() { - if (!getConfig().getString("config-version").equalsIgnoreCase("1.2.22")) { + if (!getConfig().getString("config-version").equalsIgnoreCase("1.2.23-DEV")) { Bukkit.getConsoleSender() .sendMessage(MessageColors.getMsgColor("&3&m------------------------------------")); Bukkit.getConsoleSender().sendMessage(MessageColors.getMsgColor("&f [Error]: &cConfig Version ERROR.")); @@ -204,6 +205,7 @@ public void registerCommands() { public void registerEvents() { PluginManager pm = getServer().getPluginManager(); pm.registerEvents(new WorldChangeEvent(this), this); + pm.registerEvents(new HomeEvent(this), this); } /** diff --git a/Code/src/main/java/com/github/happyuky7/separeworlditems/data/loaders/ExperienceLoader.java b/Code/src/main/java/com/github/happyuky7/separeworlditems/data/loaders/ExperienceLoader.java new file mode 100644 index 0000000..4ba35a0 --- /dev/null +++ b/Code/src/main/java/com/github/happyuky7/separeworlditems/data/loaders/ExperienceLoader.java @@ -0,0 +1,21 @@ +package com.github.happyuky7.separeworlditems.data.loaders; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; + +/** + * Utility class for loading player experience data from a configuration file. + */ +public class ExperienceLoader { + + /** + * Loads the player's experience and level from the configuration file. + * + * @param player The player whose experience and level are being loaded. + * @param config The configuration file where the data is loaded from. + */ + public static void load(Player player, FileConfiguration config) { + player.setExp((float) config.getDouble("exp", 0.0F)); + player.setLevel(config.getInt("exp-level", 0)); + } +} diff --git a/Code/src/main/java/com/github/happyuky7/separeworlditems/data/loaders/InventoryLoader.java b/Code/src/main/java/com/github/happyuky7/separeworlditems/data/loaders/InventoryLoader.java new file mode 100644 index 0000000..b3a2d13 --- /dev/null +++ b/Code/src/main/java/com/github/happyuky7/separeworlditems/data/loaders/InventoryLoader.java @@ -0,0 +1,31 @@ +package com.github.happyuky7.separeworlditems.data.loaders; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; + +/** + * Utility class for loading player inventory data from a configuration file. + */ +public class InventoryLoader { + + /** + * Loads the player's inventory, ender chest, and armor contents from the + * configuration file. + * + * @param player The player whose inventory is being loaded. + * @param config The configuration file where the data is loaded from. + */ + public static void load(Player player, FileConfiguration config) { + if (config.contains("inventory")) { + for (String key : config.getConfigurationSection("inventory").getKeys(false)) { + player.getInventory().setItem(Integer.parseInt(key), config.getItemStack("inventory." + key)); + } + } + + if (config.getBoolean("Options.ender-chest", true) && config.contains("ender_chest")) { + for (String key : config.getConfigurationSection("ender_chest").getKeys(false)) { + player.getEnderChest().setItem(Integer.parseInt(key), config.getItemStack("ender_chest." + key)); + } + } + } +} diff --git a/Code/src/main/java/com/github/happyuky7/separeworlditems/data/loaders/PlayerDataLoader.java b/Code/src/main/java/com/github/happyuky7/separeworlditems/data/loaders/PlayerDataLoader.java new file mode 100644 index 0000000..9ab77ad --- /dev/null +++ b/Code/src/main/java/com/github/happyuky7/separeworlditems/data/loaders/PlayerDataLoader.java @@ -0,0 +1,60 @@ +package com.github.happyuky7.separeworlditems.data.loaders; + +import org.bukkit.GameMode; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +/** + * Utility class for loading player data such as attributes, potion effects, and + * off-hand items from a configuration file. + */ +public class PlayerDataLoader { + + /** + * Loads the player's attributes such as gamemode, flying state, health, hunger, + * and experience from the configuration file. + * + * @param player The player whose attributes are being loaded. + * @param config The configuration file where the data is loaded from. + */ + public static void loadAttributes(Player player, FileConfiguration config) { + player.setGameMode(GameMode.valueOf(config.getString("gamemode", "SURVIVAL"))); + player.setFlying(config.getBoolean("flying", false)); + player.setHealth(config.getDouble("health", 20.0D)); + player.setFoodLevel(config.getInt("hunger", 20)); + ExperienceLoader.load(player, config); + } + + /** + * Loads the player's active potion effects from the configuration file. + * + * @param player The player whose potion effects are being loaded. + * @param config The configuration file where the data is loaded from. + */ + public static void loadPotionEffects(Player player, FileConfiguration config) { + if (config.contains("potion_effect")) { + for (String key : config.getConfigurationSection("potion_effect").getKeys(false)) { + @SuppressWarnings("deprecation") + PotionEffect effect = new PotionEffect( + PotionEffectType.getByName(config.getString("potion_effect." + key + ".type")), + config.getInt("potion_effect." + key + ".duration"), + config.getInt("potion_effect." + key + ".level")); + player.addPotionEffect(effect); + } + } + } + + /** + * Loads the player's off-hand item from the configuration file. + * + * @param player The player whose off-hand item is being loaded. + * @param config The configuration file where the data is loaded from. + */ + public static void loadOffHandItem(Player player, FileConfiguration config) { + if (config.contains("off_hand_item")) { + player.getInventory().setItemInOffHand(config.getItemStack("off_hand_item")); + } + } +} diff --git a/Code/src/main/java/com/github/happyuky7/separeworlditems/data/models/PlayerData.java b/Code/src/main/java/com/github/happyuky7/separeworlditems/data/models/PlayerData.java new file mode 100644 index 0000000..75b8bde --- /dev/null +++ b/Code/src/main/java/com/github/happyuky7/separeworlditems/data/models/PlayerData.java @@ -0,0 +1,319 @@ +package com.github.happyuky7.separeworlditems.data.models; + +import org.bukkit.GameMode; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; + +import java.util.List; + +/** + * This class holds all the relevant data for a player. + */ +public class PlayerData { + + private GameMode gameMode; + private boolean isFlying; + private double health; + private int hunger; + private float experience; + private int expLevel; + private List potionEffects; + private ItemStack[] inventory; + private ItemStack[] enderChest; + private ItemStack helmet; + private ItemStack chestplate; + private ItemStack leggings; + private ItemStack boots; + private ItemStack offHandItem; + + /** + * Constructor to initialize all fields. + * + * @param gameMode The player's game mode. + * @param isFlying Whether the player is flying. + * @param health The player's health. + * @param hunger The player's hunger level. + * @param experience The player's experience. + * @param expLevel The player's experience level. + * @param potionEffects The player's active potion effects. + * @param inventory The player's inventory contents. + * @param enderChest The player's ender chest contents. + * @param helmet The player's helmet item. + * @param chestplate The player's chestplate item. + * @param leggings The player's leggings item. + * @param boots The player's boots item. + * @param offHandItem The player's off-hand item. + */ + public PlayerData(GameMode gameMode, boolean isFlying, double health, int hunger, float experience, int expLevel, + List potionEffects, ItemStack[] inventory, ItemStack[] enderChest, + ItemStack helmet, ItemStack chestplate, ItemStack leggings, ItemStack boots, ItemStack offHandItem) { + this.gameMode = gameMode; + this.isFlying = isFlying; + this.health = health; + this.hunger = hunger; + this.experience = experience; + this.expLevel = expLevel; + this.potionEffects = potionEffects; + this.inventory = inventory; + this.enderChest = enderChest; + this.helmet = helmet; + this.chestplate = chestplate; + this.leggings = leggings; + this.boots = boots; + this.offHandItem = offHandItem; + } + + // Getters and Setters + + /** + * Gets the player's game mode. + * + * @return The player's game mode. + */ + public GameMode getGameMode() { + return gameMode; + } + + /** + * Sets the player's game mode. + * + * @param gameMode The player's new game mode. + */ + public void setGameMode(GameMode gameMode) { + this.gameMode = gameMode; + } + + /** + * Checks if the player is flying. + * + * @return True if the player is flying, otherwise false. + */ + public boolean isFlying() { + return isFlying; + } + + /** + * Sets the player's flying state. + * + * @param flying The player's new flying state. + */ + public void setFlying(boolean flying) { + isFlying = flying; + } + + /** + * Gets the player's health. + * + * @return The player's health. + */ + public double getHealth() { + return health; + } + + /** + * Sets the player's health. + * + * @param health The player's new health. + */ + public void setHealth(double health) { + this.health = health; + } + + /** + * Gets the player's hunger level. + * + * @return The player's hunger level. + */ + public int getHunger() { + return hunger; + } + + /** + * Sets the player's hunger level. + * + * @param hunger The player's new hunger level. + */ + public void setHunger(int hunger) { + this.hunger = hunger; + } + + /** + * Gets the player's experience. + * + * @return The player's experience. + */ + public float getExperience() { + return experience; + } + + /** + * Sets the player's experience. + * + * @param experience The player's new experience. + */ + public void setExperience(float experience) { + this.experience = experience; + } + + /** + * Gets the player's experience level. + * + * @return The player's experience level. + */ + public int getExpLevel() { + return expLevel; + } + + /** + * Sets the player's experience level. + * + * @param expLevel The player's new experience level. + */ + public void setExpLevel(int expLevel) { + this.expLevel = expLevel; + } + + /** + * Gets the player's active potion effects. + * + * @return A list of the player's active potion effects. + */ + public List getPotionEffects() { + return potionEffects; + } + + /** + * Sets the player's active potion effects. + * + * @param potionEffects The player's new active potion effects. + */ + public void setPotionEffects(List potionEffects) { + this.potionEffects = potionEffects; + } + + /** + * Gets the player's inventory contents. + * + * @return The player's inventory contents. + */ + public ItemStack[] getInventory() { + return inventory; + } + + /** + * Sets the player's inventory contents. + * + * @param inventory The player's new inventory contents. + */ + public void setInventory(ItemStack[] inventory) { + this.inventory = inventory; + } + + /** + * Gets the player's ender chest contents. + * + * @return The player's ender chest contents. + */ + public ItemStack[] getEnderChest() { + return enderChest; + } + + /** + * Sets the player's ender chest contents. + * + * @param enderChest The player's new ender chest contents. + */ + public void setEnderChest(ItemStack[] enderChest) { + this.enderChest = enderChest; + } + + /** + * Gets the player's helmet item. + * + * @return The player's helmet item. + */ + public ItemStack getHelmet() { + return helmet; + } + + /** + * Sets the player's helmet item. + * + * @param helmet The player's new helmet item. + */ + public void setHelmet(ItemStack helmet) { + this.helmet = helmet; + } + + /** + * Gets the player's chestplate item. + * + * @return The player's chestplate item. + */ + public ItemStack getChestplate() { + return chestplate; + } + + /** + * Sets the player's chestplate item. + * + * @param chestplate The player's new chestplate item. + */ + public void setChestplate(ItemStack chestplate) { + this.chestplate = chestplate; + } + + /** + * Gets the player's leggings item. + * + * @return The player's leggings item. + */ + public ItemStack getLeggings() { + return leggings; + } + + /** + * Sets the player's leggings item. + * + * @param leggings The player's new leggings item. + */ + public void setLeggings(ItemStack leggings) { + this.leggings = leggings; + } + + /** + * Gets the player's boots item. + * + * @return The player's boots item. + */ + public ItemStack getBoots() { + return boots; + } + + /** + * Sets the player's boots item. + * + * @param boots The player's new boots item. + */ + public void setBoots(ItemStack boots) { + this.boots = boots; + } + + /** + * Gets the player's off-hand item. + * + * @return The player's off-hand item. + */ + public ItemStack getOffHandItem() { + return offHandItem; + } + + /** + * Sets the player's off-hand item. + * + * @param offHandItem The player's new off-hand item. + */ + public void setOffHandItem(ItemStack offHandItem) { + this.offHandItem = offHandItem; + } +} diff --git a/Code/src/main/java/com/github/happyuky7/separeworlditems/data/savers/ExperienceSaver.java b/Code/src/main/java/com/github/happyuky7/separeworlditems/data/savers/ExperienceSaver.java new file mode 100644 index 0000000..83cf0d0 --- /dev/null +++ b/Code/src/main/java/com/github/happyuky7/separeworlditems/data/savers/ExperienceSaver.java @@ -0,0 +1,21 @@ +package com.github.happyuky7.separeworlditems.data.savers; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; + +/** + * Utility class for saving player experience data to a configuration file. + */ +public class ExperienceSaver { + + /** + * Saves the player's experience and level to the configuration file. + * + * @param player The player whose experience and level are being saved. + * @param config The configuration file where the data is saved. + */ + public static void save(Player player, FileConfiguration config) { + config.set("exp", player.getExp()); + config.set("exp-level", player.getLevel()); + } +} diff --git a/Code/src/main/java/com/github/happyuky7/separeworlditems/data/savers/InventorySaver.java b/Code/src/main/java/com/github/happyuky7/separeworlditems/data/savers/InventorySaver.java new file mode 100644 index 0000000..9c33547 --- /dev/null +++ b/Code/src/main/java/com/github/happyuky7/separeworlditems/data/savers/InventorySaver.java @@ -0,0 +1,40 @@ +package com.github.happyuky7.separeworlditems.data.savers; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +/** + * Utility class for saving player inventory data to a configuration file. + */ +public class InventorySaver { + + /** + * Saves the player's inventory, ender chest, and armor contents to the + * configuration file. + * + * @param player The player whose inventory is being saved. + * @param config The configuration file where the data is saved. + */ + public static void save(Player player, FileConfiguration config) { + // Save inventory contents + int index = 0; + for (ItemStack item : player.getInventory().getContents()) { + config.set("inventory." + index++, item); + } + + // Save ender chest contents if enabled + if (config.getBoolean("Options.ender-chest", true)) { + index = 0; + for (ItemStack item : player.getEnderChest().getContents()) { + config.set("ender_chest." + index++, item); + } + } + + // Save armor contents + config.set("armor_contents.helmet", player.getInventory().getHelmet()); + config.set("armor_contents.chestplate", player.getInventory().getChestplate()); + config.set("armor_contents.leggings", player.getInventory().getLeggings()); + config.set("armor_contents.boots", player.getInventory().getBoots()); + } +} diff --git a/Code/src/main/java/com/github/happyuky7/separeworlditems/data/savers/PlayerDataSaver.java b/Code/src/main/java/com/github/happyuky7/separeworlditems/data/savers/PlayerDataSaver.java new file mode 100644 index 0000000..05d050c --- /dev/null +++ b/Code/src/main/java/com/github/happyuky7/separeworlditems/data/savers/PlayerDataSaver.java @@ -0,0 +1,66 @@ +package com.github.happyuky7.separeworlditems.data.savers; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; + +/** + * Utility class for saving player data such as attributes, potion effects, and + * off-hand items to a configuration file. + */ +public class PlayerDataSaver { + + /** + * Saves the player's attributes such as gamemode, flying state, health, hunger, + * and experience to the configuration file. + * + * @param player The player whose attributes are being saved. + * @param config The configuration file where the data is saved. + */ + public static void saveAttributes(Player player, FileConfiguration config) { + // Save gamemode + if (config.getBoolean("Options.gamemode", true)) { + config.set("gamemode", player.getGameMode().toString()); + } + + // Save flying state + if (config.getBoolean("Options.flying", true)) { + config.set("flying", player.isFlying()); + } + + // Save health and hunger + config.set("health", config.getBoolean("Options.health-options.health-default-save", true) + ? player.getHealth() + : 20.0D); + config.set("hunger", player.getFoodLevel()); + + ExperienceSaver.save(player, config); + } + + /** + * Saves the player's active potion effects to the configuration file. + * + * @param player The player whose potion effects are being saved. + * @param config The configuration file where the data is saved. + */ + @SuppressWarnings("deprecation") + public static void savePotionEffects(Player player, FileConfiguration config) { + int index = 0; + for (PotionEffect effect : player.getActivePotionEffects()) { + config.set("potion_effect." + index + ".type", effect.getType().getName()); + config.set("potion_effect." + index + ".level", effect.getAmplifier()); + config.set("potion_effect." + index + ".duration", effect.getDuration()); + index++; + } + } + + /** + * Saves the player's off-hand item to the configuration file. + * + * @param player The player whose off-hand item is being saved. + * @param config The configuration file where the data is saved. + */ + public static void saveOffHandItem(Player player, FileConfiguration config) { + config.set("off_hand_item", player.getInventory().getItemInOffHand()); + } +} diff --git a/Code/src/main/java/com/github/happyuky7/separeworlditems/listeners/Integration/EssentialsX/HomeEvent.java b/Code/src/main/java/com/github/happyuky7/separeworlditems/listeners/Integration/EssentialsX/HomeEvent.java new file mode 100644 index 0000000..9ff7cd0 --- /dev/null +++ b/Code/src/main/java/com/github/happyuky7/separeworlditems/listeners/Integration/EssentialsX/HomeEvent.java @@ -0,0 +1,193 @@ +package com.github.happyuky7.separeworlditems.listeners.Integration.EssentialsX; + +import com.github.happyuky7.separeworlditems.SepareWorldItems; +import com.github.happyuky7.separeworlditems.data.loaders.InventoryLoader; +import com.github.happyuky7.separeworlditems.data.loaders.PlayerDataLoader; +import com.github.happyuky7.separeworlditems.data.savers.InventorySaver; +import com.github.happyuky7.separeworlditems.data.savers.PlayerDataSaver; +import com.github.happyuky7.separeworlditems.filemanagers.FileManager2; +import com.github.happyuky7.separeworlditems.utils.TeleportationManager; + +import net.ess3.api.events.UserTeleportHomeEvent; + +import org.bukkit.GameMode; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +import java.io.File; + +/** + * Event listener that handles player teleport events specifically related to + * home teleports. + * Integrates with the EssentialsX plugin to manage player data during + * teleportation. + */ +public class HomeEvent implements Listener { + + private final SepareWorldItems plugin; + + /** + * Constructor for initializing the HomeEvent listener with the main plugin + * instance. + * + * @param plugin The main plugin instance. + */ + public HomeEvent(SepareWorldItems plugin) { + this.plugin = plugin; + } + + /** + * Handles the user teleportation event when a player teleports to their home. + * It ensures that player data is saved and reloaded appropriately for the + * target world. + * + * @param event The event triggered when a player teleports to their home. + */ + @EventHandler + public void onUserTeleportHome(UserTeleportHomeEvent event) { + @SuppressWarnings("deprecation") + Player player = event.getUser().getBase().getPlayer(); + + // Skip handling if the player is already in a teleportation process + if (TeleportationManager.isTeleporting(player.getUniqueId())) { + return; // Player is already teleporting, skip further handling + } + + String fromWorld = player.getWorld().getName(); // Current world + String toWorld = event.getHomeLocation().getWorld().getName(); // Target world (home) + + // Check if both the source and destination worlds are configured in the plugin + FileConfiguration config = plugin.getConfig(); + if (config.contains("worlds." + fromWorld) && config.contains("worlds." + toWorld)) { + String fromGroup = config.getString("worlds." + fromWorld); + String toGroup = config.getString("worlds." + toWorld); + + // Mark the player as teleporting if the worlds differ + if (!fromWorld.equals(toWorld)) { + TeleportationManager.setTeleporting(player.getUniqueId(), true); + } + + // Save player data for the source world + savePlayerData(player, fromGroup); + + // Load or reload player data based on the group of the target world + if (!fromGroup.equals(toGroup)) { + loadPlayerData(player, toGroup); + } else { + reloadAllPlayerData(player, fromGroup); + } + } + } + + /** + * Saves the player's current data (inventory, attributes, potion effects, etc.) + * to a group-specific configuration file before teleportation. + * + * @param player The player whose data is being saved. + * @param groupName The group name associated with the player's current world. + */ + private void savePlayerData(Player player, String groupName) { + File file = new File(plugin.getDataFolder() + File.separator + "groups" + + File.separator + groupName + File.separator + player.getName() + "-" + player.getUniqueId() + ".yml"); + FileConfiguration config = FileManager2.getYaml(file); + + // Save various player data into the configuration file + InventorySaver.save(player, config); + PlayerDataSaver.saveAttributes(player, config); + PlayerDataSaver.savePotionEffects(player, config); + PlayerDataSaver.saveOffHandItem(player, config); + + // Save the updated configuration to disk + FileManager2.saveConfiguration(file, config); + + // Clear the player's state in preparation for the teleportation + clearPlayerState(player); + } + + /** + * Loads the player's data from a group-specific configuration file after + * teleportation. + * + * @param player The player whose data is being loaded. + * @param groupName The group name associated with the target world. + */ + private void loadPlayerData(Player player, String groupName) { + File file = new File(plugin.getDataFolder() + File.separator + "groups" + + File.separator + groupName + File.separator + player.getName() + "-" + player.getUniqueId() + ".yml"); + FileConfiguration config = FileManager2.getYaml(file); + + // Load player data (inventory, attributes, potion effects, etc.) + InventoryLoader.load(player, config); + PlayerDataLoader.loadAttributes(player, config); + PlayerDataLoader.loadPotionEffects(player, config); + PlayerDataLoader.loadOffHandItem(player, config); + } + + /** + * Clears the player's state by resetting inventory, ender chest, game mode, + * health, + * food level, experience, and other attributes to ensure a clean state when the + * player transitions between worlds. + * + * @param player The player whose state is being cleared. + */ + private void clearPlayerState(Player player) { + // Clear the player's inventory and ender chest + player.getInventory().clear(); + player.getEnderChest().clear(); + + // Reset the player's state to a default, clean state + player.setFlying(false); + player.setGameMode(GameMode.SURVIVAL); + player.setHealth(20.0D); // Full health + player.setFoodLevel(20); // Full hunger + player.setExp(0.0F); // No experience + player.setLevel(0); // No levels + } + + /** + * Reloads all of the player's data (inventory, attributes, potion effects, + * etc.) + * from a group-specific configuration file. + * + * @param player The player whose data is being reloaded. + * @param groupName The group name associated with the player's world. + */ + private void reloadAllPlayerData(Player player, String groupName) { + File file = new File(plugin.getDataFolder() + File.separator + "groups" + + File.separator + groupName + File.separator + player.getName() + "-" + player.getUniqueId() + ".yml"); + FileConfiguration config = FileManager2.getYaml(file); + + // Reload the player's data (inventory, attributes, potion effects, off-hand, + // etc.) + InventoryLoader.load(player, config); + PlayerDataLoader.loadAttributes(player, config); + PlayerDataLoader.loadPotionEffects(player, config); + PlayerDataLoader.loadOffHandItem(player, config); + + // Reload the player's experience and level if necessary + reloadExperienceAndLevel(player, config); + } + + /** + * Reloads the player's experience and level from a group-specific configuration + * file to ensure they match the state from the previous world or group. + * + * @param player The player whose experience and level are being reloaded. + * @param config The configuration file containing the player's data. + */ + private void reloadExperienceAndLevel(Player player, FileConfiguration config) { + // Check if experience and level data are present in the configuration + if (config.contains("exp") && config.contains("exp-level")) { + // Retrieve and set the player's experience and level + float experience = (float) config.getDouble("exp", 0.0F); + int level = config.getInt("exp-level", 0); + + // Set the player's experience and level from the loaded data + player.setExp(experience); + player.setLevel(level); + } + } +} diff --git a/Code/src/main/java/com/github/happyuky7/separeworlditems/listeners/WorldChangeEvent.java b/Code/src/main/java/com/github/happyuky7/separeworlditems/listeners/WorldChangeEvent.java deleted file mode 100644 index 8934f76..0000000 --- a/Code/src/main/java/com/github/happyuky7/separeworlditems/listeners/WorldChangeEvent.java +++ /dev/null @@ -1,287 +0,0 @@ -package com.github.happyuky7.separeworlditems.listeners; - -/* - * Code by: Happyuky7 - * GitHub: https://github.com/Happyuky7 - * License: Custom - * Link: https://github.com/Happyuky7/SEPARE-WORLD-ITEMS - */ - -import com.github.happyuky7.separeworlditems.SepareWorldItems; -import com.github.happyuky7.separeworlditems.filemanagers.FileManager2; -import com.github.happyuky7.separeworlditems.utils.MessageColors; -import org.bukkit.GameMode; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerChangedWorldEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; - -import java.io.File; - -/** - * Event listener for handling player world change events and managing player - * data - * specific to different worlds. - */ -public class WorldChangeEvent implements Listener { - - private final SepareWorldItems plugin; - - /** - * Constructor for WorldChangeEvent. - * - * @param plugin The main plugin instance. - */ - public WorldChangeEvent(SepareWorldItems plugin) { - this.plugin = plugin; - } - - /** - * Event handler for PlayerChangedWorldEvent. Manages inventory and player state - * when a player changes worlds. - * - * @param event The PlayerChangedWorldEvent. - */ - @EventHandler - public void onWorldChange(PlayerChangedWorldEvent event) { - Player player = event.getPlayer(); - - // Handle bypass option - if (plugin.playerlist1.contains(player.getUniqueId())) { - if (plugin.getConfig().getBoolean("Options.bypass-world-options.use_bypass", true)) { - player.sendMessage( - MessageColors.getMsgColor(plugin.getLangs().getString("general.bypass.bypass-warning-alert"))); - return; - } - plugin.playerlist1.remove(player.getUniqueId()); - } - - // Get source and target world names - String fromWorld = event.getFrom().getName(); - String toWorld = player.getWorld().getName(); - - FileConfiguration config = plugin.getConfig(); - - if (config.contains("worlds." + fromWorld) && config.contains("worlds." + toWorld)) { - String fromGroup = config.getString("worlds." + fromWorld); - String toGroup = config.getString("worlds." + toWorld); - - if (!fromGroup.equals(toGroup)) { - savePlayerData(player, fromGroup); - loadPlayerData(player, toGroup); - } - } - } - - /** - * Saves the player's current data to a group-specific configuration file. - * - * @param player the player whose data is being saved - * @param groupName the group name associated with the player's current world - */ - private void savePlayerData(Player player, String groupName) { - File file = new File(plugin.getDataFolder() + File.separator + "groups" - + File.separator + groupName + File.separator + player.getName() + "-" + player.getUniqueId() + ".yml"); - FileConfiguration config = FileManager2.getYaml(file); - - saveInventory(player, config); - savePlayerAttributes(player, config); - savePotionEffects(player, config); - saveOffHandItem(player, config); - - FileManager2.saveConfiguration(file, config); - - clearPlayerState(player); - } - - /** - * Loads the player's data from a group-specific configuration file. - * - * @param player the player whose data is being loaded - * @param groupName the group name associated with the player's target world - */ - private void loadPlayerData(Player player, String groupName) { - File file = new File(plugin.getDataFolder() + File.separator + "groups" - + File.separator + groupName + File.separator + player.getName() + "-" + player.getUniqueId() + ".yml"); - FileConfiguration config = FileManager2.getYaml(file); - - loadInventory(player, config); - loadPlayerAttributes(player, config); - loadPotionEffects(player, config); - loadOffHandItem(player, config); - } - - /** - * Saves the player's inventory, ender chest, and armor contents to the - * configuration file. - * - * @param player The player whose inventory is being saved. - * @param config The configuration file where the data is saved. - */ - private void saveInventory(Player player, FileConfiguration config) { - // Save inventory contents - int index = 0; - for (ItemStack item : player.getInventory().getContents()) { - config.set("inventory." + index++, item); - } - - // Save ender chest contents if enabled - if (plugin.getConfig().getBoolean("Options.ender-chest", true)) { - index = 0; - for (ItemStack item : player.getEnderChest().getContents()) { - config.set("ender_chest." + index++, item); - } - } - - // Save armor contents - config.set("armor_contents.helmet", player.getInventory().getHelmet()); - config.set("armor_contents.chestplate", player.getInventory().getChestplate()); - config.set("armor_contents.leggings", player.getInventory().getLeggings()); - config.set("armor_contents.boots", player.getInventory().getBoots()); - } - - /** - * Saves the player's attributes such as gamemode, flying state, health, hunger, - * and experience. - * - * @param player The player whose attributes are being saved. - * @param config The configuration file where the data is saved. - */ - private void savePlayerAttributes(Player player, FileConfiguration config) { - // Save gamemode - if (plugin.getConfig().getBoolean("Options.gamemode", true)) { - config.set("gamemode", player.getGameMode().toString()); - } - - // Save flying state - if (plugin.getConfig().getBoolean("Options.flying", true)) { - config.set("flying", player.isFlying()); - } - - // Save health and hunger - config.set("health", plugin.getConfig().getBoolean("Options.health-options.health-default-save", true) - ? player.getHealth() - : 20.0D); - config.set("hunger", player.getFoodLevel()); - - // Save experience - config.set("exp", player.getExp()); - config.set("exp-level", player.getLevel()); - } - - /** - * Saves the player's active potion effects to the configuration file. - * - * @param player The player whose potion effects are being saved. - * @param config The configuration file where the data is saved. - */ - @SuppressWarnings("deprecation") - private void savePotionEffects(Player player, FileConfiguration config) { - int index = 0; - for (PotionEffect effect : player.getActivePotionEffects()) { - config.set("potion_effect." + index + ".type", effect.getType().getName()); - config.set("potion_effect." + index + ".level", effect.getAmplifier()); - config.set("potion_effect." + index + ".duration", effect.getDuration()); - index++; - } - } - - /** - * Saves the player's off-hand item to the configuration file. - * - * @param player The player whose off-hand item is being saved. - * @param config The configuration file where the data is saved. - */ - private void saveOffHandItem(Player player, FileConfiguration config) { - config.set("off_hand_item", player.getInventory().getItemInOffHand()); - } - - /** - * Loads the player's off-hand item from the configuration file. - * - * @param player The player whose off-hand item is being loaded. - * @param config The configuration file where the data is loaded from. - */ - private void loadOffHandItem(Player player, FileConfiguration config) { - if (config.contains("off_hand_item")) { - player.getInventory().setItemInOffHand(config.getItemStack("off_hand_item")); - } - } - - /** - * Clears the player's state by resetting their inventory, ender chest, flying - * state, gamemode, health, hunger, experience, and level. - * - * @param player The player whose state is being cleared. - */ - private void clearPlayerState(Player player) { - player.getInventory().clear(); - player.getEnderChest().clear(); - player.setFlying(false); - player.setGameMode(GameMode.SURVIVAL); - player.setHealth(20.0D); - player.setFoodLevel(20); - player.setExp(0.0F); - player.setLevel(0); - } - - /** - * Loads the player's inventory, ender chest, and armor contents from the - * configuration file. - * - * @param player The player whose inventory is being loaded. - * @param config The configuration file where the data is loaded from. - */ - private void loadInventory(Player player, FileConfiguration config) { - if (config.contains("inventory")) { - for (String key : config.getConfigurationSection("inventory").getKeys(false)) { - player.getInventory().setItem(Integer.parseInt(key), config.getItemStack("inventory." + key)); - } - } - - if (plugin.getConfig().getBoolean("Options.ender-chest", true) && config.contains("ender_chest")) { - for (String key : config.getConfigurationSection("ender_chest").getKeys(false)) { - player.getEnderChest().setItem(Integer.parseInt(key), config.getItemStack("ender_chest." + key)); - } - } - } - - /** - * Loads the player's attributes such as gamemode, flying state, health, hunger, - * and experience from the configuration file. - * - * @param player The player whose attributes are being loaded. - * @param config The configuration file where the data is loaded from. - */ - private void loadPlayerAttributes(Player player, FileConfiguration config) { - player.setGameMode(GameMode.valueOf(config.getString("gamemode", "SURVIVAL"))); - player.setFlying(config.getBoolean("flying", false)); - player.setHealth(config.getDouble("health", 20.0D)); - player.setFoodLevel(config.getInt("hunger", 20)); - player.setExp((float) config.getDouble("exp", 0.0F)); - player.setLevel(config.getInt("exp-level", 0)); - } - - /** - * Loads the player's active potion effects from the configuration file. - * - * @param player The player whose potion effects are being loaded. - * @param config The configuration file where the data is loaded from. - */ - private void loadPotionEffects(Player player, FileConfiguration config) { - if (config.contains("potion_effect")) { - for (String key : config.getConfigurationSection("potion_effect").getKeys(false)) { - @SuppressWarnings("deprecation") - PotionEffect effect = new PotionEffect( - PotionEffectType.getByName(config.getString("potion_effect." + key + ".type")), - config.getInt("potion_effect." + key + ".duration"), - config.getInt("potion_effect." + key + ".level")); - player.addPotionEffect(effect); - } - } - } -} diff --git a/Code/src/main/java/com/github/happyuky7/separeworlditems/listeners/base/WorldChangeEvent.java b/Code/src/main/java/com/github/happyuky7/separeworlditems/listeners/base/WorldChangeEvent.java new file mode 100644 index 0000000..3a65278 --- /dev/null +++ b/Code/src/main/java/com/github/happyuky7/separeworlditems/listeners/base/WorldChangeEvent.java @@ -0,0 +1,192 @@ +package com.github.happyuky7.separeworlditems.listeners.base; + +import com.github.happyuky7.separeworlditems.SepareWorldItems; +import com.github.happyuky7.separeworlditems.data.loaders.InventoryLoader; +import com.github.happyuky7.separeworlditems.data.loaders.PlayerDataLoader; +import com.github.happyuky7.separeworlditems.data.savers.InventorySaver; +import com.github.happyuky7.separeworlditems.data.savers.PlayerDataSaver; +import com.github.happyuky7.separeworlditems.filemanagers.FileManager2; +import com.github.happyuky7.separeworlditems.utils.TeleportationManager; + +import org.bukkit.GameMode; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerChangedWorldEvent; + +import java.io.File; + +/** + * Event listener for handling player world change events. + * Manages player data and inventory based on the world the player changes to. + * Supports saving and loading data between different world groups. + */ +public class WorldChangeEvent implements Listener { + + private final SepareWorldItems plugin; + + /** + * Constructs a new WorldChangeEvent listener. + * + * @param plugin The main plugin instance. + */ + public WorldChangeEvent(SepareWorldItems plugin) { + this.plugin = plugin; + } + + /** + * Handles the PlayerChangedWorldEvent. This method is triggered whenever a + * player + * changes worlds. It manages the player's data, such as inventory, attributes, + * and other world-specific states. + * + * @param event The PlayerChangedWorldEvent that triggered this handler. + */ + @EventHandler + public void onWorldChange(PlayerChangedWorldEvent event) { + Player player = event.getPlayer(); + + // Prevent further handling if the player is in the teleportation process + // trigger by EssentialsX. + if (TeleportationManager.isTeleporting(player.getUniqueId())) { + TeleportationManager.setTeleporting(player.getUniqueId(), false); // Reset the teleportation flag + return; // Skip further processing + } + + // Retrieve the world names the player is transitioning between + String fromWorld = event.getFrom().getName(); // Previous world + String toWorld = player.getWorld().getName(); // Target world + + FileConfiguration config = plugin.getConfig(); + + // Check if the worlds are configured in the plugin's settings + if (config.contains("worlds." + fromWorld) && config.contains("worlds." + toWorld)) { + // Retrieve the world groups the player is switching between + String fromGroup = config.getString("worlds." + fromWorld); + String toGroup = config.getString("worlds." + toWorld); + + // Save the player's data from the previous world before switching + savePlayerData(player, fromGroup); + + // If worlds belong to different groups, load data for the new world + if (!fromGroup.equals(toGroup)) { + loadPlayerData(player, toGroup); + } else { + // If worlds belong to the same group, reload the player's data without changes + reloadAllPlayerData(player, fromGroup); + } + } + } + + /** + * Saves the player's data (inventory, attributes, potion effects, etc.) to a + * group-specific configuration file based on the player's current world group. + * + * @param player The player whose data is being saved. + * @param groupName The group name associated with the player's current world. + */ + private void savePlayerData(Player player, String groupName) { + File file = new File(plugin.getDataFolder() + File.separator + "groups" + + File.separator + groupName + File.separator + player.getName() + "-" + player.getUniqueId() + ".yml"); + FileConfiguration config = FileManager2.getYaml(file); + + // Save various player data (inventory, attributes, potion effects, etc.) + InventorySaver.save(player, config); + PlayerDataSaver.saveAttributes(player, config); + PlayerDataSaver.savePotionEffects(player, config); + PlayerDataSaver.saveOffHandItem(player, config); + + // Persist the changes in the configuration file + FileManager2.saveConfiguration(file, config); + + // Clear the player's state to prevent unwanted data persistence (e.g., + // inventory) + clearPlayerState(player); + } + + /** + * Loads the player's data (inventory, attributes, potion effects, etc.) from a + * group-specific configuration file based on the player's target world group. + * + * @param player The player whose data is being loaded. + * @param groupName The group name associated with the player's target world. + */ + private void loadPlayerData(Player player, String groupName) { + File file = new File(plugin.getDataFolder() + File.separator + "groups" + + File.separator + groupName + File.separator + player.getName() + "-" + player.getUniqueId() + ".yml"); + FileConfiguration config = FileManager2.getYaml(file); + + // Load player data (inventory, attributes, potion effects, etc.) + InventoryLoader.load(player, config); + PlayerDataLoader.loadAttributes(player, config); + PlayerDataLoader.loadPotionEffects(player, config); + PlayerDataLoader.loadOffHandItem(player, config); + } + + /** + * Clears the player's state by resetting inventory, ender chest, game mode, + * health, + * food level, experience, and other attributes to ensure a clean state when the + * player transitions between worlds. + * + * @param player The player whose state is being cleared. + */ + private void clearPlayerState(Player player) { + // Clear the player's inventory and ender chest + player.getInventory().clear(); + player.getEnderChest().clear(); + + // Reset the player's state to a default, clean state + player.setFlying(false); + player.setGameMode(GameMode.SURVIVAL); + player.setHealth(20.0D); // Full health + player.setFoodLevel(20); // Full hunger + player.setExp(0.0F); // No experience + player.setLevel(0); // No levels + } + + /** + * Reloads all of the player's data (inventory, attributes, potion effects, + * etc.) + * from a group-specific configuration file. + * + * @param player The player whose data is being reloaded. + * @param groupName The group name associated with the player's world. + */ + private void reloadAllPlayerData(Player player, String groupName) { + File file = new File(plugin.getDataFolder() + File.separator + "groups" + + File.separator + groupName + File.separator + player.getName() + "-" + player.getUniqueId() + ".yml"); + FileConfiguration config = FileManager2.getYaml(file); + + // Reload the player's data (inventory, attributes, potion effects, off-hand, + // etc.) + InventoryLoader.load(player, config); + PlayerDataLoader.loadAttributes(player, config); + PlayerDataLoader.loadPotionEffects(player, config); + PlayerDataLoader.loadOffHandItem(player, config); + + // Reload the player's experience and level if necessary + reloadExperienceAndLevel(player, config); + } + + /** + * Reloads the player's experience and level from a group-specific configuration + * file to ensure they match the state from the previous world or group. + * + * @param player The player whose experience and level are being reloaded. + * @param config The configuration file containing the player's data. + */ + private void reloadExperienceAndLevel(Player player, FileConfiguration config) { + // Check if experience and level data are present in the configuration + if (config.contains("exp") && config.contains("exp-level")) { + // Retrieve and set the player's experience and level + float experience = (float) config.getDouble("exp", 0.0F); + int level = config.getInt("exp-level", 0); + + // Set the player's experience and level from the loaded data + player.setExp(experience); + player.setLevel(level); + } + } +} diff --git a/Code/src/main/java/com/github/happyuky7/separeworlditems/utils/TeleportationManager.java b/Code/src/main/java/com/github/happyuky7/separeworlditems/utils/TeleportationManager.java new file mode 100644 index 0000000..69f0241 --- /dev/null +++ b/Code/src/main/java/com/github/happyuky7/separeworlditems/utils/TeleportationManager.java @@ -0,0 +1,74 @@ +package com.github.happyuky7.separeworlditems.utils; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * Utility class that manages the state of players during teleportation events. + * It keeps track of players who are in the process of teleporting, preventing + * conflicts and ensuring smooth teleportation operations, such as saving and loading + * player data across different worlds. + *

+ * This class is crucial in scenarios where teleportation events are triggered, + * and the system must ensure that a player does not get caught in multiple teleportation + * events simultaneously. For example, when a player teleports between worlds, their data + * must be saved before teleporting and reloaded afterward. The {@code TeleportationManager} + * prevents the data from being overwritten or handled multiple times while the player is already teleporting. + *

+ */ +public class TeleportationManager { + // Set to store UUIDs of players who are currently teleporting + private static final Set teleportingPlayers = new HashSet<>(); + + /** + * Marks a player as being in the process of teleporting or not. + *

+ * This method is called before teleportation begins to mark the player as + * being in a teleporting state. It ensures that any actions that require + * the player to be out of teleportation (e.g., saving/loading player data) + * will not interfere with the teleportation process. + *

+ *

+ * Example usage: + *

+     * {@code TeleportationManager.setTeleporting(playerUUID, true); // Mark as teleporting}
+     * {@code TeleportationManager.setTeleporting(playerUUID, false); // Mark as not teleporting}
+     * 
+ *

+ * + * @param playerUUID The unique identifier of the player. + * @param isTeleporting A boolean indicating whether the player is teleporting. + */ + public static void setTeleporting(UUID playerUUID, boolean isTeleporting) { + if (isTeleporting) { + teleportingPlayers.add(playerUUID); // Add the player to the teleporting set + } else { + teleportingPlayers.remove(playerUUID); // Remove the player from the teleporting set + } + } + + /** + * Checks whether a player is currently in the process of teleporting. + *

+ * This method is used to verify if a player is already teleporting, preventing + * multiple teleportation processes from being triggered at once. This is particularly + * important when there are multiple triggers for teleportation, such as home teleport + * or world change. + *

+ *

+ * Example usage: + *

+     * {@code if (TeleportationManager.isTeleporting(playerUUID)) { }
+     *     // Skip processing since the player is already teleporting
+     * }
+     * 
+ *

+ * + * @param playerUUID The unique identifier of the player. + * @return {@code true} if the player is currently teleporting, {@code false} otherwise. + */ + public static boolean isTeleporting(UUID playerUUID) { + return teleportingPlayers.contains(playerUUID); // Return true if player is in teleporting set + } +} diff --git a/Code/src/main/resources/config.yml b/Code/src/main/resources/config.yml index 27aad49..7929846 100644 --- a/Code/src/main/resources/config.yml +++ b/Code/src/main/resources/config.yml @@ -1,12 +1,12 @@ # # SepareWorldItems -# Version: 1.2.22 +# Version: 1.2.23-DEV # Tutorial: https://github.com/Happyuky7/SEPARE-WORLD-ITEMS/wiki # Discord OR Support: https://discord.gg/3EebYUyeUX # -# Config Version: 1.2.22 -config-version: 1.2.22 +# Config Version: 1.2.23-DEV +config-version: 1.2.23-DEV experimental: lang: en_US diff --git a/Code/src/main/resources/langs.yml b/Code/src/main/resources/langs.yml index 83038c9..0f3629d 100644 --- a/Code/src/main/resources/langs.yml +++ b/Code/src/main/resources/langs.yml @@ -1,5 +1,5 @@ # Config Langs -# Config = 1.2.22 +# Config = 1.2.23-DEV # Prefix Value: %prefix% # Lang: EN # Here you can find the list of languages to which the plugin is already translated: diff --git a/Code/src/main/resources/langs/en_US.yml b/Code/src/main/resources/langs/en_US.yml index 564ccac..5412ac2 100644 --- a/Code/src/main/resources/langs/en_US.yml +++ b/Code/src/main/resources/langs/en_US.yml @@ -1,5 +1,5 @@ # Config Langs -# Config = 1.2.22 +# Config = 1.2.23-DEV # Prefix Value: %prefix% # Lang: EN # Here you can find the list of languages to which the plugin is already translated: