From d0db273bf21013014e6d606530d37b8d6de35120 Mon Sep 17 00:00:00 2001 From: evnrca Date: Wed, 26 Aug 2026 22:09:25 +0800 Subject: [PATCH 1/3] Initial commit of local changes --- .gitignore | 4 + .vscode/settings.json | 4 + PlayerProfiles.iml | 2 + README.md | 5 + api.iml | 12 + api/api.iml | 13 + api/pom.xml | 38 + .../com/muhammaddaffa/api/IRegionFinder.java | 11 + core.iml | 12 + core/core.iml | 13 + core/pom.xml | 103 +++ .../playerprofiles/ConfigValue.java | 215 +++++ .../playerprofiles/PlayerProfiles.java | 124 +++ .../playerprofiles/commands/LockCommand.java | 113 +++ .../playerprofiles/commands/MainCommand.java | 128 +++ .../commands/ProfileCommand.java | 109 +++ .../commands/ToggleCommand.java | 86 ++ .../commands/UnlockCommand.java | 113 +++ .../commands/abstraction/SubCommand.java | 19 + .../commands/subcommands/ListGUICommand.java | 36 + .../commands/subcommands/OpenGUICommand.java | 91 ++ .../subcommands/OpenProfileCommand.java | 67 ++ .../commands/subcommands/ReloadCommand.java | 35 + .../hooks/combatlogx/HCombatLogX.java | 53 ++ .../hooks/deluxecombat/HDeluxeCombat.java | 12 + .../inventory/InventoryManager.java | 55 ++ .../inventory/ProfileInventory.java | 118 +++ .../inventory/items/GUIItem.java | 65 ++ .../inventory/items/ItemsLoader.java | 69 ++ .../listeners/PlayerInteract.java | 151 ++++ .../manager/DependencyManager.java | 50 ++ .../manager/customgui/CustomGUI.java | 17 + .../manager/customgui/CustomGUIManager.java | 113 +++ .../manager/customgui/CustomGuiCreator.java | 65 ++ .../manager/profile/Profile.java | 29 + .../manager/profile/ProfileManager.java | 80 ++ .../playerprofiles/metrics/Metrics.java | 848 ++++++++++++++++++ .../playerprofiles/utils/ClickManager.java | 114 +++ .../playerprofiles/utils/ItemManager.java | 240 +++++ .../playerprofiles/utils/Utils.java | 91 ++ core/src/main/resources/config.yml | 115 +++ .../main/resources/custom-gui/punish-gui.yml | 156 ++++ core/src/main/resources/data.yml | 1 + core/src/main/resources/gui-creator.yml | 5 + core/src/main/resources/gui.yml | 183 ++++ core/src/main/resources/plugin.yml | 19 + dist/pom.xml | 75 ++ pom.xml | 26 + worldguard-wrapper.iml | 12 + worldguard-wrapper/pom.xml | 53 ++ .../worldguardwrapper/WorldGuardWrapper.java | 32 + worldguard-wrapper/worldguard-wrapper.iml | 13 + worldguard6.iml | 13 + worldguard6/pom.xml | 59 ++ .../worldguardwrapper/wg6/RegionFinder6.java | 28 + worldguard6/worldguard6.iml | 13 + worldguard7.iml | 12 + worldguard7/pom.xml | 59 ++ .../worldguardwrapper/wg7/RegionFinder7.java | 35 + worldguard7/worldguard7.iml | 13 + 60 files changed, 4445 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 PlayerProfiles.iml create mode 100644 README.md create mode 100644 api.iml create mode 100644 api/api.iml create mode 100644 api/pom.xml create mode 100644 api/src/main/java/com/muhammaddaffa/api/IRegionFinder.java create mode 100644 core.iml create mode 100644 core/core.iml create mode 100644 core/pom.xml create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/ConfigValue.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/PlayerProfiles.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/commands/LockCommand.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/commands/MainCommand.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/commands/ProfileCommand.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/commands/ToggleCommand.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/commands/UnlockCommand.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/commands/abstraction/SubCommand.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/ListGUICommand.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/OpenGUICommand.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/OpenProfileCommand.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/ReloadCommand.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/hooks/combatlogx/HCombatLogX.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/hooks/deluxecombat/HDeluxeCombat.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/InventoryManager.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/ProfileInventory.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/items/GUIItem.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/items/ItemsLoader.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/listeners/PlayerInteract.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/manager/DependencyManager.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/manager/customgui/CustomGUI.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/manager/customgui/CustomGUIManager.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/manager/customgui/CustomGuiCreator.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/manager/profile/Profile.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/manager/profile/ProfileManager.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/metrics/Metrics.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/utils/ClickManager.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/utils/ItemManager.java create mode 100644 core/src/main/java/com/muhammaddaffa/playerprofiles/utils/Utils.java create mode 100644 core/src/main/resources/config.yml create mode 100644 core/src/main/resources/custom-gui/punish-gui.yml create mode 100644 core/src/main/resources/data.yml create mode 100644 core/src/main/resources/gui-creator.yml create mode 100644 core/src/main/resources/gui.yml create mode 100644 core/src/main/resources/plugin.yml create mode 100644 dist/pom.xml create mode 100644 pom.xml create mode 100644 worldguard-wrapper.iml create mode 100644 worldguard-wrapper/pom.xml create mode 100644 worldguard-wrapper/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/WorldGuardWrapper.java create mode 100644 worldguard-wrapper/worldguard-wrapper.iml create mode 100644 worldguard6.iml create mode 100644 worldguard6/pom.xml create mode 100644 worldguard6/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg6/RegionFinder6.java create mode 100644 worldguard6/worldguard6.iml create mode 100644 worldguard7.iml create mode 100644 worldguard7/pom.xml create mode 100644 worldguard7/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg7/RegionFinder7.java create mode 100644 worldguard7/worldguard7.iml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40fba35 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +target +.idea +dependency-reduced-pom.xml +core/deps \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d53ecaf --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "java.compile.nullAnalysis.mode": "automatic", + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/PlayerProfiles.iml b/PlayerProfiles.iml new file mode 100644 index 0000000..78b2cc5 --- /dev/null +++ b/PlayerProfiles.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..860439a --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# PlayerProfiles +If you're looking to get a support for this plugin, please add **mdaffa** on discord! + +# License +You can do whatever you want with the source code, just don't redistribute it. diff --git a/api.iml b/api.iml new file mode 100644 index 0000000..fa63d4b --- /dev/null +++ b/api.iml @@ -0,0 +1,12 @@ + + + + + + + SPIGOT + + + + + \ No newline at end of file diff --git a/api/api.iml b/api/api.iml new file mode 100644 index 0000000..a589521 --- /dev/null +++ b/api/api.iml @@ -0,0 +1,13 @@ + + + + + + + SPIGOT + + 1 + + + + \ No newline at end of file diff --git a/api/pom.xml b/api/pom.xml new file mode 100644 index 0000000..83b5fea --- /dev/null +++ b/api/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + com.muhammaddaffa + PlayerProfiles + 8.0.4 + + + api + + + 17 + 17 + UTF-8 + + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + + + org.spigotmc + spigot-api + 1.20.1-R0.1-SNAPSHOT + provided + + + + \ No newline at end of file diff --git a/api/src/main/java/com/muhammaddaffa/api/IRegionFinder.java b/api/src/main/java/com/muhammaddaffa/api/IRegionFinder.java new file mode 100644 index 0000000..72c0777 --- /dev/null +++ b/api/src/main/java/com/muhammaddaffa/api/IRegionFinder.java @@ -0,0 +1,11 @@ +package com.muhammaddaffa.api; + +import org.bukkit.Location; + +import java.util.List; + +public interface IRegionFinder { + + List getRegions(Location location); + +} \ No newline at end of file diff --git a/core.iml b/core.iml new file mode 100644 index 0000000..fa63d4b --- /dev/null +++ b/core.iml @@ -0,0 +1,12 @@ + + + + + + + SPIGOT + + + + + \ No newline at end of file diff --git a/core/core.iml b/core/core.iml new file mode 100644 index 0000000..a589521 --- /dev/null +++ b/core/core.iml @@ -0,0 +1,13 @@ + + + + + + + SPIGOT + + 1 + + + + \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml new file mode 100644 index 0000000..e10343c --- /dev/null +++ b/core/pom.xml @@ -0,0 +1,103 @@ + + + 4.0.0 + + com.muhammaddaffa + PlayerProfiles + 8.0.4 + + + core + + + 21 + 21 + UTF-8 + + + + clean package + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.0 + + + + me.aglerr.mclibs + com.muhammaddaffa.playerprofiles.mclibs + + + + + + package + + shade + + + + + + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + placeholderapi + https://repo.extendedclip.com/content/repositories/placeholderapi/ + + + sk89q-repo + https://maven.enginehub.org/repo/ + + + sirblobman-public + https://nexus.sirblobman.xyz/public/ + + + jitpack.io + https://jitpack.io + + + + + + org.spigotmc + spigot-api + 1.21.5-R0.1-SNAPSHOT + provided + + + me.clip + placeholderapi + 2.11.6 + provided + + + + com.github.mdaffa48 + MDLib + 2.3.8 + + + + com.github.timderspieler + DeluxeCombat-API + 1.5.1 + provided + + + ${project.groupId} + worldguard-wrapper + ${project.version} + + + + \ No newline at end of file diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/ConfigValue.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/ConfigValue.java new file mode 100644 index 0000000..960b4a5 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/ConfigValue.java @@ -0,0 +1,215 @@ +package com.muhammaddaffa.playerprofiles; + +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.List; + +public class ConfigValue { + + public static String PREFIX; + + // Auto Refresh + public static boolean AUTO_REFRESH_ENABLED; + public static int AUTO_REFRESH_TICK; + + // Distance Check + public static boolean DISTANCE_CHECK_ENABLED; + public static double MAXIMUM_DISTANCE; + public static String DISTANCE_TOO_FAR; + + // Options + public static boolean DISABLE_NPC_PROFILE; + public static boolean MUST_SHIFT_CLICK; + public static boolean DISABLE_IN_COMBAT_ENABLED; + public static String DISABLE_IN_COMBAT_MESSAGE; + + // Disabled worlds + public static List DISABLED_WORLDS; + public static String DISABLED_WORLD_MESSAGE; + + // Disabled regions + public static List DISABLED_REGIONS; + public static String PLAYER_DISABLED_REGIONS; + public static String TARGET_DISABLED_REGIONS; + + // Interact Cooldown + public static boolean COOLDOWN_ENABLED; + public static int COOLDOWN_TIME; + public static String COOLDOWN_MESSAGE; + + // Messages + public static String NO_PERMISSION; + public static String RELOAD; + public static String INVALID_PLAYER; + public static String LOCKED_PROFILE; + public static String LOCK_PROFILE; + public static String LOCK_PROFILE_OTHERS; + public static String UNLOCK_PROFILE; + public static String UNLOCK_PROFILE_OTHERS; + public static String INVALID_GUI_NAME; + public static String LIST_GUI; + + // Messages List + public static List HELP_MESSAGES; + + public static void initialize(){ + FileConfiguration config = PlayerProfiles.CONFIG_DEFAULT.getConfig(); + PREFIX = config.getString("messages.prefix"); + + AUTO_REFRESH_ENABLED = config.getBoolean("autoRefresh.enabled"); + AUTO_REFRESH_TICK = config.getInt("autoRefresh.refreshEvery"); + + DISTANCE_CHECK_ENABLED = config.getBoolean("distanceCheck.enabled"); + MAXIMUM_DISTANCE = config.getDouble("distanceCheck.distance"); + DISTANCE_TOO_FAR = config.getString("distanceCheck.tooFarMessage"); + + DISABLE_NPC_PROFILE = config.getBoolean("options.disableNPC"); + MUST_SHIFT_CLICK = config.getBoolean("options.shiftClick"); + DISABLE_IN_COMBAT_ENABLED = config.getBoolean("options.disableInCombat.enabled"); + DISABLE_IN_COMBAT_MESSAGE = config.getString("options.disableInCombat.message"); + + DISABLED_WORLDS = config.getStringList("disabledWorlds.worlds"); + DISABLED_WORLD_MESSAGE = config.getString("disabledWorlds.message"); + + DISABLED_REGIONS = config.getStringList("disabledRegions.regions"); + PLAYER_DISABLED_REGIONS = config.getString("disabledRegions.playerInDisabledRegionMessage"); + TARGET_DISABLED_REGIONS = config.getString("disabledRegions.targetInDisabledRegionMessage"); + + COOLDOWN_ENABLED = config.getBoolean("cooldown.enabled"); + COOLDOWN_TIME = config.getInt("cooldown.duration"); + COOLDOWN_MESSAGE = config.getString("cooldown.message"); + + NO_PERMISSION = config.getString("messages.noPermission"); + RELOAD = config.getString("messages.reload"); + INVALID_PLAYER = config.getString("messages.invalidPlayer"); + LOCKED_PROFILE = config.getString("messages.targetProfileLocked"); + LOCK_PROFILE = config.getString("messages.lockProfile"); + LOCK_PROFILE_OTHERS = config.getString("messages.lockProfileOthers"); + UNLOCK_PROFILE = config.getString("messages.unlockProfile"); + UNLOCK_PROFILE_OTHERS = config.getString("messages.unlockProfileOthers"); + INVALID_GUI_NAME = config.getString("messages.invalidGUIName"); + LIST_GUI = config.getString("messages.listGUI"); + + HELP_MESSAGES = config.getStringList("messages.help"); + } + + public static String template() { + return """ + ############################################################# + # # + # Player Profiles - Template GUI + # DO NOT DELETE THIS GUI! + ############################################################# + + # List of placeholder builtin you can use! + # And it supports PlaceholderAPI + # {target} = The target player + # {player} = The player who opened the inventory + # {target_status} = The target profile status (locked or unlocked) + # {player_status} = The player profile status (locked or unlocked) + # {target_health} = The target health + # {player_health} = The player health + # {target_exp} = The target experience + # {player_exp} = The player experience + # {target_level} = The target level + # {player_level} = The player level + # {target_uuid} = The target UUID + # {player_uuid} = The player UUID + # {target_world} = The target world + # {player_world} = The player world + + title: "Default Menu" # Inventory title + size: 27 # Inventory size + + # We do support fill item automatically but if you want to be more customizeable, + # you can disable it and create your own fill item on the items section + fillItems: + enabled: true + material: BLACK_STAINED_GLASS_PANE + name: "&f" + lore: [] + + items: + # Create your own item by following the example below + 1: + # The material can be anything but if you want to get the player head + # You can do by doing it like below + material: head;{target} + # You can also get the player head by doing it like below + name: "&6{target}'s Information" + # Slots can be anything but keep it surrounded by [] and separated by comma if you want + # to put the item on multiple slots + slots: [ 12 ] + # You can also get the player head by doing it like below + glowing: false + # You can also get the player head by doing it like below + hideAttributes: false + # You can set the item custom model data + customModelData: 0 + # You can set the item model data + # itemModel: minecraft:dirt + # You can set the item to only be seen by the visitor + # Or only be seen by the owner + # Or both + onlyVisitor: false + onlyOwner: false + # You can set the item to only be seen by the player who has the permission + # If you don't want to use it just skip it or remove it. + usePermission: false + permission: "custom.permission" + # You can set the item priority + # The higher the priority, the more it will be prioritized + # If the priority is the same, it will be based on the order of the item + priority: 0 + # You can set the item lore + lore: + - "" + - " * &7Profile Status: {target_status}" + - " * &7Health: &c{target_health}" + - " * &7Level: &6{target_level}" + - " * &7Experience: &6{target_exp}" + - " * &7World: &6{target_world}" + - "" + - "&bMore with PlaceholderAPI" + - " * &7Is Flying: &6%player_is_flying%" + - " * &7Is Sneaking: &6%player_is_sneaking%" + - " * &7Is Sprinting: &6%player_is_sprinting%" + - " * &7Is OP: &6%player_is_op%" + - " * &7Ping: &a%player_ping%ms" + # You can set the item commands + # The command can be anything but keep it surrounded by [] + # You can use the commands either by sending via console or player just by doing this + # [CONSOLE] /command + # [PLAYER] /command + # [MESSAGEPLAYER] &aMessage + # [MESSAGETARGET] &aMessage + # [OPENGUIMENU] gui-name.yml + # [CLOSE] + leftClickCommands: [] + rightClickCommands: [] + 2: + material: DIRT + name: "&6Complement {target}!" + slots: [ 14 ] + glowing: false + hideAttributes: true + customModelData: 0 + # You can set the item model data + # itemModel: minecraft:dirt + onlyVisitor: false + onlyOwner: false + lore: + - "&7Complement {target}!" + leftClickCommands: + - "[PLAYER] msg {target} i like your hair :)" + - "[MESSAGE] &8[&6Profile&8] &aYou have been complemented by &e{player}" + - "[CLOSE]" + rightClickCommands: + - "[PLAYER] msg {target} i like your hair :)" + - "[MESSAGE] &8[&6Profile&8] &aYou have been complemented by &e{player}" + - "[CLOSE]" + """; + } + + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/PlayerProfiles.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/PlayerProfiles.java new file mode 100644 index 0000000..35b6f21 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/PlayerProfiles.java @@ -0,0 +1,124 @@ +package com.muhammaddaffa.playerprofiles; + +import com.muhammaddaffa.mdlib.MDLib; +import com.muhammaddaffa.mdlib.utils.Common; +import com.muhammaddaffa.mdlib.utils.Config; +import com.muhammaddaffa.playerprofiles.commands.*; + +import com.muhammaddaffa.playerprofiles.inventory.InventoryManager; +import com.muhammaddaffa.playerprofiles.listeners.PlayerInteract; +import com.muhammaddaffa.playerprofiles.manager.DependencyManager; +import com.muhammaddaffa.playerprofiles.manager.customgui.CustomGUIManager; +import com.muhammaddaffa.playerprofiles.manager.customgui.CustomGuiCreator; +import com.muhammaddaffa.playerprofiles.manager.profile.ProfileManager; +import com.muhammaddaffa.playerprofiles.metrics.Metrics; +import org.bukkit.Bukkit; +import org.bukkit.plugin.java.JavaPlugin; + +public class PlayerProfiles extends JavaPlugin { + + private static final int BSTATS_ID = 7049; + + private static PlayerProfiles instance; + + public static Config DATA_DEFAULT, CONFIG_DEFAULT, GUI_DEFAULT, GUI_CREATOR; + + private final InventoryManager inventoryManager = new InventoryManager(this); + private final CustomGUIManager customGUIManager = new CustomGUIManager(this); + private final ProfileManager profileManager = new ProfileManager(); + private final CustomGuiCreator customGuiCreator = new CustomGuiCreator(this); + + @Override + public void onLoad() { + MDLib.inject(this); + MDLib.registerWorldGuard(); + } + + @Override + public void onEnable(){ + instance = this; + // Injecting the libs + MDLib.onEnable(this); + // Check the dependency + DependencyManager.checkDependency(); + // Initialize all config + initializeConfig(); + // Initialize all custom gui creator + customGuiCreator.createCustomGui(); + // Initialize all config values + ConfigValue.initialize(); + // Load all items for the inventory + inventoryManager.initialize(); + // Load all custom guis + customGUIManager.loadCustomGUI(); + // Load all profile data + profileManager.loadProfileData(); + // Register the player interact event + Bukkit.getPluginManager().registerEvents(new PlayerInteract(this), this); + // Register all commands + registerCommands(); + // Add bstats metrics + new Metrics(this, BSTATS_ID); + } + + @Override + public void onDisable(){ + // Plugin shutdown logic + MDLib.shutdown(); + // Save all profile data + profileManager.saveProfileData(); + } + + private void initializeConfig() { + CONFIG_DEFAULT = new Config("config.yml", null, true); + GUI_DEFAULT = new Config("gui.yml", null, true); + DATA_DEFAULT = new Config("data.yml", null, false); + GUI_CREATOR = new Config("gui-creator.yml", null, true); + + CONFIG_DEFAULT.setShouldUpdate(true); + Config.updateConfigs(); + Config.reload(); + } + + public void reloadAllThing(){ + // Reload all configuration + Config.reload(); + // Re-initialize the config value + ConfigValue.initialize(); + // Reload all items for the profile inventory + inventoryManager.reInitialize(); + // Reload all custom gui creator + customGuiCreator.createCustomGui(); + // Reload all custom guis + customGUIManager.reloadCustomGUI(); + } + + private void registerCommands(){ + // Register /playerprofiles command + new MainCommand(this).registerThisCommand(); + // Register /profile command + new ProfileCommand(this).registerThisCommand(); + // Register /lockprofile command + new LockCommand(this).registerThisCommand(); + // Register /unlockprofile command + new UnlockCommand(this).registerThisCommand(); + // Register /toggleprofile command + new ToggleCommand(this).registerThisCommand(); + } + + public static PlayerProfiles getInstance() { + return instance; + } + + public InventoryManager getInventoryManager(){ + return inventoryManager; + } + + public CustomGUIManager getCustomGUIManager() { + return customGUIManager; + } + + public ProfileManager getProfileManager() { + return profileManager; + } +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/LockCommand.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/LockCommand.java new file mode 100644 index 0000000..824b9ca --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/LockCommand.java @@ -0,0 +1,113 @@ +package com.muhammaddaffa.playerprofiles.commands; + +import com.muhammaddaffa.mdlib.utils.Common; +import com.muhammaddaffa.mdlib.utils.Logger; +import com.muhammaddaffa.playerprofiles.ConfigValue; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +public class LockCommand implements CommandExecutor, TabCompleter { + + private static final String COMMAND_NAME = "lockprofile"; + + private final PlayerProfiles plugin; + public LockCommand(PlayerProfiles plugin){ + this.plugin = plugin; + } + + public void registerThisCommand(){ + plugin.getCommand(COMMAND_NAME).setExecutor(this); + plugin.getCommand(COMMAND_NAME).setTabCompleter(this); + // Get the file configuration of config.yml + FileConfiguration config = PlayerProfiles.CONFIG_DEFAULT.getConfig(); + // Get the aliases from the config + List aliases = config.getStringList("commandAliases.lockProfile"); + // Add all aliases to the command + plugin.getCommand(COMMAND_NAME).getAliases().addAll(aliases); + // Trying to register the aliases from the config + try{ + final Field bukkitCommandMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); + + bukkitCommandMap.setAccessible(true); + ((CommandMap) bukkitCommandMap.get(Bukkit.getServer())).register(COMMAND_NAME, plugin.getCommand(COMMAND_NAME)); + + bukkitCommandMap.setAccessible(false); + } catch (Exception ex){ + Logger.info("&cFailed to register /lockprofile command"); + ex.printStackTrace(); + } + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, @NotNull String[] args) { + // Check if the args length is 0 (/lockprofile) + if(args.length == 0){ + // Permission: playerprofiles.lockprofile + if(!(sender.hasPermission("playerprofiles.lockprofile"))){ + sender.sendMessage(Common.color(ConfigValue.NO_PERMISSION + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{permission}", "playerprofiles.lockprofile"))); + return true; + } + // If the sender is not a player (the sender is console) + if(!(sender instanceof Player)){ + // Send console a usage message and return the code + sender.sendMessage(Common.color("&cUsage: /lockprofile (player)")); + return true; + } + // After the check above, we can get the Player object from the sender + Player player = (Player) sender; + // Send a lock profile message to player + player.sendMessage(Common.color(ConfigValue.LOCK_PROFILE + .replace("{prefix}", ConfigValue.PREFIX))); + // Actually lock the player's profile + plugin.getProfileManager().lockProfile(player); + } + // Check if args length is 1 (/lockprofile (player)) + if(args.length == 1){ + // Permission: playerprofiles.lockprofile.others + if(!(sender.hasPermission("playerprofiles.lockprofile.others"))){ + sender.sendMessage(Common.color(ConfigValue.NO_PERMISSION + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{permission}", "playerprofiles.lockprofile.others"))); + return true; + } + // Get the player object from args[0] + Player player = Bukkit.getPlayer(args[0]); + // Check if the player is not valid or online + if(player == null){ + // Send an invalid player message + sender.sendMessage(Common.color(ConfigValue.INVALID_PLAYER + .replace("{prefix}", ConfigValue.PREFIX))); + // Stop the code + return true; + } + // The player is valid and everything's going well + // Now, we finally locked the player's profile + plugin.getProfileManager().lockProfile(player); + // And send the message to the command sender + sender.sendMessage(Common.color(ConfigValue.LOCK_PROFILE_OTHERS + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{player}", player.getName()))); + } + return true; + } + + @Nullable + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, @NotNull String[] args) { + if(args.length == 1 && sender.hasPermission("playerprofiles.lockprofile.others")){ + return null; + } + return new ArrayList<>(); + } +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/MainCommand.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/MainCommand.java new file mode 100644 index 0000000..9383be9 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/MainCommand.java @@ -0,0 +1,128 @@ +package com.muhammaddaffa.playerprofiles.commands; + +import com.muhammaddaffa.mdlib.utils.Common; +import com.muhammaddaffa.mdlib.utils.Logger; +import com.muhammaddaffa.playerprofiles.ConfigValue; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import com.muhammaddaffa.playerprofiles.commands.abstraction.SubCommand; +import com.muhammaddaffa.playerprofiles.commands.subcommands.ListGUICommand; +import com.muhammaddaffa.playerprofiles.commands.subcommands.OpenGUICommand; +import com.muhammaddaffa.playerprofiles.commands.subcommands.OpenProfileCommand; +import com.muhammaddaffa.playerprofiles.commands.subcommands.ReloadCommand; +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MainCommand implements CommandExecutor, TabCompleter { + + private static final String COMMAND_NAME = "playerprofiles"; + + private final Map subCommandMap = new HashMap<>(); + + private final PlayerProfiles plugin; + public MainCommand(PlayerProfiles plugin){ + this.plugin = plugin; + + // Reload command + this.subCommandMap.put("reload", new ReloadCommand()); + // Open GUI command + this.subCommandMap.put("opengui", new OpenGUICommand()); + // List GUI command + this.subCommandMap.put("listgui", new ListGUICommand()); + // Open profile command + this.subCommandMap.put("openprofile", new OpenProfileCommand()); + } + + public void registerThisCommand(){ + plugin.getCommand(COMMAND_NAME).setExecutor(this); + plugin.getCommand(COMMAND_NAME).setTabCompleter(this); + // Get the file configuration of config.yml + FileConfiguration config = PlayerProfiles.CONFIG_DEFAULT.getConfig(); + // Get the aliases from the config + List aliases = config.getStringList("commandAliases.playerProfiles"); + // Add all aliases to the command + plugin.getCommand(COMMAND_NAME).getAliases().addAll(aliases); + // Trying to register the aliases from the config + try{ + final Field bukkitCommandMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); + + bukkitCommandMap.setAccessible(true); + ((CommandMap) bukkitCommandMap.get(Bukkit.getServer())).register(COMMAND_NAME, plugin.getCommand(COMMAND_NAME)); + + bukkitCommandMap.setAccessible(false); + } catch (Exception ex){ + Logger.info("&cFailed to register /playerprofiles command"); + ex.printStackTrace(); + } + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, String[] args) { + + // Return if the args length is 0 and send help messages + if(args.length == 0){ + this.sendHelpMessages(sender); + return true; + } + + // Trying to get subcommand from 'args[0]' + SubCommand subCommand = this.subCommandMap.get(args[0].toLowerCase()); + + // Return if there is no subcommand with 'args[0]' and send help messages + if(subCommand == null) { + this.sendHelpMessages(sender); + return true; + } + + // Check if sub command has permission + if(subCommand.getPermission() != null){ + // Check if sender/player doesn't have permission for the subcommand + if(!(sender.hasPermission(subCommand.getPermission()))){ + // Return and send messages + sender.sendMessage(Common.color(ConfigValue.NO_PERMISSION + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{permission}", subCommand.getPermission()))); + return true; + } + } + + // Execute the sub command + subCommand.execute(plugin, sender, args); + return true; + } + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, String[] args) { + + if(args.length == 1){ + return new ArrayList<>(this.subCommandMap.keySet()); + } + + if(args.length >= 2){ + SubCommand subCommand = this.subCommandMap.get(args[0].toLowerCase()); + if(subCommand == null) return null; + + if(subCommand.getPermission() == null){ + return subCommand.parseTabCompletion(plugin, sender, args); + } + if(sender.hasPermission(subCommand.getPermission())){ + return subCommand.parseTabCompletion(plugin, sender, args); + } + return new ArrayList<>(); + } + return new ArrayList<>(); + } + + private void sendHelpMessages(CommandSender sender){ + ConfigValue.HELP_MESSAGES.forEach(message -> + sender.sendMessage(Common.color(message))); + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/ProfileCommand.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/ProfileCommand.java new file mode 100644 index 0000000..4006895 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/ProfileCommand.java @@ -0,0 +1,109 @@ +package com.muhammaddaffa.playerprofiles.commands; + +import com.muhammaddaffa.mdlib.utils.Common; +import com.muhammaddaffa.mdlib.utils.Logger; +import com.muhammaddaffa.playerprofiles.ConfigValue; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import com.muhammaddaffa.playerprofiles.inventory.InventoryManager; +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +public class ProfileCommand implements CommandExecutor, TabCompleter { + + private static final String COMMAND_NAME = "profile"; + + private final PlayerProfiles plugin; + public ProfileCommand(PlayerProfiles plugin){ + this.plugin = plugin; + } + + public void registerThisCommand(){ + plugin.getCommand(COMMAND_NAME).setExecutor(this); + plugin.getCommand(COMMAND_NAME).setTabCompleter(this); + // Get the file configuration of config.yml + FileConfiguration config = PlayerProfiles.CONFIG_DEFAULT.getConfig(); + // Get the aliases from the config + List aliases = config.getStringList("commandAliases.profile"); + // Add all aliases to the command + plugin.getCommand(COMMAND_NAME).getAliases().addAll(aliases); + // Trying to register the aliases from the config + try{ + final Field bukkitCommandMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); + + bukkitCommandMap.setAccessible(true); + ((CommandMap) bukkitCommandMap.get(Bukkit.getServer())).register(COMMAND_NAME, plugin.getCommand(COMMAND_NAME)); + + bukkitCommandMap.setAccessible(false); + } catch (Exception ex){ + Logger.info("&cFailed to register /profile command"); + ex.printStackTrace(); + } + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, @NotNull String[] args) { + // If the command sender is not player, return the code + if(!(sender instanceof Player)){ + Logger.info("&cOnly players can execute /profile command"); + return true; + } + // Get the player object from the sender + Player player = (Player) sender; + // Get the InventoryManager + InventoryManager inventoryManager = plugin.getInventoryManager(); + // Check if args length is 0 (/profile) + if (args.length == 0) { + // Permission: playerprofiles.profile + if(!(player.hasPermission("playerprofiles.profile"))){ + player.sendMessage(Common.color(ConfigValue.NO_PERMISSION + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{permission}", "playerprofiles.profile"))); + return true; + } + // If the player has permission, open the profile + inventoryManager.openInventory(null, player, player); + // Stop the code here + return true; + } + // Check if args length is 1 (/profile (player)) + if (args.length == 1) { + // Permission: playerprofiles.profile.others + if(!(player.hasPermission("playerprofiles.profile.others"))){ + player.sendMessage(Common.color(ConfigValue.NO_PERMISSION + .replace("{prefix}", ConfigValue.PREFIX) + .replace("[permission}", "playerprofiles.profile.others"))); + return true; + } + // If the player has permission, open the profile of the target + // First, get the target as Player object + Player target = Bukkit.getPlayer(args[0]); + // If the target is invalid, return the code + if(target == null){ + sender.sendMessage(Common.color(ConfigValue.INVALID_PLAYER + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{player}", args[0]))); + return true; + } + // If the target is valid, we open the target's profile for player + inventoryManager.openInventory(null, player, target); + } + return true; + } + + @Nullable + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, @NotNull String[] args) { + if(args.length == 1 && sender.hasPermission("playerprofiles.profile.others")){ + return null; + } + return new ArrayList<>(); + } +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/ToggleCommand.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/ToggleCommand.java new file mode 100644 index 0000000..40ee5db --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/ToggleCommand.java @@ -0,0 +1,86 @@ +package com.muhammaddaffa.playerprofiles.commands; + +import com.muhammaddaffa.mdlib.utils.Common; +import com.muhammaddaffa.mdlib.utils.Logger; +import com.muhammaddaffa.playerprofiles.ConfigValue; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +public class ToggleCommand implements CommandExecutor, TabCompleter { + + private static final String COMMAND_NAME = "toggleprofile"; + + private final PlayerProfiles plugin; + public ToggleCommand(PlayerProfiles plugin){ + this.plugin = plugin; + } + + public void registerThisCommand(){ + plugin.getCommand(COMMAND_NAME).setExecutor(this); + plugin.getCommand(COMMAND_NAME).setTabCompleter(this); + // Get the file configuration of config.yml + // Trying to register the aliases from the config + try{ + final Field bukkitCommandMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); + + bukkitCommandMap.setAccessible(true); + ((CommandMap) bukkitCommandMap.get(Bukkit.getServer())).register(COMMAND_NAME, plugin.getCommand(COMMAND_NAME)); + + bukkitCommandMap.setAccessible(false); + } catch (Exception ex){ + Logger.info("&cFailed to register /toggleprofile command"); + ex.printStackTrace(); + } + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, @NotNull String[] args) { + // Check if the args length is 0 (/lockprofile) + if(args.length == 0){ + // Permission: playerprofiles.lockprofile + if(!(sender.hasPermission("playerprofiles.toggleprofile"))){ + sender.sendMessage(Common.color(ConfigValue.NO_PERMISSION + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{permission}", "playerprofiles.toggleprofile"))); + return true; + } + // If the sender is not a player (the sender is console) + if(!(sender instanceof Player)){ + // Send console a usage message and return the code + sender.sendMessage(Common.color("&cUsage: /toggleprofile (player)")); + return true; + } + // After the check above, we can get the Player object from the sender + Player player = (Player) sender; + // Check if player is locked + if (plugin.getProfileManager().getOrCreate(player).isLocked()) { + player.sendMessage(Common.color(ConfigValue.UNLOCK_PROFILE + .replace("{prefix}", ConfigValue.PREFIX))); + // Actually lock the player's profile + plugin.getProfileManager().unlockProfile(player); + } else { + // Send a lock profile message to player + player.sendMessage(Common.color(ConfigValue.LOCK_PROFILE + .replace("{prefix}", ConfigValue.PREFIX))); + // Actually lock the player's profile + plugin.getProfileManager().lockProfile(player); + } + } + return true; + } + + @Nullable + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, @NotNull String[] args) { + return new ArrayList<>(); + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/UnlockCommand.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/UnlockCommand.java new file mode 100644 index 0000000..3d8e943 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/UnlockCommand.java @@ -0,0 +1,113 @@ +package com.muhammaddaffa.playerprofiles.commands; + +import com.muhammaddaffa.mdlib.utils.Common; +import com.muhammaddaffa.mdlib.utils.Logger; +import com.muhammaddaffa.playerprofiles.ConfigValue; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +public class UnlockCommand implements CommandExecutor, TabCompleter { + + private static final String COMMAND_NAME = "unlockprofile"; + + private final PlayerProfiles plugin; + public UnlockCommand(PlayerProfiles plugin){ + this.plugin = plugin; + } + + public void registerThisCommand(){ + plugin.getCommand(COMMAND_NAME).setExecutor(this); + plugin.getCommand(COMMAND_NAME).setTabCompleter(this); + // Get the file configuration of config.yml + FileConfiguration config = PlayerProfiles.CONFIG_DEFAULT.getConfig(); + // Get the aliases from the config + List aliases = config.getStringList("commandAliases.unlockProfile"); + // Add all aliases to the command + plugin.getCommand(COMMAND_NAME).getAliases().addAll(aliases); + // Trying to register the aliases from the config + try{ + final Field bukkitCommandMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); + + bukkitCommandMap.setAccessible(true); + ((CommandMap) bukkitCommandMap.get(Bukkit.getServer())).register(COMMAND_NAME, plugin.getCommand(COMMAND_NAME)); + + bukkitCommandMap.setAccessible(false); + } catch (Exception ex){ + Logger.info("&cFailed to register /unlockprofile command"); + ex.printStackTrace(); + } + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, @NotNull String[] args) { + // Check if the args length is 0 (/unlockprofile) + if(args.length == 0){ + // Permission: playerprofiles.unlockprofile + if(!(sender.hasPermission("playerprofiles.unlockprofile"))){ + sender.sendMessage(Common.color(ConfigValue.NO_PERMISSION + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{permission}", "playerprofiles.unlockprofile"))); + return true; + } + // If the sender is not a player (the sender is console) + if(!(sender instanceof Player)){ + // Send console a usage message and return the code + sender.sendMessage(Common.color("&cUsage: /unlockprofile (player)")); + return true; + } + // After the check above, we can get the Player object from the sender + Player player = (Player) sender; + // Send a lock profile message to player + player.sendMessage(Common.color(ConfigValue.UNLOCK_PROFILE + .replace("{prefix}", ConfigValue.PREFIX))); + // Actually lock the player's profile + plugin.getProfileManager().unlockProfile(player); + } + // Check if args length is 1 (/unlockprofile (player)) + if(args.length == 1){ + // Permission: playerprofiles.unlockprofile.others + if(!(sender.hasPermission("playerprofiles.unlockprofile.others"))){ + sender.sendMessage(Common.color(ConfigValue.NO_PERMISSION + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{permission}", "playerprofiles.unlockprofile.others"))); + return true; + } + // Get the player object from args[0] + Player player = Bukkit.getPlayer(args[0]); + // Check if the player is not valid or online + if(player == null){ + // Send an invalid player message + sender.sendMessage(Common.color(ConfigValue.INVALID_PLAYER + .replace("{prefix}", ConfigValue.PREFIX))); + // Stop the code + return true; + } + // The player is valid and everything's going well + // Now, we finally locked the player's profile + plugin.getProfileManager().unlockProfile(player); + // And send the message to the command sender + sender.sendMessage(Common.color(ConfigValue.UNLOCK_PROFILE_OTHERS + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{player}", player.getName()))); + } + return true; + } + + @Nullable + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, @NotNull String[] args) { + if(args.length == 1 && sender.hasPermission("playerprofiles.unlockprofile.others")){ + return null; + } + return new ArrayList<>(); + } +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/abstraction/SubCommand.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/abstraction/SubCommand.java new file mode 100644 index 0000000..a72253d --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/abstraction/SubCommand.java @@ -0,0 +1,19 @@ +package com.muhammaddaffa.playerprofiles.commands.abstraction; + +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public abstract class SubCommand { + + @Nullable + public abstract String getPermission(); + + @Nullable + public abstract List parseTabCompletion(PlayerProfiles plugin, CommandSender sender, String[] args); + + public abstract void execute(PlayerProfiles plugin, CommandSender sender, String[] args); + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/ListGUICommand.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/ListGUICommand.java new file mode 100644 index 0000000..2f75481 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/ListGUICommand.java @@ -0,0 +1,36 @@ +package com.muhammaddaffa.playerprofiles.commands.subcommands; + +import com.muhammaddaffa.mdlib.utils.Common; +import com.muhammaddaffa.playerprofiles.ConfigValue; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import com.muhammaddaffa.playerprofiles.commands.abstraction.SubCommand; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class ListGUICommand extends SubCommand { + + @Override + public @Nullable String getPermission() { + return "playerprofiles.admin"; + } + + @Override + public @NotNull List parseTabCompletion(PlayerProfiles plugin, CommandSender sender, String[] args) { + return new ArrayList<>(); + } + + @Override + public void execute(PlayerProfiles plugin, CommandSender sender, String[] args) { + // First, get the list of gui name in comma + String guiList = String.join(", ", plugin.getCustomGUIManager().getListName()); + // Send the message to the command sender + sender.sendMessage(Common.color(ConfigValue.LIST_GUI + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{gui}", guiList))); + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/OpenGUICommand.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/OpenGUICommand.java new file mode 100644 index 0000000..514e2a9 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/OpenGUICommand.java @@ -0,0 +1,91 @@ +package com.muhammaddaffa.playerprofiles.commands.subcommands; + +import com.muhammaddaffa.playerprofiles.ConfigValue; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import com.muhammaddaffa.playerprofiles.commands.abstraction.SubCommand; +import com.muhammaddaffa.playerprofiles.inventory.InventoryManager; +import com.muhammaddaffa.playerprofiles.manager.customgui.CustomGUI; +import com.muhammaddaffa.playerprofiles.manager.customgui.CustomGUIManager; +import com.muhammaddaffa.mdlib.utils.Common; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class OpenGUICommand extends SubCommand { + + @Override + public @Nullable String getPermission() { + return "playerprofiles.admin"; + } + + @Override + public List parseTabCompletion(PlayerProfiles plugin, CommandSender sender, String[] args) { + if(args.length == 2){ + return null; + } + if(args.length == 3){ + return null; + } + if(args.length == 4){ + return plugin.getCustomGUIManager().getListName(); + } + return new ArrayList<>(); + } + + @Override + public void execute(PlayerProfiles plugin, CommandSender sender, String[] args) { + // The full command is /playerprofiles opengui (player) (target) (gui-name) - args length = 4 + // So we want to tell the command sender if the the args doesn't enough + if(args.length < 4){ + sender.sendMessage(Common.color("&cUsage: /playerprofiles opengui (player) (target) (gui-name)")); + return; + } + // First get the player object from args[1] + Player player = Bukkit.getPlayer(args[1]); + // Check if the player is not valid + if(player == null){ + // If the player isn't valid, we want to tell the command sender + sender.sendMessage(Common.color(ConfigValue.INVALID_PLAYER + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{player}", args[1]))); + // Stop the code here + return; + } + // After the check above, we already guaranteed the Player would not be null + // Now we want to get the Player object as target from args[2] + Player target = Bukkit.getPlayer(args[2]); + // Check if the target is not valid player + if(target == null){ + // If the target is not valid player, we want to warn the command sender + sender.sendMessage(Common.color(ConfigValue.INVALID_PLAYER + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{player}", args[2]))); + } + // After the check above, we already guaranteed the target would not be null + // then we want to get the Custom GUI from args[3] + // First, we get the CustomGUIManager + CustomGUIManager customGUIManager = plugin.getCustomGUIManager(); + // And get the CustomGUI object + CustomGUI customGUI = customGUIManager.getByName(args[3]); + // And we check if the custom gui is null or doesn't exist + if(customGUI == null){ + // We want to warn the sender if the custom gui isn't exist + sender.sendMessage(Common.color(ConfigValue.INVALID_GUI_NAME + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{gui}", args[3]))); + // And stop the code here + return; + } + // After the check above we guaranteed the custom gui is not null + // And now we're gonna try to open the custom gui + // But first, we need to get the inventory manager + InventoryManager inventoryManager = plugin.getInventoryManager(); + // Finally, we open the inventory for the player + inventoryManager.openInventory(customGUI, player, target); + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/OpenProfileCommand.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/OpenProfileCommand.java new file mode 100644 index 0000000..577f46d --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/OpenProfileCommand.java @@ -0,0 +1,67 @@ +package com.muhammaddaffa.playerprofiles.commands.subcommands; + +import com.muhammaddaffa.playerprofiles.ConfigValue; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import com.muhammaddaffa.playerprofiles.commands.abstraction.SubCommand; +import com.muhammaddaffa.mdlib.utils.Common; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class OpenProfileCommand extends SubCommand { + + @Override + public @Nullable String getPermission() { + return "playerprofiles.admin"; + } + + @Override + public @NotNull List parseTabCompletion(PlayerProfiles plugin, CommandSender sender, String[] args) { + if(args.length == 2){ + return Collections.singletonList("(target-player)"); + } + if(args.length == 3){ + return Collections.singletonList("(open-for)"); + } + return new ArrayList<>(); + } + + @Override + public void execute(PlayerProfiles plugin, CommandSender sender, String[] args) { + // The full command is /playerprofiles openprofile (target) (for-player) - args length = 3 + // So we want to tell the command sender if the the args doesn't enough + if(args.length < 3){ + sender.sendMessage(Common.color("&cUsage: /playerprofiles openprofile (target) (for-player)")); + return; + } + // Get the target as Player object + Player target = Bukkit.getPlayer(args[1]); + // Check if the target is not valid player + if(target == null){ + // If the target is not valid player, we want to warn the command sender + sender.sendMessage(Common.color(ConfigValue.INVALID_PLAYER + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{player}", args[2]))); + } + // After we check with the above method, we guaranteed the target isn't null + // And now we want to get the player object + Player player = Bukkit.getPlayer(args[2]); + // Check if the player is not valid player + if(player == null){ + // If the player is not valid player, we want to warn the command sender + sender.sendMessage(Common.color(ConfigValue.INVALID_PLAYER + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{player}", args[2]))); + } + // Now we are guaranteed both player and target will not be null + // Finally, we open the profile of target for player + plugin.getInventoryManager().openInventory(null, player, target); + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/ReloadCommand.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/ReloadCommand.java new file mode 100644 index 0000000..e1eaf63 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/commands/subcommands/ReloadCommand.java @@ -0,0 +1,35 @@ +package com.muhammaddaffa.playerprofiles.commands.subcommands; + +import com.muhammaddaffa.playerprofiles.ConfigValue; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import com.muhammaddaffa.playerprofiles.commands.abstraction.SubCommand; +import com.muhammaddaffa.mdlib.utils.Common; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class ReloadCommand extends SubCommand { + + @Override + public @Nullable String getPermission() { + return "playerprofiles.admin"; + } + + @Override + public @NotNull List parseTabCompletion(PlayerProfiles plugin, CommandSender sender, String[] args) { + return new ArrayList<>(); + } + + @Override + public void execute(PlayerProfiles plugin, CommandSender sender, String[] args) { + // Call the reload method from the PlayerProfiles class + plugin.reloadAllThing(); + // Send the message to the command sender + sender.sendMessage(Common.color(ConfigValue.RELOAD + .replace("{prefix}", ConfigValue.PREFIX))); + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/hooks/combatlogx/HCombatLogX.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/hooks/combatlogx/HCombatLogX.java new file mode 100644 index 0000000..ddf4fc4 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/hooks/combatlogx/HCombatLogX.java @@ -0,0 +1,53 @@ +package com.muhammaddaffa.playerprofiles.hooks.combatlogx; + +import com.muhammaddaffa.mdlib.utils.Logger; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import java.lang.reflect.Method; + +public class HCombatLogX { + + private static Method isInCombatMethod; + private static boolean initialized = false; + + private static void initialize() { + if (initialized) return; + initialized = true; + + Plugin plugin = Bukkit.getPluginManager().getPlugin("CombatLogX"); + if (plugin == null) return; + + try { + Class combatLogXClass = Class.forName("com.github.sirblobman.combatlogx.api.ICombatLogX"); + Class combatManagerClass = Class.forName("com.github.sirblobman.combatlogx.api.manager.ICombatManager"); + + Method getCombatManagerMethod = combatLogXClass.getMethod("getCombatManager"); + Object combatManager = getCombatManagerMethod.invoke(plugin); + + isInCombatMethod = combatManagerClass.getMethod("isInCombat", Player.class); + } catch (Exception e) { + Logger.severe("Failed to initialize CombatLogX hook: " + e.getMessage()); + } + } + + public static boolean isInCombat(Player player) { + initialize(); + if (isInCombatMethod == null) return false; + + try { + Plugin plugin = Bukkit.getPluginManager().getPlugin("CombatLogX"); + if (plugin == null) return false; + + Class combatLogXClass = Class.forName("com.github.sirblobman.combatlogx.api.ICombatLogX"); + Method getCombatManagerMethod = combatLogXClass.getMethod("getCombatManager"); + Object combatManager = getCombatManagerMethod.invoke(plugin); + + return (boolean) isInCombatMethod.invoke(combatManager, player); + } catch (Exception e) { + return false; + } + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/hooks/deluxecombat/HDeluxeCombat.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/hooks/deluxecombat/HDeluxeCombat.java new file mode 100644 index 0000000..8f99e06 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/hooks/deluxecombat/HDeluxeCombat.java @@ -0,0 +1,12 @@ +package com.muhammaddaffa.playerprofiles.hooks.deluxecombat; + +import com.muhammaddaffa.playerprofiles.manager.DependencyManager; +import org.bukkit.entity.Player; + +public class HDeluxeCombat { + + public static boolean isInCombat(Player player){ + return DependencyManager.getDeluxeAPI().isInCombat(player); + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/InventoryManager.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/InventoryManager.java new file mode 100644 index 0000000..07179fe --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/InventoryManager.java @@ -0,0 +1,55 @@ +package com.muhammaddaffa.playerprofiles.inventory; + +import com.muhammaddaffa.mdlib.fastinv.FastInv; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import com.muhammaddaffa.playerprofiles.inventory.items.ItemsLoader; +import com.muhammaddaffa.playerprofiles.manager.customgui.CustomGUI; +import com.muhammaddaffa.playerprofiles.manager.customgui.CustomGUIManager; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +import java.util.logging.Logger; + +public class InventoryManager { + + private final ItemsLoader itemsLoader; + private final CustomGUIManager customGUIManager; + public InventoryManager(PlayerProfiles plugin){ + itemsLoader = new ItemsLoader(plugin); + customGUIManager = plugin.getCustomGUIManager(); + } + + public void initialize(){ + itemsLoader.loadItems(); + } + + public void reInitialize(){ + itemsLoader.reloadItems(); + } + + public void openInventory(@Nullable CustomGUI customGUI, Player player, Player target){ + // Check if the file name is null (means it's the main inventory) + if(customGUI == null){ + FileConfiguration config = PlayerProfiles.GUI_DEFAULT.getConfig(); + + String title = config.getString("title"); + + int size = config.getInt("size"); + + FastInv inventory = new ProfileInventory(itemsLoader.getMainMenuItems(), player, target, size, title); + inventory.open(player); + //System.out.println("FROM NULL"); + return; + } + // Code logic if the custom gui isn't null + // And the CustomGUI is @NotNull because it has been checked before calling this method + // Create the LazyInventory object + FastInv inventory = new ProfileInventory(customGUI.items(), player, target, customGUI.size(), customGUI.title()); + //System.out.println("NOT NULL"); + // Finally open the inventory for the player + inventory.open(player); + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/ProfileInventory.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/ProfileInventory.java new file mode 100644 index 0000000..94cbf4f --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/ProfileInventory.java @@ -0,0 +1,118 @@ +package com.muhammaddaffa.playerprofiles.inventory; + +import com.muhammaddaffa.mdlib.fastinv.FastInv; +import com.muhammaddaffa.mdlib.task.handleTask.HandleTask; +import com.muhammaddaffa.mdlib.utils.Common; +import com.muhammaddaffa.mdlib.utils.Executor; +import com.muhammaddaffa.playerprofiles.ConfigValue; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import com.muhammaddaffa.playerprofiles.inventory.items.GUIItem; +import com.muhammaddaffa.playerprofiles.utils.ClickManager; +import com.muhammaddaffa.playerprofiles.utils.ItemManager; +import com.muhammaddaffa.playerprofiles.utils.Utils; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitTask; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class ProfileInventory extends FastInv { + + public ProfileInventory(List items, Player player, Player target, int size, String title) { + super(size, Utils.tryParsePAPI(Common.color(title), player, target)); + + this.setAllItems(items, player, target); + + // If auto refresh is enabled + if(ConfigValue.AUTO_REFRESH_ENABLED){ + // Start the auto refresh task + HandleTask task = Executor.syncTimer(0L, ConfigValue.AUTO_REFRESH_TICK, () -> + this.setAllItems(items, player, target)); + // And remove the task after the inventory closed + this.addCloseHandler(event -> task.cancel()); + } + // If distance check is enabled + if(ConfigValue.DISTANCE_CHECK_ENABLED){ + // Start the check distance task + HandleTask task = Executor.syncTimer(0L, 20L, () -> + checkDistance(player, target)); + // And remove the task after the inventory closed + this.addCloseHandler(event -> task.cancel()); + } + + } + + private void setAllItems(List items, Player player, Player target) { + List clonedItem = new ArrayList<>(items); + clonedItem.sort(Comparator.comparingInt(GUIItem::priority)); + + // Loop through all items + clonedItem.forEach(item -> { + // Get the item stack + ItemStack stack = ItemManager.createGUIItem(item, player, target); + // Check if the item use permission to see + if(item.usePermission()){ + // If the player doesn't have the permission, don't show it + if(!player.hasPermission(item.permission())) { + return; + } + } + // Check if the item is set to only visitor + if(item.onlyVisitor()){ + // If the player and the target is the same, don't show the item + if(player.equals(target)) { + return; + } + } + // Check if the item is set to only owner + if(item.onlyOwner()){ + // If the player and the target is not the same, don't show the item + if(!player.equals(target)) { + return; + } + } + // Now, set the item to the inventory + this.setItems(item.slots(), stack, event -> { + // Set the click event for the item + ClickManager.handleInventoryClick(item, player, target, event); + }); + }); + // After all items being set, we finally set the fill items + ItemManager.fillItem(this, PlayerProfiles.GUI_DEFAULT.getConfig()); + } + + private void checkDistance(Player player, Player target){ + // If the target is offline, just close the inventory + if(target == null){ + // Close the inventory + player.closeInventory(); + // Send a message to the player + player.sendMessage(Common.color(ConfigValue.DISTANCE_TOO_FAR + .replace("{prefix}", ConfigValue.PREFIX)) + .replace("{player}", target.getName())); + return; + } + if(!player.getWorld().equals(target.getWorld())){ + // Close the inventory + player.closeInventory(); + // Send a message to the player + player.sendMessage(Common.color(ConfigValue.DISTANCE_TOO_FAR + .replace("{prefix}", ConfigValue.PREFIX)) + .replace("{player}", target.getName())); + return; + } + // Get the distance between the player and the target + double distance = player.getLocation().distance(target.getLocation()); + // Check if the distance is greater than the configured maximum distance + if(distance > ConfigValue.MAXIMUM_DISTANCE){ + // Close the inventory + player.closeInventory(); + // Send a message to the player + player.sendMessage(Common.color(ConfigValue.DISTANCE_TOO_FAR + .replace("{prefix}", ConfigValue.PREFIX)) + .replace("{player}", target.getName())); + } + } +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/items/GUIItem.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/items/GUIItem.java new file mode 100644 index 0000000..3e76e71 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/items/GUIItem.java @@ -0,0 +1,65 @@ +package com.muhammaddaffa.playerprofiles.inventory.items; + +import java.util.List; + +public record GUIItem( + String type, + String material, + int amount, + String name, + List slots, + boolean glowing, + boolean hideAttributes, + boolean usePermission, + String permission, + List lore, + List leftCommands, + List rightCommands, + int customModelData, + String itemModel, + boolean onlyOwner, + boolean onlyVisitor, + int priority +) { + + @Override + public String type() { + if (type == null) { + return "DUMMY"; + } + return type; + } + + @Override + public String material() { + if (material == null) { + return "BARRIER"; + } + return material; + } + + @Override + public String name() { + if (name == null) { + return "&cInvalid name! Specify it on the config!"; + } + return name; + } + + @Override + public String permission() { + if (permission == null) { + return "invalid.permission"; + } + return permission; + } + + @Override + public int priority() { + if (priority == 0) { + return 100; + } + return priority; + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/items/ItemsLoader.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/items/ItemsLoader.java new file mode 100644 index 0000000..078dc92 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/inventory/items/ItemsLoader.java @@ -0,0 +1,69 @@ +package com.muhammaddaffa.playerprofiles.inventory.items; + +import com.muhammaddaffa.playerprofiles.PlayerProfiles; + +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.ArrayList; +import java.util.List; + +public class ItemsLoader { + + private final List mainMenuItems = new ArrayList<>(); + + private final PlayerProfiles plugin; + public ItemsLoader(PlayerProfiles plugin){ + this.plugin = plugin; + } + + public void loadItems(){ + loadMainMenuItems(); + } + + public void reloadItems(){ + mainMenuItems.clear(); + + loadMainMenuItems(); + } + + private void loadMainMenuItems(){ + FileConfiguration config = PlayerProfiles.GUI_DEFAULT.getConfig(); + // Return if there is no items + if(!config.isConfigurationSection("items")) return; + // Loop through all items + for(String configKey : config.getConfigurationSection("items").getKeys(false)){ + String path = "items." + configKey; + String type = config.getString(path + ".type"); + String material = config.getString(path + ".material"); + int amount = config.getInt(path + ".amount"); + String name = config.getString(path + ".name"); + List slots = config.getIntegerList(path + ".slots"); + boolean glowing = config.getBoolean(path + ".glowing"); + boolean hideAttributes = config.getBoolean(path + ".hideAttributes"); + boolean usePermission = config.getBoolean(path + ".usePermission"); + String permission = config.getString(path + ".permission"); + List lore = config.getStringList(path + ".lore"); + List leftCommands = config.getStringList(path + ".leftClickCommands"); + List rightCommands = config.getStringList(path + ".rightClickCommands"); + int customModelData = config.getInt(path + ".customModelData"); + String itemModel = config.getString(path + ".itemModel"); + boolean onlyOwner = config.getBoolean(path + ".onlyOwner"); + boolean onlyVisitor = config.getBoolean(path + ".onlyVisitor"); + int priority = config.getInt(path + ".priority", 0); + + // Null-safe for item model + if (itemModel == null || itemModel.isEmpty()) { + itemModel = ""; + } + // Finally add the item to the list + GUIItem guiItem = new GUIItem(type, material, amount, name, slots, glowing, hideAttributes, usePermission, + permission, lore, leftCommands, rightCommands, customModelData, itemModel, onlyOwner, onlyVisitor, priority); + this.mainMenuItems.add(guiItem); + } + } + + public List getMainMenuItems(){ + return mainMenuItems; + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/listeners/PlayerInteract.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/listeners/PlayerInteract.java new file mode 100644 index 0000000..da4e38c --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/listeners/PlayerInteract.java @@ -0,0 +1,151 @@ +package com.muhammaddaffa.playerprofiles.listeners; + +import com.muhammaddaffa.mdlib.utils.Common; +import com.muhammaddaffa.playerprofiles.ConfigValue; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import com.muhammaddaffa.playerprofiles.hooks.combatlogx.HCombatLogX; +import com.muhammaddaffa.playerprofiles.hooks.deluxecombat.HDeluxeCombat; +import com.muhammaddaffa.playerprofiles.inventory.InventoryManager; +import com.muhammaddaffa.playerprofiles.manager.DependencyManager; +import com.muhammaddaffa.playerprofiles.manager.profile.ProfileManager; +import com.muhammaddaffa.playerprofiles.utils.Utils; +import com.muhammaddaffa.playerprofiles.worldguardwrapper.WorldGuardWrapper; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.inventory.EquipmentSlot; + +import java.util.HashMap; +import java.util.Map; + +public class PlayerInteract implements Listener { + + private final PlayerProfiles plugin; + public PlayerInteract(PlayerProfiles plugin){ + this.plugin = plugin; + } + + // This is for the cooldown feature + private final Map mapCooldown = new HashMap<>(); + + @EventHandler + public void onPlayerRightClickEntity(PlayerInteractAtEntityEvent event){ + // First of all we want to return the code if the right clicked entity is not player + if(!(event.getRightClicked() instanceof Player)) return; + // Get the player object from this event + Player player = event.getPlayer(); + // We check if the server is 1.9+, means they have off hand + // This event will be fired twice for both main hand and off hand + // So we want to stop the code if the interact hand is an off hand + if(Utils.hasOffHand() && event.getHand() == EquipmentSlot.OFF_HAND){ + return; + } + // Check if the player must shift click to open the profile + if (ConfigValue.MUST_SHIFT_CLICK && !event.getPlayer().isSneaking()) { + return; + } + // Now, we get the right clicked entity as Player + Player target = (Player) event.getRightClicked(); + // Now we check for the NPC option, should we open the profile of the NPC? + if(ConfigValue.DISABLE_NPC_PROFILE && target.hasMetadata("NPC")){ + return; + } + // Profile locked feature, basically every player can lock their profile + // so no one can open their profile. First, we need to get the ProfileManager + ProfileManager profileManager = plugin.getProfileManager(); + if(profileManager.isProfileLocked(target)){ + // Send a message to the player + player.sendMessage(Common.color(ConfigValue.LOCKED_PROFILE + .replace("{prefix}", ConfigValue.PREFIX))); + // Stop the code + return; + } + // First, check for the cooldown feature, and now we check if the player is in cooldown + if(mapCooldown.containsKey(player)){ + // Get the time left + long timeLeft = this.getCooldownTimeLeft(player); + // We remove the player from the map if the time left is equals to below 0 + if(timeLeft <= 0){ + mapCooldown.remove(player); + } + // And if the time left is greater than 0, we stop the code here + if(timeLeft > 0){ + // Send player message + player.sendMessage(Common.color(ConfigValue.COOLDOWN_MESSAGE + .replace("{prefix}", ConfigValue.PREFIX) + .replace("{time}", timeLeft + ""))); + // Stop the code + return; + } + } + // Check for the disabled worlds, if the player is in disabled worlds + // they can't open others profile + if(ConfigValue.DISABLED_WORLDS.contains(player.getWorld().getName())){ + player.sendMessage(Common.color(ConfigValue.DISABLED_WORLD_MESSAGE + .replace("{prefix}", ConfigValue.PREFIX))); + return; + } + // Check for the world guard regions, if the player or the target is inside the disabled + // regions, the player cannot open the profile + // First of all, check if the world guard is enabled + if(DependencyManager.WORLD_GUARD){ + // First, we check for the player location, if the region is listed on the disabled regions + // we stopped the code + for(String region : WorldGuardWrapper.getInstance().getRegionFinder().getRegions(player.getLocation())){ + if(ConfigValue.DISABLED_REGIONS.contains(region)){ + player.sendMessage(Common.color(ConfigValue.PLAYER_DISABLED_REGIONS + .replace("{prefix}", ConfigValue.PREFIX))); + return; + } + } + // Now, we check for the target location + for(String region : WorldGuardWrapper.getInstance().getRegionFinder().getRegions(target.getLocation())){ + if(ConfigValue.DISABLED_REGIONS.contains(region)){ + player.sendMessage(Common.color(ConfigValue.TARGET_DISABLED_REGIONS + .replace("{prefix}", ConfigValue.PREFIX))); + return; + } + } + } + // Now we check for the combat part, this feature is to prevent player from + // opening profiles while on combat (Require: CombatLogX or DeluxeCombat) + if(ConfigValue.DISABLE_IN_COMBAT_ENABLED){ + // Check if CombatLogX is enabled + if(DependencyManager.COMBAT_LOG_X){ + // Check if the player is in combat, if true we return the code + if(HCombatLogX.isInCombat(player)){ + player.sendMessage(Common.color(ConfigValue.DISABLE_IN_COMBAT_MESSAGE + .replace("{prefix}", ConfigValue.PREFIX))); + return; + } + } + // Check if DeluxeCombat is enabled + if(DependencyManager.DELUXE_COMBAT){ + // Check if the player is in combat, if true we return the code + if(HDeluxeCombat.isInCombat(player)){ + player.sendMessage(Common.color(ConfigValue.DISABLE_IN_COMBAT_MESSAGE + .replace("{prefix}", ConfigValue.PREFIX))); + return; + } + } + } + // Now this feature is a interact cooldown message, that means player cannot + // spam open others profile, this option is recommended + if(ConfigValue.COOLDOWN_ENABLED){ + mapCooldown.put(player, System.currentTimeMillis()); + } + // After all those fucking checks, now we finally open the profiles + // for the player, first get the InventoryManager class + InventoryManager inventoryManager = plugin.getInventoryManager(); + // Now we open the inventory for the player + inventoryManager.openInventory(null, player, target); + // Play sound to the player + Utils.playSound(player, "onProfileOpen"); + } + + private Long getCooldownTimeLeft(Player player){ + return ((mapCooldown.get(player) / 1000) + ConfigValue.COOLDOWN_TIME) - (System.currentTimeMillis() / 1000); + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/DependencyManager.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/DependencyManager.java new file mode 100644 index 0000000..a879ec1 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/DependencyManager.java @@ -0,0 +1,50 @@ +package com.muhammaddaffa.playerprofiles.manager; + +import com.muhammaddaffa.mdlib.utils.Logger; +import nl.marido.deluxecombat.api.DeluxeCombatAPI; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; + +public class DependencyManager { + + public static boolean PLACEHOLDER_API; + public static boolean COMBAT_LOG_X; + + public static boolean DELUXE_COMBAT; + private static DeluxeCombatAPI deluxeAPI; + + public static boolean WORLD_GUARD; + public static int WORLD_GUARD_VERSION; + + public static void checkDependency(){ + PluginManager pm = Bukkit.getPluginManager(); + + PLACEHOLDER_API = pm.getPlugin("PlaceholderAPI") != null; + COMBAT_LOG_X = pm.getPlugin("CombatLogX") != null; + DELUXE_COMBAT = pm.getPlugin("DeluxeCombat") != null; + WORLD_GUARD = pm.getPlugin("WorldGuard") != null; + + if(DELUXE_COMBAT){ + deluxeAPI = new DeluxeCombatAPI(); + } + + if(WORLD_GUARD){ + Plugin plugin = pm.getPlugin("WorldGuard"); + + if(plugin.getDescription().getVersion().startsWith("6")) + Logger.info("&rFound WorldGuard! Using WorldGuard API version 6"); + WORLD_GUARD_VERSION = 6; + + if(plugin.getDescription().getVersion().startsWith("7")) + Logger.info("&rFound WorldGuard! Using WorldGuard API version 7"); + WORLD_GUARD_VERSION = 7; + } + + } + + public static DeluxeCombatAPI getDeluxeAPI(){ + return deluxeAPI; + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/customgui/CustomGUI.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/customgui/CustomGUI.java new file mode 100644 index 0000000..71ba724 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/customgui/CustomGUI.java @@ -0,0 +1,17 @@ +package com.muhammaddaffa.playerprofiles.manager.customgui; + +import com.muhammaddaffa.playerprofiles.inventory.items.GUIItem; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public record CustomGUI( + String fileName, + @NotNull String title, + int size, + FileConfiguration config, + List items +) { + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/customgui/CustomGUIManager.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/customgui/CustomGUIManager.java new file mode 100644 index 0000000..0cd2fef --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/customgui/CustomGUIManager.java @@ -0,0 +1,113 @@ +package com.muhammaddaffa.playerprofiles.manager.customgui; + +import com.muhammaddaffa.mdlib.utils.Common; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import com.muhammaddaffa.playerprofiles.inventory.items.GUIItem; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Boat; + +import java.io.File; +import java.util.*; + +public class CustomGUIManager { + + private final Map guiMap = new HashMap<>(); + + private final PlayerProfiles plugin; + public CustomGUIManager(PlayerProfiles plugin){ + this.plugin = plugin; + } + + public CustomGUI getByName(String name){ + return guiMap.get(name); + } + + public List getListName(){ + return new ArrayList<>(guiMap.keySet()); + } + + public void clearCustomGUI(){ + guiMap.clear(); + } + + public void reloadCustomGUI(){ + clearCustomGUI(); + loadCustomGUI(); + } + + public void loadCustomGUI() { + // Get the directory path + // Get the custom-gui directory + File directory = getMainDirectory(); + // Check if the directory is not exist + if(!directory.exists()) + // Finally create the directory + directory.mkdirs(); + // Get all files in custom-gui directory + File[] files = directory.listFiles(); + // Check if there is no files + if(files.length <= 0){ + // Create the file onto the server directory + plugin.saveResource("custom-gui/punish-gui.yml", false); + } + // Now get the final list of files in the directory + File[] finalFiles = directory.listFiles(); + // Loop through all files + for (File file : finalFiles){ + // Get the file configuration + FileConfiguration config = YamlConfiguration.loadConfiguration(file); + // Get the file name + String fileName = file.getName(); + // Create an empty array list of GUIItem + List guiItems = new ArrayList<>(); + // Get the inventory title + String title = config.getString("title"); + // Get the final title, null-safe + String finalTitle = title == null ? "Inventory" : title; + // Get the inventory size + int size = config.getInt("size"); + // Now, we load the items - first loop through all items + for (String configKey : config.getConfigurationSection("items").getKeys(false)) { + String path = "items." + configKey; + String type = config.getString(path + ".type"); + String material = config.getString(path + ".material"); + int amount = config.getInt(path + ".amount"); + String name = config.getString(path + ".name"); + List slots = config.getIntegerList(path + ".slots"); + boolean glowing = config.getBoolean(path + ".glowing"); + boolean hideAttributes = config.getBoolean(path + ".hideAttributes"); + boolean usePermission = config.getBoolean(path + ".usePermission"); + String permission = config.getString(path + ".permission"); + List lore = config.getStringList(path + ".lore"); + List leftCommands = config.getStringList(path + ".leftClickCommands"); + List rightCommands = config.getStringList(path + ".rightClickCommands"); + int customModelData = config.getInt(path + ".customModelData"); + String itemModel = config.getString(path + ".itemModel"); + boolean onlyOwner = config.getBoolean(path + ".onlyOwner"); + boolean onlyVisitor = config.getBoolean(path + ".onlyVisitor"); + int priority = config.getInt(path + ".priority", 0); + + // Null-safe for item model + if (itemModel == null || itemModel.isEmpty()) { + itemModel = ""; + } + // Create the GUIItem object + GUIItem guiItem = new GUIItem(type, material, amount, name, slots, glowing, hideAttributes, usePermission, + permission, lore, leftCommands, rightCommands, customModelData, itemModel, onlyOwner, onlyVisitor, priority); + // Finally add the gui item to the list that has been created before + guiItems.add(guiItem); + } + // After items are loaded, now we create the CustomGUI object + CustomGUI customGUI = new CustomGUI(fileName, Common.color(finalTitle), size, config, guiItems); + // Now we store the custom gui onto the list + this.guiMap.put(fileName, customGUI); + } + // That's it + } + + private File getMainDirectory() { + return new File(PlayerProfiles.getInstance().getDataFolder() + File.separator + "custom-gui"); + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/customgui/CustomGuiCreator.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/customgui/CustomGuiCreator.java new file mode 100644 index 0000000..cf903bd --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/customgui/CustomGuiCreator.java @@ -0,0 +1,65 @@ +package com.muhammaddaffa.playerprofiles.manager.customgui; + +import com.muhammaddaffa.mdlib.utils.Logger; +import com.muhammaddaffa.playerprofiles.ConfigValue; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +public class CustomGuiCreator { + + private final PlayerProfiles plugin; + + public CustomGuiCreator(PlayerProfiles plugin){ + this.plugin = plugin; + } + + public void createCustomGui() { + // Create the gui + FileConfiguration config = PlayerProfiles.GUI_CREATOR.getConfig(); + for (String section : config.getConfigurationSection("menu").getKeys(false)) { + String fileName = config.getString("menu." + section + ".fileName"); + if (fileName == null || !fileName.endsWith(".yml")) { + Logger.severe("Invalid file name or invalid extensions!"); + continue; + } + + File file = getFileName(fileName); + if (file.exists()) continue; + + // Create the file + try { + file.getParentFile().mkdirs(); + file.createNewFile(); + if (file.length() == 0) { + populateFile(file); + } + Logger.info("Created custom gui " + fileName); + } catch (Exception ex) { + Logger.severe("Failed to create custom gui " + fileName); + ex.printStackTrace(); + } + } + } + + private void populateFile(File file) { + // Populate the file + try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { + writer.write(ConfigValue.template()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private File getMainDirectory() { + return new File(PlayerProfiles.getInstance().getDataFolder() + File.separator + "custom-gui"); + } + private File getFileName(String name) { + return new File(PlayerProfiles.getInstance().getDataFolder() + File.separator + "custom-gui" + File.separator + name); + } +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/profile/Profile.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/profile/Profile.java new file mode 100644 index 0000000..92dd996 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/profile/Profile.java @@ -0,0 +1,29 @@ +package com.muhammaddaffa.playerprofiles.manager.profile; + +public class Profile { + + private final String uuid; + private boolean locked = false; + + public Profile(String uuid){ + this.uuid = uuid; + } + + public Profile(String uuid, boolean lockedStatus){ + this.uuid = uuid; + this.locked = lockedStatus; + } + + public String getUUID(){ + return uuid; + } + + public boolean isLocked(){ + return locked; + } + + public void setLocked(boolean status){ + this.locked = status; + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/profile/ProfileManager.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/profile/ProfileManager.java new file mode 100644 index 0000000..60ebbf9 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/profile/ProfileManager.java @@ -0,0 +1,80 @@ +package com.muhammaddaffa.playerprofiles.manager.profile; + +import com.muhammaddaffa.mdlib.utils.Config; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class ProfileManager { + + private final Map profileMap = new HashMap<>(); + + public Profile getOrCreate(Player player){ + return getOrCreate(player.getUniqueId()); + } + + public Profile getOrCreate(UUID uuid){ + return getOrCreate(uuid.toString()); + } + + public Profile getOrCreate(String uuid){ + // Check if the player has the data + if(profileMap.containsKey(uuid)){ + // Return the profile data, if the player has the data + return profileMap.get(uuid); + } + // If player doesn't have profile data, create a new one + Profile profile = new Profile(uuid); + // Store the data to the hash map + profileMap.put(uuid, profile); + // Return the new profile + return profile; + } + + public boolean isProfileLocked(Player player){ + return getOrCreate(player).isLocked(); + } + + public void lockProfile(Player player){ + getOrCreate(player).setLocked(true); + } + + public void unlockProfile(Player player){ + getOrCreate(player).setLocked(false); + } + + public void loadProfileData(){ + // Get the file configuration of data.yml + FileConfiguration config = PlayerProfiles.DATA_DEFAULT.getConfig(); + // If there is no data, just stop the code + if(!config.isConfigurationSection("data")) return; + // Loop through all data config section + for(String uuid : config.getConfigurationSection("data").getKeys(false)){ + // Get the lock status from the data + boolean lockedStatus = config.getBoolean("data." + uuid); + // Create a new profile object and store them into the hash map + profileMap.put(uuid, new Profile(uuid, lockedStatus)); + } + } + + public void saveProfileData(){ + // Get the File of data.yml + Config data = PlayerProfiles.DATA_DEFAULT; + // Get the file configuration of data.yml + FileConfiguration config = data.getConfig(); + // Loop through all data in the hash map + for(String uuid : profileMap.keySet()){ + // Get the profile object + Profile profile = getOrCreate(uuid); + // Set the profile data into the config + config.set("data." + uuid, profile.isLocked()); + } + // After all data has been set, save the config + data.saveConfig(); + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/metrics/Metrics.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/metrics/Metrics.java new file mode 100644 index 0000000..7b8db40 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/metrics/Metrics.java @@ -0,0 +1,848 @@ +package com.muhammaddaffa.playerprofiles.metrics; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Method; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.zip.GZIPOutputStream; +import javax.net.ssl.HttpsURLConnection; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +public class Metrics { + + private final Plugin plugin; + + private final MetricsBase metricsBase; + + /** + * Creates a new Metrics instance. + * + * @param plugin Your plugin instance. + * @param serviceId The id of the service. It can be found at What is my plugin id? + */ + public Metrics(JavaPlugin plugin, int serviceId) { + this.plugin = plugin; + // Get the config file + File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); + File configFile = new File(bStatsFolder, "config.yml"); + YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); + if (!config.isSet("serverUuid")) { + config.addDefault("enabled", true); + config.addDefault("serverUuid", UUID.randomUUID().toString()); + config.addDefault("logFailedRequests", false); + config.addDefault("logSentData", false); + config.addDefault("logResponseStatusText", false); + // Inform the server owners about bStats + config + .options() + .header( + "bStats (https://bStats.org) collects some basic information for plugin authors, like how\n" + + "many people use their plugin and their total player count. It's recommended to keep bStats\n" + + "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n" + + "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n" + + "anonymous.") + .copyDefaults(true); + try { + config.save(configFile); + } catch (IOException ignored) { + } + } + // Load the data + boolean enabled = config.getBoolean("enabled", true); + String serverUUID = config.getString("serverUuid"); + boolean logErrors = config.getBoolean("logFailedRequests", false); + boolean logSentData = config.getBoolean("logSentData", false); + boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false); + metricsBase = + new MetricsBase( + "bukkit", + serverUUID, + serviceId, + enabled, + this::appendPlatformData, + this::appendServiceData, + submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask), + plugin::isEnabled, + (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), + (message) -> this.plugin.getLogger().log(Level.INFO, message), + logErrors, + logSentData, + logResponseStatusText); + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + metricsBase.addCustomChart(chart); + } + + private void appendPlatformData(JsonObjectBuilder builder) { + builder.appendField("playerAmount", getPlayerAmount()); + builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0); + builder.appendField("bukkitVersion", Bukkit.getVersion()); + builder.appendField("bukkitName", Bukkit.getName()); + builder.appendField("javaVersion", System.getProperty("java.version")); + builder.appendField("osName", System.getProperty("os.name")); + builder.appendField("osArch", System.getProperty("os.arch")); + builder.appendField("osVersion", System.getProperty("os.version")); + builder.appendField("coreCount", Runtime.getRuntime().availableProcessors()); + } + + private void appendServiceData(JsonObjectBuilder builder) { + builder.appendField("pluginVersion", plugin.getDescription().getVersion()); + } + + private int getPlayerAmount() { + try { + // Around MC 1.8 the return type was changed from an array to a collection, + // This fixes java.lang.NoSuchMethodError: + // org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; + Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); + return onlinePlayersMethod.getReturnType().equals(Collection.class) + ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() + : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; + } catch (Exception e) { + // Just use the new method if the reflection failed + return Bukkit.getOnlinePlayers().size(); + } + } + + public static class MetricsBase { + + /** The version of the Metrics class. */ + public static final String METRICS_VERSION = "2.2.1"; + + private static final ScheduledExecutorService scheduler = + Executors.newScheduledThreadPool(1, task -> new Thread(task, "bStats-Metrics")); + + private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; + + private final String platform; + + private final String serverUuid; + + private final int serviceId; + + private final Consumer appendPlatformDataConsumer; + + private final Consumer appendServiceDataConsumer; + + private final Consumer submitTaskConsumer; + + private final Supplier checkServiceEnabledSupplier; + + private final BiConsumer errorLogger; + + private final Consumer infoLogger; + + private final boolean logErrors; + + private final boolean logSentData; + + private final boolean logResponseStatusText; + + private final Set customCharts = new HashSet<>(); + + private final boolean enabled; + + /** + * Creates a new MetricsBase class instance. + * + * @param platform The platform of the service. + * @param serviceId The id of the service. + * @param serverUuid The server uuid. + * @param enabled Whether or not data sending is enabled. + * @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and + * appends all platform-specific data. + * @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and + * appends all service-specific data. + * @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be + * used to delegate the data collection to a another thread to prevent errors caused by + * concurrency. Can be {@code null}. + * @param checkServiceEnabledSupplier A supplier to check if the service is still enabled. + * @param errorLogger A consumer that accepts log message and an error. + * @param infoLogger A consumer that accepts info log messages. + * @param logErrors Whether or not errors should be logged. + * @param logSentData Whether or not the sent data should be logged. + * @param logResponseStatusText Whether or not the response status text should be logged. + */ + public MetricsBase( + String platform, + String serverUuid, + int serviceId, + boolean enabled, + Consumer appendPlatformDataConsumer, + Consumer appendServiceDataConsumer, + Consumer submitTaskConsumer, + Supplier checkServiceEnabledSupplier, + BiConsumer errorLogger, + Consumer infoLogger, + boolean logErrors, + boolean logSentData, + boolean logResponseStatusText) { + this.platform = platform; + this.serverUuid = serverUuid; + this.serviceId = serviceId; + this.enabled = enabled; + this.appendPlatformDataConsumer = appendPlatformDataConsumer; + this.appendServiceDataConsumer = appendServiceDataConsumer; + this.submitTaskConsumer = submitTaskConsumer; + this.checkServiceEnabledSupplier = checkServiceEnabledSupplier; + this.errorLogger = errorLogger; + this.infoLogger = infoLogger; + this.logErrors = logErrors; + this.logSentData = logSentData; + this.logResponseStatusText = logResponseStatusText; + checkRelocation(); + if (enabled) { + startSubmitting(); + } + } + + public void addCustomChart(CustomChart chart) { + this.customCharts.add(chart); + } + + private void startSubmitting() { + final Runnable submitTask = + () -> { + if (!enabled || !checkServiceEnabledSupplier.get()) { + // Submitting data or service is disabled + scheduler.shutdown(); + return; + } + if (submitTaskConsumer != null) { + submitTaskConsumer.accept(this::submitData); + } else { + this.submitData(); + } + }; + // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution + // of requests on the + // bStats backend. To circumvent this problem, we introduce some randomness into the initial + // and second delay. + // WARNING: You must not modify and part of this Metrics class, including the submit delay or + // frequency! + // WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it! + long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); + long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); + scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); + scheduler.scheduleAtFixedRate( + submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); + } + + private void submitData() { + final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder(); + appendPlatformDataConsumer.accept(baseJsonBuilder); + final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); + appendServiceDataConsumer.accept(serviceJsonBuilder); + JsonObjectBuilder.JsonObject[] chartData = + customCharts.stream() + .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) + .filter(Objects::nonNull) + .toArray(JsonObjectBuilder.JsonObject[]::new); + serviceJsonBuilder.appendField("id", serviceId); + serviceJsonBuilder.appendField("customCharts", chartData); + baseJsonBuilder.appendField("service", serviceJsonBuilder.build()); + baseJsonBuilder.appendField("serverUUID", serverUuid); + baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION); + JsonObjectBuilder.JsonObject data = baseJsonBuilder.build(); + scheduler.execute( + () -> { + try { + // Send the data + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logErrors) { + errorLogger.accept("Could not submit bStats metrics data", e); + } + } + }); + } + + private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { + if (logSentData) { + infoLogger.accept("Sent bStats metrics data: " + data.toString()); + } + String url = String.format(REPORT_URL, platform); + HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(); + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("User-Agent", "Metrics-Service/1"); + connection.setDoOutput(true); + try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { + outputStream.write(compressedData); + } + StringBuilder builder = new StringBuilder(); + try (BufferedReader bufferedReader = + new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + String line; + while ((line = bufferedReader.readLine()) != null) { + builder.append(line); + } + } + if (logResponseStatusText) { + infoLogger.accept("Sent data to bStats and received response: " + builder); + } + } + + /** Checks that the class was properly relocated. */ + private void checkRelocation() { + // You can use the property to disable the check in your test environment + if (System.getProperty("bstats.relocatecheck") == null + || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this little + // "trick" ... :D + final String defaultPackage = + new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); + final String examplePackage = + new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure no one just copy & pastes the example and uses the wrong package + // names + if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) + || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } + } + + /** + * Gzips the given string. + * + * @param str The string to gzip. + * @return The gzipped string. + */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { + gzip.write(str.getBytes(StandardCharsets.UTF_8)); + } + return outputStream.toByteArray(); + } + } + + public static class AdvancedBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class SimpleBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimpleBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + for (Map.Entry entry : map.entrySet()) { + valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()}); + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class MultiLineChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public MultiLineChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class AdvancedPie extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedPie(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public abstract static class CustomChart { + + private final String chartId; + + protected CustomChart(String chartId) { + if (chartId == null) { + throw new IllegalArgumentException("chartId must not be null"); + } + this.chartId = chartId; + } + + public JsonObjectBuilder.JsonObject getRequestJsonObject( + BiConsumer errorLogger, boolean logErrors) { + JsonObjectBuilder builder = new JsonObjectBuilder(); + builder.appendField("chartId", chartId); + try { + JsonObjectBuilder.JsonObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + builder.appendField("data", data); + } catch (Throwable t) { + if (logErrors) { + errorLogger.accept("Failed to get data for custom chart with id " + chartId, t); + } + return null; + } + return builder.build(); + } + + protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception; + } + + public static class SingleLineChart extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SingleLineChart(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + int value = callable.call(); + if (value == 0) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } + + public static class SimplePie extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } + + public static class DrilldownPie extends CustomChart { + + private final Callable>> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public DrilldownPie(String chartId, Callable>> callable) { + super(chartId); + this.callable = callable; + } + + @Override + public JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map> map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean reallyAllSkipped = true; + for (Map.Entry> entryValues : map.entrySet()) { + JsonObjectBuilder valueBuilder = new JsonObjectBuilder(); + boolean allSkipped = true; + for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { + valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue()); + allSkipped = false; + } + if (!allSkipped) { + reallyAllSkipped = false; + valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build()); + } + } + if (reallyAllSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + /** + * An extremely simple JSON builder. + * + *

While this class is neither feature-rich nor the most performant one, it's sufficient enough + * for its use-case. + */ + public static class JsonObjectBuilder { + + private StringBuilder builder = new StringBuilder(); + + private boolean hasAtLeastOneField = false; + + public JsonObjectBuilder() { + builder.append("{"); + } + + /** + * Appends a null field to the JSON. + * + * @param key The key of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendNull(String key) { + appendFieldUnescaped(key, "null"); + return this; + } + + /** + * Appends a string field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, String value) { + if (value == null) { + throw new IllegalArgumentException("JSON value must not be null"); + } + appendFieldUnescaped(key, "\"" + escape(value) + "\""); + return this; + } + + /** + * Appends an integer field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int value) { + appendFieldUnescaped(key, String.valueOf(value)); + return this; + } + + /** + * Appends an object to the JSON. + * + * @param key The key of the field. + * @param object The object. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject object) { + if (object == null) { + throw new IllegalArgumentException("JSON object must not be null"); + } + appendFieldUnescaped(key, object.toString()); + return this; + } + + /** + * Appends a string array to the JSON. + * + * @param key The key of the field. + * @param values The string array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, String[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values) + .map(value -> "\"" + escape(value) + "\"") + .collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends an integer array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends an object array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends a field to the object. + * + * @param key The key of the field. + * @param escapedValue The escaped value of the field. + */ + private void appendFieldUnescaped(String key, String escapedValue) { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + if (key == null) { + throw new IllegalArgumentException("JSON key must not be null"); + } + if (hasAtLeastOneField) { + builder.append(","); + } + builder.append("\"").append(escape(key)).append("\":").append(escapedValue); + hasAtLeastOneField = true; + } + + /** + * Builds the JSON string and invalidates this builder. + * + * @return The built JSON string. + */ + public JsonObject build() { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + JsonObject object = new JsonObject(builder.append("}").toString()); + builder = null; + return object; + } + + /** + * Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt. + * + *

This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'. + * Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n"). + * + * @param value The value to escape. + * @return The escaped value. + */ + private static String escape(String value) { + final StringBuilder builder = new StringBuilder(); + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (c == '"') { + builder.append("\\\""); + } else if (c == '\\') { + builder.append("\\\\"); + } else if (c <= '\u000F') { + builder.append("\\u000").append(Integer.toHexString(c)); + } else if (c <= '\u001F') { + builder.append("\\u00").append(Integer.toHexString(c)); + } else { + builder.append(c); + } + } + return builder.toString(); + } + + /** + * A super simple representation of a JSON object. + * + *

This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not + * allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String, + * JsonObject)}. + */ + public static class JsonObject { + + private final String value; + + private JsonObject(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + } + } +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/utils/ClickManager.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/utils/ClickManager.java new file mode 100644 index 0000000..751086d --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/utils/ClickManager.java @@ -0,0 +1,114 @@ +package com.muhammaddaffa.playerprofiles.utils; + +import com.muhammaddaffa.mdlib.utils.Common; +import com.muhammaddaffa.mdlib.utils.Logger; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import com.muhammaddaffa.playerprofiles.inventory.items.GUIItem; +import com.muhammaddaffa.playerprofiles.manager.customgui.CustomGUI; +import com.muhammaddaffa.playerprofiles.manager.customgui.CustomGUIManager; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; + +public class ClickManager { + + private static final Pattern pattern = Pattern.compile("(?<=\\[CONSOLE\\] |\\[PLAYER\\] |\\[SOUND\\] |\\[MESSAGE\\] |\\[MESSAGEPLAYER\\] |\\[MESSAGETARGET\\] |\\[OPENGUIMENU\\] |\\[CLOSE\\])"); + + public static void handleInventoryClick(GUIItem item, Player player, Player target, InventoryClickEvent event){ + // First of all, cancel the fucking event + event.setCancelled(true); + // If the click type is LEFT, handle the left click + if(event.getClick() == ClickType.LEFT) + handleInventoryLeftClick(item, player, target); + // If the click type is RIGHT, handle the right click + if(event.getClick() == ClickType.RIGHT) + handleInventoryRightClick(item, player, target); + } + + private static void handleInventoryLeftClick(GUIItem item, Player player, Player target){ + // Loop through all left click commands and handle the task + item.leftCommands().forEach(command -> handleTask(command, player, target)); + } + + private static void handleInventoryRightClick(GUIItem item, Player player, Player target){ + // Loop through all right click commands and handle the task + item.rightCommands().forEach(command -> handleTask(command, player, target)); + } + + private static void handleTask(String command, Player player, Player target) { + // Get the array from the splitted pattern + String[] cmds = pattern.split(command); + String tag = cmds[0]; + // Get the list of arguments + List taskList = new ArrayList<>(Arrays.asList(cmds).subList(1, cmds.length)); + String task = cmds.length > 1 ? String.join(" ", taskList) : ""; + // Get the final arguments with parsed placeholder + String finalTask = Utils.tryParsePAPI(task, player, target) + .replace("{player}", player.getName()) + .replace("{target}", target.getName()); + // Check if the tag is CONSOLE + if(tag.equalsIgnoreCase("[CONSOLE] ")){ + // Remove the color so the command could work + finalTask = ChatColor.stripColor(finalTask); + // Execute a command in console + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), finalTask); + return; + } + // Check if the tag is PLAYER + if(tag.equalsIgnoreCase("[PLAYER] ")){ + // Remove the color so the command could work + finalTask = ChatColor.stripColor(finalTask); + // Make the player perform a command + player.performCommand(finalTask); + return; + } + if (tag.equalsIgnoreCase("[MESSAGE] ")) { + // Send the player a message + player.sendMessage(Common.color(finalTask)); + // Send the target a message + target.sendMessage(Common.color(finalTask)); + return; + } + // Check if the tag is MESSAGE + if(tag.equalsIgnoreCase("[MESSAGEPLAYER] ")) { + // Send the player a message + player.sendMessage(Common.color(finalTask)); + return; + } + if (tag.equalsIgnoreCase("[MESSAGETARGET] ")) { + // Send the target a message + target.sendMessage(Common.color(finalTask)); + return; + } + // Check if the tag is OPENGUIMENU + if(tag.equalsIgnoreCase("[OPENGUIMENU] ")){ + finalTask = ChatColor.stripColor(finalTask); + // Get the custom gui manager + CustomGUIManager customGUIManager = PlayerProfiles.getInstance().getCustomGUIManager(); + // Get the custom gui from the task + CustomGUI customGUI = customGUIManager.getByName(finalTask); + // Return if the custom gui is invalid + if(customGUI == null){ + Logger.info("&c" + player.getName() + " trying to open an invalid GUI! (" + finalTask + ")"); + return; + } + // If the custom gui is valid, open the inventory to the player + PlayerProfiles.getInstance().getInventoryManager().openInventory(customGUI, player, target); + // Open a custom menu (coming soon) + return; + } + // Check if the tag is CLOSE + if(tag.equalsIgnoreCase("[CLOSE]")){ + // Close the inventory + player.closeInventory(); + } + } + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/utils/ItemManager.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/utils/ItemManager.java new file mode 100644 index 0000000..4853932 --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/utils/ItemManager.java @@ -0,0 +1,240 @@ +package com.muhammaddaffa.playerprofiles.utils; + +import com.muhammaddaffa.mdlib.fastinv.FastInv; +import com.muhammaddaffa.mdlib.utils.Common; +import com.muhammaddaffa.mdlib.utils.ItemBuilder; +import com.muhammaddaffa.mdlib.xseries.XMaterial; +import com.muhammaddaffa.playerprofiles.inventory.items.GUIItem; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.Optional; +import java.util.UUID; + +public class ItemManager { + + public static ItemStack createItem(GUIItem item, Player player, Player target){ + ItemBuilder builder = null; + // Check if the item material contains ';' + if(item.material().contains(";")){ + // That means this item is a base64 head item + // First, we split the ';' to get the head value + String[] split = item.material().split(";"); + String identifier = split[0]; + // If the identifier is SLOTS, take the player inventory slot as the item + if(identifier.equalsIgnoreCase("SLOTS") || identifier.equalsIgnoreCase("SLOT")){ + int slot = Integer.parseInt(split[1]); + ItemStack stack = target.getInventory().getItem(slot); + if(stack == null){ + return new ItemStack(Material.AIR); + } else { + builder = new ItemBuilder(stack); + } + } + // Head Item + if(identifier.equalsIgnoreCase("HEAD") || identifier.equalsIgnoreCase("HEADS")){ + // Get the head value + String headValue = split[1] + .replace("{player}", player.getName()) + .replace("{target}", target.getName()); + // And now build the ItemStack using ItemBuilder + ItemBuilder base = new ItemBuilder(XMaterial.PLAYER_HEAD.parseItem()) + .name(Utils.tryParsePAPI(item.name(), player, target)) + .lore(Utils.tryParsePAPI(item.lore(), player, target)) + .amount(Math.max(1, item.amount())) + .customModelData(item.customModelData()); + try { + base.skull(headValue); // bisa jadi nama player + } catch (Exception e) { + // fallback ke Steve (atau custom skin base64) + base.skull("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJl..."); + } + builder = base; + } + + } else { + // If the item doesn't contains ';', that means the item is not a head + // First of all, we check if the item is exist or valid + Optional optionalMaterial = XMaterial.matchXMaterial(item.material()); + // We check if the item is not valid + if(!optionalMaterial.isPresent()){ + // If so, instead throwing an error, just return error item + return errorItem(item); + } + // Finally create the item stack using the item builder + builder = new ItemBuilder(optionalMaterial.get().parseItem()) + .name(Utils.tryParsePAPI(item.name(), player, target)) + .lore(Utils.tryParsePAPI(item.lore(), player, target)) + .amount(Math.max(1, item.amount())) + .customModelData(item.customModelData()); + } + // Add hide attributes item flag if it's enabled + if(item.hideAttributes()) builder.flags(ItemFlag.HIDE_ATTRIBUTES); + // Add random enchant and hide enchant attributes if item set to glowing + if(item.glowing()) builder.enchant(Enchantment.UNBREAKING).flags(ItemFlag.HIDE_ENCHANTS); + // Finally build the item stack + ItemStack stack = builder.build(); + // Create ItemMeta + ItemMeta meta = stack.getItemMeta(); + if (meta == null) return null; + // Add the item model if it's not null or empty + String modelItemString = item.itemModel(); + if (isVersionAtLeast(1, 21, 2)) { + if (modelItemString != null) { + String[] parts = modelItemString.split(":", 2); + if (parts.length == 2) { + NamespacedKey modelItem = NamespacedKey.fromString(parts[0] + ":" + parts[1]); + meta.setItemModel(modelItem); + stack.setItemMeta(meta); + } + } + } + return stack; + } + + public static ItemStack createGUIItem(GUIItem item, Player player, Player target){ + if(item.type() == null) + return ItemManager.createItem(item, player, target); + + if(item.type().contains(";")){ + String[] split = item.type().split(";"); + String identifier = split[0]; + // Inventory slot item + if(identifier.equalsIgnoreCase("SLOTS")){ + int slot = Integer.parseInt(split[1]); + ItemStack stack = target.getInventory().getItem(slot); + return stack == null ? new ItemStack(Material.AIR) : stack; + } + // Head Item + if(identifier.equalsIgnoreCase("HEAD") || identifier.equalsIgnoreCase("HEADS")){ + String headValue = split[1] + .replace("{player}", player.getName()) + .replace("{target}", target.getName()); + ItemBuilder builder = new ItemBuilder(XMaterial.PLAYER_HEAD.parseItem()) + .name(Utils.tryParsePAPI(item.name(), player, target)) + .lore(Utils.tryParsePAPI(item.lore(), player, target)) + .amount(Math.max(1, item.amount())) + .customModelData(item.customModelData()) + .skull(headValue); + return builder.build(); + } + } + + switch(item.type()){ + case "HELMET": + return createArmorItem(item, player, target, target.getInventory().getHelmet()); + case "CHESTPLATE": + return createArmorItem(item, player, target, target.getInventory().getChestplate()); + case "LEGGINGS": + return createArmorItem(item, player, target, target.getInventory().getLeggings()); + case "BOOTS": + return createArmorItem(item, player, target, target.getInventory().getBoots()); + case "MAIN_HAND": + return createArmorItem(item, player, target, target.getItemInHand()); + case "OFF_HAND":{ + if(Utils.hasOffHand()){ + return createArmorItem(item, player, target, target.getInventory().getItemInOffHand()); + } + return new ItemStack(Material.AIR); + } + default: + return ItemManager.createItem(item, player, target); + } + } + + public static void fillItem(FastInv inventory, FileConfiguration config){ + if(!config.getBoolean("fillItems.enabled")) return; + // Get the Optional XMaterial + Optional optional = XMaterial.matchXMaterial(config.getString("fillItems.material")); + // If the XMaterial isn't present, just return + if(!optional.isPresent()) return; + // Get the ItemStack if the XMaterial is present + ItemStack stack = optional.get().parseItem(); + // Item builder boiss. + ItemBuilder builder = new ItemBuilder(stack) + .name(Common.color(config.getString("fillItems.name"))) + .lore(Common.color(config.getStringList("fillItems.lore"))) + .customModelData(config.getInt("fillItems.customModelData")); + // Get the final item stack + ItemStack finalStack = builder.build(); + // Loop through all inventory slots + for (int i = 0; i < inventory.getInventory().getSize(); i++) { + // Get the item stack from the slot + ItemStack slotStack = inventory.getInventory().getItem(i); + // Skip if the slot is not null and the type is not material AIR + if(slotStack != null) continue; + // If all going well, we set the slot to the fillter item stack + inventory.setItem(i, finalStack); + } + } + + private static ItemStack createArmorItem(GUIItem item, Player player, Player target, ItemStack stack){ + if(stack == null || stack.getType() == Material.AIR) return createItem(item, player, target); + return stack; + } + + private static ItemStack errorItem(GUIItem item){ + + return new ItemBuilder(XMaterial.BARRIER.parseItem()) + .name("&cInvalid Material!") + .lore("&7Please check your configuration for item '{item}'".replace("{item}", item.name()), " ", "&7Additional Information:", "&7Material: {material}".replace("{material}", item.material())) + .build(); + } + + private static boolean isUuidString(String input) { + if (input == null) return false; + try { + UUID.fromString(input); + return true; + } catch (IllegalArgumentException ex) { + return false; + } + } + + private static boolean isBase64Texture(String input) { + if (input == null) return false; + // Ciri umum base64 textures Mojang: string panjang, karakter base64, sering diawali "eyJ0ZXh0dXJlcy" + if (input.length() < 40) return false; + if (input.startsWith("eyJ0ZXh0dXJlcy")) return true; + // Kalau kamu simpan URL textures langsung, bisa tambahkan cek "http://textures.minecraft.net" + if (input.startsWith("http://textures.minecraft.net") || input.startsWith("https://textures.minecraft.net")) { + return true; + } + // Cek karakter base64 dasar + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + boolean ok = (c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z') + || (c >= '0' && c <= '9') + || c == '+' || c == '/' || c == '=' || c == '-' || c == '_'; + if (!ok) return false; + } + return true; + } + + public static boolean isVersionAtLeast(int major, int minor, int patch) { + String version = Bukkit.getBukkitVersion().split("-")[0]; + String[] parts = version.split("\\."); + + try { + int maj = Integer.parseInt(parts[0]); + int min = Integer.parseInt(parts[1]); + int pat = parts.length > 2 ? Integer.parseInt(parts[2]) : 0; + + if (maj != major) return maj > major; + if (min != minor) return min > minor; + return pat >= patch; + } catch (NumberFormatException e) { + return false; // fallback + } + } + + +} diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/utils/Utils.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/utils/Utils.java new file mode 100644 index 0000000..a85c18d --- /dev/null +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/utils/Utils.java @@ -0,0 +1,91 @@ +package com.muhammaddaffa.playerprofiles.utils; + +import com.muhammaddaffa.mdlib.utils.Common; +import com.muhammaddaffa.mdlib.xseries.XSound; +import com.muhammaddaffa.playerprofiles.PlayerProfiles; +import com.muhammaddaffa.playerprofiles.manager.profile.ProfileManager; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public class Utils { + + public static boolean hasCustomModelData(){ + return Bukkit.getVersion().contains("1.14") || + Bukkit.getVersion().contains("1.15") || + Bukkit.getVersion().contains("1.16") || + Bukkit.getVersion().contains("1.17") || + Bukkit.getVersion().contains("1.18") || + Bukkit.getVersion().contains("1.19") || + Bukkit.getVersion().contains("1.20") || + Bukkit.getVersion().contains("1.21") || + Bukkit.getVersion().contains("1.22"); + } + + public static String tryParsePAPI(@NotNull String message, Player player, Player target){ + // Get the profile manager + ProfileManager profileManager = PlayerProfiles.getInstance().getProfileManager(); + // Player profile status + String playerStatus = profileManager.isProfileLocked(player) ? "&cLocked" : "&aUnlocked"; + // Target profile status + String targetStatus = profileManager.isProfileLocked(target) ? "&cLocked" : "&aUnlocked"; + // Finally, return the value with parsed player and target + return Common.papi(target, message) + .replace("{player}", player.getName()) + .replace("{target}", target.getName()) + .replace("{player_status}", playerStatus) + .replace("{target_status}", targetStatus) + .replace("{player_health}", player.getHealth() + "") + .replace("{target_health}", target.getHealth() + "") + .replace("{player_exp}", player.getExp() + "") + .replace("{target_exp}", target.getExp() + "") + .replace("{player_level}", player.getLevel() + "") + .replace("{target_level}", target.getLevel() + "") + .replace("{player_uuid}", player.getUniqueId().toString()) + .replace("{target_uuid}", target.getUniqueId().toString()) + .replace("{player_world}", player.getWorld().getName()) + .replace("{target_world}", target.getWorld().getName()); + } + + public static List tryParsePAPI(@NotNull List messages, Player player, Player target){ + return messages.stream().map(message -> tryParsePAPI(message, player, target)).collect(Collectors.toList()); + } + + public static void playSound(Player player, String soundPath){ + // Get the config.yml + FileConfiguration config = PlayerProfiles.CONFIG_DEFAULT.getConfig(); + // Get the path to the sound + String path = "sounds." + soundPath; + // Return if the sound isn't enabled + if(!config.getBoolean(path + ".enabled")) + return; + // Get the 1.8 - 1.17 sound support optional + Optional optional = XSound.matchXSound(config.getString(path + ".sound")); + // If the sound is invalid, return + if(!optional.isPresent()) + return; + // Get the player location + Location location = player.getLocation(); + // Get the sound from the optional XSound + Sound sound = optional.get().parseSound(); + // Get the volume + float volume = (float) config.getDouble(path + ".volume"); + // Get the pitch + float pitch = (float) config.getDouble(path + ".pitch"); + // Finally play the sound to the player + player.playSound(location, sound, volume, pitch); + } + + public static boolean hasOffHand() { + return !Bukkit.getVersion().contains("1.7") || + !Bukkit.getVersion().contains("1.8"); + } + +} diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml new file mode 100644 index 0000000..41e20f3 --- /dev/null +++ b/core/src/main/resources/config.yml @@ -0,0 +1,115 @@ +# Configuration Explanation: +# (options) +# (disableNPC) Should we disable viewing the NPC's profile? +# (disableInCombat) +# (enabled) Should we prevent player from opening profile while in combat? +# (message) The message that will be sent if player trying to open profile while in combat +# (shiftClick) Should player open profile by shift clicking? +# If disabled, right click will open player profile +options: + disableNPC: true #Disable player from viewing NPC profile. + disableInCombat: + enabled: true + message: "{prefix} &cYou are not allowed to open profile while in combat!" + shiftClick: true + +# This feature is to auto update the placeholder on every item +# Configuration Explanation: +# (autoRefresh) +# (enabled) Should we enabled this feature? +# (refreshEvery) How often should we update the item? (Time is in ticks - 20 ticks = 1 second) +autoRefresh: + enabled: true + refreshEvery: 20 + +# This feature is to give player cooldown to open profiles +# (cooldown) +# (enabled) Should we enable this feature? +# (duration) How long is the cooldown? (Time in second) +# (message) Message that will be sent if player trying to open profile while on cooldown +cooldown: + enabled: true + duration: 3 #in seconds + message: "{prefix} &cPlease wait for another {time} second(s)!" + +# Aliases for commands in this plugin +# Configuration Explanation: +# (commandAliases) +# (playerProfiles) Aliases for /playerprofiles command (main command) +# (profile) Aliases for /profile command +# (lockProfile) Aliases for /lockprofile command +# (unlockProfile) Aliases for /unlockprofile command +commandAliases: + playerProfiles: + - 'playerprofile' + - 'playerp' + - 'pp' + profile: + - 'viewprofile' + - 'p' + lockProfile: + - 'profilelock' + - 'lock' + unlockProfile: + - 'profileunlock' + - 'unlock' + +# If the world name is in the list and players location is inside +# the world that are on the list, player are unable to open any player profiles +# Note: The world name is case sensitive +disabledWorlds: + message: "{prefix} &cYou are not allowed to open profile in this world!" + worlds: + - 'pvpWorld' + - 'testWorld' + +# If the region name is in this list, player are unable to +# open other's player profile with condition if the player/others location +# is inside the disable regions (the region name is case-sensitive) +disabledRegions: + playerInDisabledRegionMessage: "{prefix} &cYou are not allowed to open profile in this region!" + targetInDisabledRegionMessage: "{prefix} &cThe target is in disabled region area!" + regions: + - 'disabledRegions' + - 'pvp' + +# This feature is to check between player1 (the player) and player2 (the target) location distance +# if the distance is greater than the configured distance - player1 will close the profile inventory +# Configuration Explanation: +# (distanceCheck) +# (enabled) Should we enabled this feature? +# (distance) The maximum distance (number is in blocks) +# (tooFarMessage) Message if the distance is greater than the configured distance +distanceCheck: + enabled: true + distance: 30 + tooFarMessage: "{prefix} &e{player} &cis too far from you!" + +sounds: + onProfileOpen: + enabled: true + sound: BLOCK_LAVA_POP + volume: 1 + pitch: 1 + +messages: + prefix: '&8[&6Profile&8]' + noPermission: "{prefix} &cYou are lacking permission '{permission}'" + reload: "{prefix} &aYou have reloaded the configuration!" + invalidPlayer: "{prefix} &cThe player '{player}' is not online!" + targetProfileLocked: "{prefix} &cThe target profile is locked!" + lockProfile: "{prefix} &7You have &clocked &7your profile." + lockProfileOthers: "{prefix} &7You have &clocked &7{player}'s profile." + unlockProfile: "{prefix} &7You have &aunlocked &7your profile." + unlockProfileOthers: "{prefix} &7You have &aunlocked &7{player}'s profile." + invalidGUIName: "{prefix} &cThere is no GUI with name '{gui}' &7(See /playerprofiles listgui)" + listGUI: "{prefix} &7List GUI: &a{gui}" + help: + - "&6&lPlayerProfiles &7- by aglerr" + - "&6- /playerprofiles help &7- shows the help messages" + - "&6- /playerprofiles reload &7- reload the configuration" + - "&6- /playerprofiles opengui (player) (target) (gui-name) &7- open custom gui" + - "&6- /playerprofiles listgui &7- see the list of custom gui names" + - "&6- /lockprofile [player] &7- lock your own/others profile" + - "&6- /unlockprofile [player] &7- unlock your own/others profile" + - "&6- /profile [player] &7- see your own/others profile" \ No newline at end of file diff --git a/core/src/main/resources/custom-gui/punish-gui.yml b/core/src/main/resources/custom-gui/punish-gui.yml new file mode 100644 index 0000000..aadc812 --- /dev/null +++ b/core/src/main/resources/custom-gui/punish-gui.yml @@ -0,0 +1,156 @@ +title: "&8Punish (&a{target}&8)" +size: 27 + +fillItems: + enabled: true + material: BLACK_STAINED_GLASS_PANE + name: "&f" + lore: [] + +items: + targetInformation: + material: head;{target} + name: "&6{target}'s Information" + slots: [ 4 ] + glowing: false + hideAttributes: false + customModelData: 0 + onlyVisitor: false + onlyOwner: false + usePermission: false + permission: "custom.permission" + lore: + - "" + - " * &7Profile Status: {target_status}" + - " * &7Health: &c{target_health}" + - " * &7Level: &6{target_level}" + - " * &7Experience: &6{target_exp}" + - " * &7World: &6{target_world}" + - "" + - "&bMore with PlaceholderAPI" + - " * &7Is Flying: &6%player_is_flying%" + - " * &7Is Sneaking: &6%player_is_sneaking%" + - " * &7Is Sprinting: &6%player_is_sprinting%" + - " * &7Is OP: &6%player_is_op%" + - " * &7Ping: &a%player_ping%ms" + leftClickCommands: [] + rightClickCommands: [] + kickPlayer: + material: GOLDEN_BOOTS + name: "&6Kick {target}" + slots: [ 11 ] + glowing: false + hideAttributes: true + customModelData: 0 + onlyVisitor: false + onlyOwner: false + usePermission: true + permission: "playerprofiles.kick" + lore: + - "&7Click to kick {target}" + leftClickCommands: + - "[CONSOLE] kick {target}" + - "[MESSAGE] &8[&6Profile&8] &aYou have kicked &e{target}" + - "[CLOSE]" + rightClickCommands: + - "[CONSOLE] kick {target}" + - "[MESSAGE] &8[&6Profile&8] &aYou have kicked &e{target}" + - "[CLOSE]" + banPlayer: + material: DIAMOND_AXE + name: "&6Ban {target}" + slots: [ 12 ] + glowing: false + hideAttributes: true + customModelData: 0 + onlyVisitor: false + onlyOwner: false + usePermission: true + permission: "playerprofiles.ban" + lore: + - "&7Click to ban {target}" + leftClickCommands: + - "[CONSOLE] ban {target}" + - "[MESSAGE] &8[&6Profile&8] &aYou have banned &e{target}" + - "[CLOSE]" + rightClickCommands: + - "[CONSOLE] ban {target}" + - "[MESSAGE] &8[&6Profile&8] &aYou have banned &e{target}" + - "[CLOSE]" + mutePlayer: + material: RED_CARPET + name: "&6Mute {target} for 5 minutes" + slots: [ 13 ] + glowing: false + hideAttributes: false + customModelData: 0 + onlyVisitor: false + onlyOwner: false + usePermission: true + permission: "playerprofiles.mute" + lore: + - "&7Click to mute {target} for 5 minutes!" + leftClickCommands: + - "[CONSOLE] mute {target} 5m" + - "[MESSAGE] &8[&6Profile&8] &aYou have muted &e{target} &afor 5 minutes!" + - "[CLOSE]" + rightClickCommands: + - "[CONSOLE] mute {target} 5m" + - "[MESSAGE] &8[&6Profile&8] &aYou have muted &e{target} &afor 5 minutes!" + - "[CLOSE]" + killPlayer: + material: DIAMOND_SWORD + name: "&6Kill {target}" + slots: [ 14 ] + glowing: false + hideAttributes: false + customModelData: 0 + onlyVisitor: false + onlyOwner: false + usePermission: true + permission: "playerprofiles.kill" + lore: + - "&7Click to kill {target}" + leftClickCommands: + - "[CONSOLE] kill {target}" + - "[MESSAGE] &8[&6Profile&8] &aYou have killed &e{target}" + - "[CLOSE]" + rightClickCommands: + - "[CONSOLE] kill {target}" + - "[MESSAGE] &8[&6Profile&8] &aYou have killed &e{target}" + - "[CLOSE]" + clearInventory: + material: CHEST + name: "&6Clear {target} inventory" + slots: [ 15 ] + glowing: false + hideAttributes: false + customModelData: 0 + onlyVisitor: false + onlyOwner: false + usePermission: true + permission: "playerprofiles.clearinventory" + lore: + - "&7Click to clear {target} inventory" + leftClickCommands: + - "[CONSOLE] clear {target}" + - "[MESSAGE] &8[&6Profile&8] &aYou have cleared &e{target} &ainventory" + - "[CLOSE]" + rightClickCommands: + - "[CONSOLE] clear {target}" + - "[MESSAGE] &8[&6Profile&8] &aYou have cleared &e{target} &ainventory" + - "[CLOSE]" + backButton: + material: head;eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWMwNjQ2YTI4YjQ1MWRhNzQzOTRlNjk4YjA0ZmFjOTM1YmExOTc1ZjQyODI5MDY3YTBmYmZlZDE4MWEzNjU5NCJ9fX0= + name: "&cBack" + slots: [ 18 ] + glowing: false + hideAttributes: false + customModelData: 0 + onlyVisitor: false + onlyOwner: false + usePermission: false + permission: "custom.permission" + lore: [] + leftClickCommands: + - "[CONSOLE] playerprofiles openprofile {target} {player}" diff --git a/core/src/main/resources/data.yml b/core/src/main/resources/data.yml new file mode 100644 index 0000000..aea0b69 --- /dev/null +++ b/core/src/main/resources/data.yml @@ -0,0 +1 @@ +# Do not touch! \ No newline at end of file diff --git a/core/src/main/resources/gui-creator.yml b/core/src/main/resources/gui-creator.yml new file mode 100644 index 0000000..477dae0 --- /dev/null +++ b/core/src/main/resources/gui-creator.yml @@ -0,0 +1,5 @@ +menu: + punish-gui: + fileName: "punish-gui.yml" + view-gui: + fileName: "view-gui.yml" \ No newline at end of file diff --git a/core/src/main/resources/gui.yml b/core/src/main/resources/gui.yml new file mode 100644 index 0000000..402f456 --- /dev/null +++ b/core/src/main/resources/gui.yml @@ -0,0 +1,183 @@ +title: "&8{target}'s Profiles" +size: 54 + +fillItems: + enabled: true + material: BLACK_STAINED_GLASS_PANE + name: "&f" + lore: [] + +items: + tradeTarget: + material: EMERALD + name: "&eTrade {target}" + slots: [ 24 ] + glowing: false + hideAttributes: false + customModelData: 0 + onlyOwner: false + onlyVisitor: false + usePermission: false + permission: "custom.permission" + lore: + - "&7Click to trade with &e{target}" + leftClickCommands: + - "[PLAYER] trade {target}" + - "[MESSAGE] &7Trade request has been sent to &e{target}" + - "[CLOSE]" + rightClickCommands: + - "[PLAYER] trade {target}" + - "[MESSAGE] &7Trade request has been sent to &e{target}" + - "[CLOSE]" + customMenu: + material: DIAMOND_AXE + name: "&ePunish {target}" + slots: [ 23 ] + glowing: false + hideAttributes: true + customModelData: 0 + onlyOwner: false + onlyVisitor: false + usePermission: true + permission: "playerprofiles.punishgui" + lore: + - "&7This item can only be seen by the" + - "&7profile visitors with permission" + - "&7'playerprofiles.punishgui'" + - "" + - "&aClick to punish {target}" + leftClickCommands: + - "[OPENGUIMENU] punish-gui.yml" + rightClickCommands: + - "[OPENGUIMENU] punish-gui.yml" + exampleHelmet: + type: HELMET + material: RED_STAINED_GLASS_PANE + name: "&eHelmet" + slots: [10] + glowing: true + hideAttributes: false + customModelData: 0 + onlyOwner: false + onlyVisitor: false + usePermission: false + permission: "custom.permission" + lore: + - "&cEmpty!" + exampleChestplate: + type: CHESTPLATE + material: RED_STAINED_GLASS_PANE + name: "&eChestplate" + slots: [ 19 ] + glowing: true + hideAttributes: false + customModelData: 0 + onlyOwner: false + onlyVisitor: false + usePermission: false + permission: "custom.permission" + lore: + - "&cEmpty!" + exampleLeggings: + type: LEGGINGS + material: RED_STAINED_GLASS_PANE + name: "&eLeggings" + slots: [ 28 ] + glowing: true + hideAttributes: false + customModelData: 0 + onlyOwner: false + onlyVisitor: false + usePermission: false + permission: "custom.permission" + lore: + - "&cEmpty!" + exampleBoots: + type: BOOTS + material: RED_STAINED_GLASS_PANE + name: "&eBoots" + slots: [ 37 ] + glowing: true + hideAttributes: false + customModelData: 0 + onlyOwner: false + onlyVisitor: false + usePermission: false + permission: "custom.permission" + lore: + - "&cEmpty!" + exampleMainHand: + type: MAIN_HAND + material: RED_STAINED_GLASS_PANE + name: "&eMain Hand" + slots: [ 20 ] + glowing: true + hideAttributes: false + customModelData: 0 + onlyOwner: false + onlyVisitor: false + usePermission: false + permission: "custom.permission" + lore: + - "&cEmpty!" + exampleOffhand: + type: OFF_HAND + material: RED_STAINED_GLASS_PANE + name: "&eOff Hand" + slots: [ 29 ] + glowing: true + hideAttributes: false + customModelData: 0 + onlyOwner: false + onlyVisitor: false + usePermission: false + permission: "custom.permission" + lore: + - "&cEmpty!" + unlockProfile: + material: head;eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjA3ZmJjMzM5ZmYyNDFhYzNkNjYxOWJjYjY4MjUzZGZjM2M5ODc4MmJhZjNmMWY0ZWZkYjk1NGY5YzI2In19fQ== + name: "&aUnlock Profile" + slots: [ 53 ] + glowing: true + hideAttributes: false + customModelData: 0 + onlyOwner: true + onlyVisitor: false + usePermission: false + permission: "custom.permission" + lore: + - "&7Click to unlock your profile" + leftClickCommands: + - "[CONSOLE] unlockprofile {target}" + rightClickCommands: + - "[CONSOLE] unlockprofile {target}" + profileStatus: + material: CHEST + name: "&6Profile Status" + slots: [ 52 ] + glowing: false + hideAttributes: false + customModelData: 0 + onlyOwner: true + onlyVisitor: false + usePermission: false + permission: "custom.permission" + lore: + - "{target_status}" + lockProfile: + material: head;eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzE5ZjUwYjQzMmQ4NjhhZTM1OGUxNmY2MmVjMjZmMzU0MzdhZWI5NDkyYmNlMTM1NmM5YWE2YmIxOWEzODYifX19 + name: "&cLock Profile" + slots: [ 51 ] + glowing: true + hideAttributes: false + customModelData: 0 + onlyOwner: true + onlyVisitor: false + usePermission: false + permission: "custom.permission" + lore: + - "&7Click to lock your profile" + leftClickCommands: + - "[CONSOLE] lockprofile {target}" + rightClickCommands: + - "[CONSOLE] lockprofile {target}" diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml new file mode 100644 index 0000000..2216f36 --- /dev/null +++ b/core/src/main/resources/plugin.yml @@ -0,0 +1,19 @@ +name: PlayerProfiles +version: 8.0.3 +main: com.muhammaddaffa.playerprofiles.PlayerProfiles +author: aglerr, Starfruit2210 +description: Plugin to show player profiles +api-version: 1.13 +softdepend: [CombatLogX, DeluxeCombat, PlaceholderAPI, WorldGuard, WorldEdit] + +commands: + playerprofiles: + aliases: [] + profile: + aliases: [] + unlockProfile: + aliases: [] + lockProfile: + aliases: [] + toggleprofile: + aliases: [] \ No newline at end of file diff --git a/dist/pom.xml b/dist/pom.xml new file mode 100644 index 0000000..42bfb9b --- /dev/null +++ b/dist/pom.xml @@ -0,0 +1,75 @@ + + + 4.0.0 + + com.muhammaddaffa + PlayerProfiles + 8.0.4 + + + dist + + + 21 + 21 + UTF-8 + + + + ../target + PlayerProfiles-${project.version} + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.0 + + + package + + shade + + + + + ${project.parent.groupId}:* + + + + + + + + + + + + ${project.groupId} + core + ${project.version} + + + ${project.groupId} + api + ${project.version} + + + ${project.groupId} + worldguard6 + ${project.version} + + + ${project.groupId} + worldguard7 + ${project.version} + + + ${project.groupId} + worldguard-wrapper + ${project.version} + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..dc0274c --- /dev/null +++ b/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + com.muhammaddaffa + PlayerProfiles + pom + 8.0.4 + + + 21 + 21 + + + + dist + core + api + worldguard-wrapper + worldguard6 + worldguard7 + + + \ No newline at end of file diff --git a/worldguard-wrapper.iml b/worldguard-wrapper.iml new file mode 100644 index 0000000..fa63d4b --- /dev/null +++ b/worldguard-wrapper.iml @@ -0,0 +1,12 @@ + + + + + + + SPIGOT + + + + + \ No newline at end of file diff --git a/worldguard-wrapper/pom.xml b/worldguard-wrapper/pom.xml new file mode 100644 index 0000000..f08a5c0 --- /dev/null +++ b/worldguard-wrapper/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + com.muhammaddaffa + PlayerProfiles + 8.0.4 + + + worldguard-wrapper + + + 21 + 21 + UTF-8 + + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + + + org.spigotmc + spigot-api + 1.20.1-R0.1-SNAPSHOT + provided + + + ${project.groupId} + worldguard6 + ${project.version} + + + ${project.groupId} + worldguard6 + ${project.version} + + + ${project.groupId} + worldguard7 + ${project.version} + + + + \ No newline at end of file diff --git a/worldguard-wrapper/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/WorldGuardWrapper.java b/worldguard-wrapper/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/WorldGuardWrapper.java new file mode 100644 index 0000000..42517fc --- /dev/null +++ b/worldguard-wrapper/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/WorldGuardWrapper.java @@ -0,0 +1,32 @@ +package com.muhammaddaffa.playerprofiles.worldguardwrapper; + +import com.muhammaddaffa.api.IRegionFinder; +import com.muhammaddaffa.playerprofiles.worldguardwrapper.wg6.RegionFinder6; +import com.muhammaddaffa.playerprofiles.worldguardwrapper.wg7.RegionFinder7; + +public class WorldGuardWrapper { + + private static final WorldGuardWrapper instance = new WorldGuardWrapper(); + + public static WorldGuardWrapper getInstance(){ + return instance; + } + + private final IRegionFinder regionFinder; + + private WorldGuardWrapper(){ + IRegionFinder selected; + try{ + Class.forName("com.sk89q.worldguard.WorldGuard"); + selected = new RegionFinder7(); + } catch (ClassNotFoundException ex){ + selected = new RegionFinder6(); + } + regionFinder = selected; + } + + public IRegionFinder getRegionFinder() { + return regionFinder; + } + +} diff --git a/worldguard-wrapper/worldguard-wrapper.iml b/worldguard-wrapper/worldguard-wrapper.iml new file mode 100644 index 0000000..a589521 --- /dev/null +++ b/worldguard-wrapper/worldguard-wrapper.iml @@ -0,0 +1,13 @@ + + + + + + + SPIGOT + + 1 + + + + \ No newline at end of file diff --git a/worldguard6.iml b/worldguard6.iml new file mode 100644 index 0000000..e8e1a99 --- /dev/null +++ b/worldguard6.iml @@ -0,0 +1,13 @@ + + + + + + + SPIGOT + BUKKIT + + + + + \ No newline at end of file diff --git a/worldguard6/pom.xml b/worldguard6/pom.xml new file mode 100644 index 0000000..f55427d --- /dev/null +++ b/worldguard6/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + com.muhammaddaffa + PlayerProfiles + 8.0.4 + + + worldguard6 + + + 21 + 21 + UTF-8 + + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + sk89q-repo + https://maven.enginehub.org/repo/ + + + + + + + org.spigotmc + spigot-api + 1.17-R0.1-SNAPSHOT + provided + + + com.sk89q.worldguard + worldguard-legacy + 6.2 + provided + + + com.sk89q.worldedit + worldedit-bukkit + 6.1 + provided + + + ${project.groupId} + api + ${project.version} + + + + \ No newline at end of file diff --git a/worldguard6/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg6/RegionFinder6.java b/worldguard6/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg6/RegionFinder6.java new file mode 100644 index 0000000..27c9048 --- /dev/null +++ b/worldguard6/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg6/RegionFinder6.java @@ -0,0 +1,28 @@ +package com.muhammaddaffa.playerprofiles.worldguardwrapper.wg6; + +import com.muhammaddaffa.api.IRegionFinder; +import com.sk89q.worldguard.bukkit.WGBukkit; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import org.bukkit.Location; + +import java.util.ArrayList; +import java.util.List; + +public class RegionFinder6 implements IRegionFinder { + + @Override + public List getRegions(Location location) { + // Get the ApplicableRegionSet from the location + com.sk89q.worldguard.protection.ApplicableRegionSet ars = WGBukkit.getPlugin().getRegionContainer().createQuery().getApplicableRegions(location); + // Create an empty array list of string + List regions = new ArrayList<>(); + // Loop through all regions in the location + for(ProtectedRegion region : ars){ + // Add the region name to the list + regions.add(region.getId()); + } + // Finally, return the list of regions name + return regions; + } + +} \ No newline at end of file diff --git a/worldguard6/worldguard6.iml b/worldguard6/worldguard6.iml new file mode 100644 index 0000000..a589521 --- /dev/null +++ b/worldguard6/worldguard6.iml @@ -0,0 +1,13 @@ + + + + + + + SPIGOT + + 1 + + + + \ No newline at end of file diff --git a/worldguard7.iml b/worldguard7.iml new file mode 100644 index 0000000..fa63d4b --- /dev/null +++ b/worldguard7.iml @@ -0,0 +1,12 @@ + + + + + + + SPIGOT + + + + + \ No newline at end of file diff --git a/worldguard7/pom.xml b/worldguard7/pom.xml new file mode 100644 index 0000000..bcf10c5 --- /dev/null +++ b/worldguard7/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + com.muhammaddaffa + PlayerProfiles + 8.0.4 + + + worldguard7 + + + 21 + 21 + UTF-8 + + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + sk89q-repo + https://maven.enginehub.org/repo/ + + + + + + + org.spigotmc + spigot-api + 1.17-R0.1-SNAPSHOT + provided + + + com.sk89q.worldguard + worldguard-core + 7.0.0-SNAPSHOT + provided + + + com.sk89q.worldedit + worldedit-bukkit + 7.3.0-SNAPSHOT + provided + + + ${project.groupId} + api + ${project.version} + + + + \ No newline at end of file diff --git a/worldguard7/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg7/RegionFinder7.java b/worldguard7/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg7/RegionFinder7.java new file mode 100644 index 0000000..5b70e0a --- /dev/null +++ b/worldguard7/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg7/RegionFinder7.java @@ -0,0 +1,35 @@ +package com.muhammaddaffa.playerprofiles.worldguardwrapper.wg7; + +import com.muhammaddaffa.api.IRegionFinder; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import org.bukkit.Location; + +import java.util.ArrayList; +import java.util.List; + +public class RegionFinder7 implements IRegionFinder { + + @Override + public List getRegions(Location location) { + // Get the location util using BukkitAdapter + com.sk89q.worldedit.util.Location loc = BukkitAdapter.adapt(location); + // Get the RegionContainer + com.sk89q.worldguard.protection.regions.RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer(); + // Get the RegionQuery + com.sk89q.worldguard.protection.regions.RegionQuery query = container.createQuery(); + // Get the ApplicableRegionSet + com.sk89q.worldguard.protection.ApplicableRegionSet ars = query.getApplicableRegions(loc); + // Create an empty list of string + List regions = new ArrayList<>(); + // Loop through all regions in the location + for(ProtectedRegion region : ars){ + // Add the region name/id to the regions list + regions.add(region.getId()); + } + // Finally, return the list of regions name + return regions; + } + +} \ No newline at end of file diff --git a/worldguard7/worldguard7.iml b/worldguard7/worldguard7.iml new file mode 100644 index 0000000..a589521 --- /dev/null +++ b/worldguard7/worldguard7.iml @@ -0,0 +1,13 @@ + + + + + + + SPIGOT + + 1 + + + + \ No newline at end of file From 6845ba9f8f8e3fb7b1f7862fe47171c631d862ec Mon Sep 17 00:00:00 2001 From: evnrca Date: Thu, 27 Aug 2026 00:44:56 +0800 Subject: [PATCH 2/3] Full support for Modern Minecraft Paper servers 1.21.11+ - Updated to Java 21 across all modules - Upgraded Spigot API to 1.21.11-R0.1-SNAPSHOT - Updated WorldGuard to 7.0.10 and WorldEdit to 7.3.12 - Removed deprecated worldguard6 module (legacy WorldGuard 6.x) - Simplified WorldGuardWrapper to single RegionFinder7 implementation - Fixed DependencyManager missing braces for WG version detection - Updated plugin.yml api-version to 1.21 - Modernized RegionFinder7 for WorldGuard 7 API - Bumped version to 8.0.5 --- api/pom.xml | 6 +- core/pom.xml | 6 +- .../manager/DependencyManager.java | 6 +- core/src/main/resources/plugin.yml | 4 +- dist/pom.xml | 9 +-- pom.xml | 7 ++- worldguard-wrapper/pom.xml | 20 ++----- .../worldguardwrapper/WorldGuardWrapper.java | 14 +---- worldguard6.iml | 13 ---- worldguard6/pom.xml | 59 ------------------- .../worldguardwrapper/wg6/RegionFinder6.java | 28 --------- worldguard6/worldguard6.iml | 13 ---- worldguard7/pom.xml | 12 ++-- .../worldguardwrapper/wg7/RegionFinder7.java | 12 +--- 14 files changed, 31 insertions(+), 178 deletions(-) delete mode 100644 worldguard6.iml delete mode 100644 worldguard6/pom.xml delete mode 100644 worldguard6/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg6/RegionFinder6.java delete mode 100644 worldguard6/worldguard6.iml diff --git a/api/pom.xml b/api/pom.xml index 83b5fea..ce5de65 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -6,14 +6,12 @@ com.muhammaddaffa PlayerProfiles - 8.0.4 + 8.0.5 api - 17 - 17 UTF-8 @@ -30,7 +28,7 @@ org.spigotmc spigot-api - 1.20.1-R0.1-SNAPSHOT + ${spigot.version} provided diff --git a/core/pom.xml b/core/pom.xml index e10343c..c62fd86 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,14 +6,12 @@ com.muhammaddaffa PlayerProfiles - 8.0.4 + 8.0.5 core - 21 - 21 UTF-8 @@ -71,7 +69,7 @@ org.spigotmc spigot-api - 1.21.5-R0.1-SNAPSHOT + ${spigot.version} provided diff --git a/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/DependencyManager.java b/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/DependencyManager.java index a879ec1..c23f0a1 100644 --- a/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/DependencyManager.java +++ b/core/src/main/java/com/muhammaddaffa/playerprofiles/manager/DependencyManager.java @@ -32,13 +32,15 @@ public static void checkDependency(){ if(WORLD_GUARD){ Plugin plugin = pm.getPlugin("WorldGuard"); - if(plugin.getDescription().getVersion().startsWith("6")) + if(plugin.getDescription().getVersion().startsWith("6")) { Logger.info("&rFound WorldGuard! Using WorldGuard API version 6"); WORLD_GUARD_VERSION = 6; + } - if(plugin.getDescription().getVersion().startsWith("7")) + if(plugin.getDescription().getVersion().startsWith("7")) { Logger.info("&rFound WorldGuard! Using WorldGuard API version 7"); WORLD_GUARD_VERSION = 7; + } } } diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml index 2216f36..35693a3 100644 --- a/core/src/main/resources/plugin.yml +++ b/core/src/main/resources/plugin.yml @@ -1,9 +1,9 @@ name: PlayerProfiles -version: 8.0.3 +version: 8.0.5 main: com.muhammaddaffa.playerprofiles.PlayerProfiles author: aglerr, Starfruit2210 description: Plugin to show player profiles -api-version: 1.13 +api-version: 1.21 softdepend: [CombatLogX, DeluxeCombat, PlaceholderAPI, WorldGuard, WorldEdit] commands: diff --git a/dist/pom.xml b/dist/pom.xml index 42bfb9b..1e3c9e8 100644 --- a/dist/pom.xml +++ b/dist/pom.xml @@ -6,14 +6,12 @@ com.muhammaddaffa PlayerProfiles - 8.0.4 + 8.0.5 dist - 21 - 21 UTF-8 @@ -55,11 +53,6 @@ api ${project.version} - - ${project.groupId} - worldguard6 - ${project.version} - ${project.groupId} worldguard7 diff --git a/pom.xml b/pom.xml index dc0274c..5f9e365 100644 --- a/pom.xml +++ b/pom.xml @@ -7,11 +7,15 @@ com.muhammaddaffa PlayerProfiles pom - 8.0.4 + 8.0.5 21 21 + UTF-8 + 1.21.11-R0.1-SNAPSHOT + 7.0.10 + 7.3.12 @@ -19,7 +23,6 @@ core api worldguard-wrapper - worldguard6 worldguard7 diff --git a/worldguard-wrapper/pom.xml b/worldguard-wrapper/pom.xml index f08a5c0..2c3f87c 100644 --- a/worldguard-wrapper/pom.xml +++ b/worldguard-wrapper/pom.xml @@ -6,14 +6,12 @@ com.muhammaddaffa PlayerProfiles - 8.0.4 + 8.0.5 worldguard-wrapper - 21 - 21 UTF-8 @@ -23,6 +21,10 @@ spigot-repo https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + sk89q-repo + https://maven.enginehub.org/repo/ + @@ -30,19 +32,9 @@ org.spigotmc spigot-api - 1.20.1-R0.1-SNAPSHOT + ${spigot.version} provided - - ${project.groupId} - worldguard6 - ${project.version} - - - ${project.groupId} - worldguard6 - ${project.version} - ${project.groupId} worldguard7 diff --git a/worldguard-wrapper/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/WorldGuardWrapper.java b/worldguard-wrapper/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/WorldGuardWrapper.java index 42517fc..81f03f3 100644 --- a/worldguard-wrapper/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/WorldGuardWrapper.java +++ b/worldguard-wrapper/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/WorldGuardWrapper.java @@ -1,7 +1,6 @@ package com.muhammaddaffa.playerprofiles.worldguardwrapper; import com.muhammaddaffa.api.IRegionFinder; -import com.muhammaddaffa.playerprofiles.worldguardwrapper.wg6.RegionFinder6; import com.muhammaddaffa.playerprofiles.worldguardwrapper.wg7.RegionFinder7; public class WorldGuardWrapper { @@ -12,18 +11,7 @@ public static WorldGuardWrapper getInstance(){ return instance; } - private final IRegionFinder regionFinder; - - private WorldGuardWrapper(){ - IRegionFinder selected; - try{ - Class.forName("com.sk89q.worldguard.WorldGuard"); - selected = new RegionFinder7(); - } catch (ClassNotFoundException ex){ - selected = new RegionFinder6(); - } - regionFinder = selected; - } + private final IRegionFinder regionFinder = new RegionFinder7(); public IRegionFinder getRegionFinder() { return regionFinder; diff --git a/worldguard6.iml b/worldguard6.iml deleted file mode 100644 index e8e1a99..0000000 --- a/worldguard6.iml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - SPIGOT - BUKKIT - - - - - \ No newline at end of file diff --git a/worldguard6/pom.xml b/worldguard6/pom.xml deleted file mode 100644 index f55427d..0000000 --- a/worldguard6/pom.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - 4.0.0 - - com.muhammaddaffa - PlayerProfiles - 8.0.4 - - - worldguard6 - - - 21 - 21 - UTF-8 - - - - - - spigot-repo - https://hub.spigotmc.org/nexus/content/repositories/snapshots/ - - - sk89q-repo - https://maven.enginehub.org/repo/ - - - - - - - org.spigotmc - spigot-api - 1.17-R0.1-SNAPSHOT - provided - - - com.sk89q.worldguard - worldguard-legacy - 6.2 - provided - - - com.sk89q.worldedit - worldedit-bukkit - 6.1 - provided - - - ${project.groupId} - api - ${project.version} - - - - \ No newline at end of file diff --git a/worldguard6/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg6/RegionFinder6.java b/worldguard6/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg6/RegionFinder6.java deleted file mode 100644 index 27c9048..0000000 --- a/worldguard6/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg6/RegionFinder6.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.muhammaddaffa.playerprofiles.worldguardwrapper.wg6; - -import com.muhammaddaffa.api.IRegionFinder; -import com.sk89q.worldguard.bukkit.WGBukkit; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import org.bukkit.Location; - -import java.util.ArrayList; -import java.util.List; - -public class RegionFinder6 implements IRegionFinder { - - @Override - public List getRegions(Location location) { - // Get the ApplicableRegionSet from the location - com.sk89q.worldguard.protection.ApplicableRegionSet ars = WGBukkit.getPlugin().getRegionContainer().createQuery().getApplicableRegions(location); - // Create an empty array list of string - List regions = new ArrayList<>(); - // Loop through all regions in the location - for(ProtectedRegion region : ars){ - // Add the region name to the list - regions.add(region.getId()); - } - // Finally, return the list of regions name - return regions; - } - -} \ No newline at end of file diff --git a/worldguard6/worldguard6.iml b/worldguard6/worldguard6.iml deleted file mode 100644 index a589521..0000000 --- a/worldguard6/worldguard6.iml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - SPIGOT - - 1 - - - - \ No newline at end of file diff --git a/worldguard7/pom.xml b/worldguard7/pom.xml index bcf10c5..28b0194 100644 --- a/worldguard7/pom.xml +++ b/worldguard7/pom.xml @@ -6,14 +6,12 @@ com.muhammaddaffa PlayerProfiles - 8.0.4 + 8.0.5 worldguard7 - 21 - 21 UTF-8 @@ -34,19 +32,19 @@ org.spigotmc spigot-api - 1.17-R0.1-SNAPSHOT + ${spigot.version} provided com.sk89q.worldguard - worldguard-core - 7.0.0-SNAPSHOT + worldguard-bukkit + ${worldguard.version} provided com.sk89q.worldedit worldedit-bukkit - 7.3.0-SNAPSHOT + ${worldedit.version} provided diff --git a/worldguard7/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg7/RegionFinder7.java b/worldguard7/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg7/RegionFinder7.java index 5b70e0a..f2e3308 100644 --- a/worldguard7/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg7/RegionFinder7.java +++ b/worldguard7/src/main/java/com/muhammaddaffa/playerprofiles/worldguardwrapper/wg7/RegionFinder7.java @@ -13,22 +13,16 @@ public class RegionFinder7 implements IRegionFinder { @Override public List getRegions(Location location) { - // Get the location util using BukkitAdapter com.sk89q.worldedit.util.Location loc = BukkitAdapter.adapt(location); - // Get the RegionContainer + com.sk89q.worldguard.protection.regions.RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer(); - // Get the RegionQuery com.sk89q.worldguard.protection.regions.RegionQuery query = container.createQuery(); - // Get the ApplicableRegionSet com.sk89q.worldguard.protection.ApplicableRegionSet ars = query.getApplicableRegions(loc); - // Create an empty list of string + List regions = new ArrayList<>(); - // Loop through all regions in the location - for(ProtectedRegion region : ars){ - // Add the region name/id to the regions list + for (ProtectedRegion region : ars) { regions.add(region.getId()); } - // Finally, return the list of regions name return regions; } From 01d847f402930bacb12692b99ad6777b6576a152 Mon Sep 17 00:00:00 2001 From: evnrca Date: Thu, 27 Aug 2026 01:06:59 +0800 Subject: [PATCH 3/3] Add comprehensive README for Paper 1.21.11+ fork --- README.md | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 173 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 860439a..aa7e532 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,176 @@ # PlayerProfiles -If you're looking to get a support for this plugin, please add **mdaffa** on discord! -# License +A modern Minecraft plugin for viewing player profiles with detailed statistics, inventory, and more. This fork provides full support for **Paper 1.21.11+** servers. + +## Features + +- **Player Profiles** - View detailed player information, inventory, armor, ender chest, and statistics +- **Custom GUIs** - Create and manage custom GUI profiles with flexible configuration +- **WorldGuard Integration** - Region-based profile access control (disabled regions) +- **Combat Integration** - CombatLogX and DeluxeCombat support (disable profiles in combat) +- **PlaceholderAPI Support** - Full placeholder support for dynamic content +- **Profile Locking** - Players can lock/unlock their profiles +- **Cooldown System** - Configurable cooldowns for profile viewing +- **Distance Checks** - Auto-close profiles when players move too far apart +- **Multi-world Support** - Disable profiles in specific worlds +- **Auto-refresh** - Automatic placeholder updates in GUI items +- **Metrics** - bStats integration for plugin statistics + +## Requirements + +- **Paper 1.21.11+** (or compatible forks) +- **Java 21** +- **WorldGuard 7.x** (for region features) +- **WorldEdit 7.x** (required by WorldGuard) +- Optional: **PlaceholderAPI**, **CombatLogX**, **DeluxeCombat** + +## Installation + +1. Download the latest `PlayerProfiles-8.0.5.jar` from releases +2. Place in your server's `plugins/` folder +3. Restart the server +4. Configure `plugins/PlayerProfiles/config.yml` to your needs +5. Run `/playerprofiles reload` to apply changes + +## Configuration + +Main configuration files in `plugins/PlayerProfiles/`: + +| File | Description | +|------|-------------| +| `config.yml` | Main settings (cooldowns, combat, worlds, regions, sounds) | +| `gui.yml` | Default profile GUI layout and items | +| `gui-creator.yml` | Custom GUI creator settings | +| `data.yml` | Player profile data storage | +| `custom-gui/punish-gui.yml` | Example custom GUI | + +### Key Config Options + +```yaml +options: + disableNPC: true # Disable NPC profile viewing + disableInCombat: + enabled: true # Block profiles in combat + message: "{prefix} &cYou are not allowed to open profile while in combat!" + shiftClick: true # Shift+right-click to open profile + +autoRefresh: + enabled: true # Auto-update placeholders + refreshEvery: 20 # Update interval (ticks) + +cooldown: + enabled: true + duration: 3 # Seconds + message: "{prefix} &cPlease wait for another {time} second(s)!" + +disabledWorlds: + message: "{prefix} &cYou are not allowed to open profile in this world!" + worlds: + - 'pvpWorld' + +disabledRegions: + playerInDisabledRegionMessage: "{prefix} &cYou are not allowed to open profile in this region!" + targetInDisabledRegionMessage: "{prefix} &cThe target is in disabled region area!" + regions: + - 'disabledRegions' + - 'pvp' + +distanceCheck: + enabled: true + distance: 30 # Blocks + tooFarMessage: "{prefix} &e{player} &cis too far from you!" +``` + +## Commands + +| Command | Aliases | Permission | Description | +|---------|---------|------------|-------------| +| `/playerprofiles` | `/pp`, `/playerprofile`, `/playerp` | `playerprofiles.admin` | Main command | +| `/playerprofiles reload` | | `playerprofiles.admin` | Reload configuration | +| `/playerprofiles opengui ` | | `playerprofiles.admin` | Open custom GUI for player | +| `/playerprofiles listgui` | | `playerprofiles.admin` | List available custom GUIs | +| `/profile [player]` | `/p`, `/viewprofile` | `playerprofiles.profile` | View profile | +| `/lockprofile [player]` | `/lock`, `/profilelock` | `playerprofiles.lock` | Lock profile | +| `/unlockprofile [player]` | `/unlock`, `/profileunlock` | `playerprofiles.unlock` | Unlock profile | +| `/toggleprofile` | | `playerprofiles.toggle` | Toggle profile viewing | + +## Permissions + +``` +playerprofiles.* # All permissions +playerprofiles.admin # Admin commands (reload, opengui, listgui) +playerprofiles.profile # View profiles (/profile) +playerprofiles.profile.others # View other players' profiles +playerprofiles.lock # Lock own profile +playerprofiles.lock.others # Lock others' profiles +playerprofiles.unlock # Unlock own profile +playerprofiles.unlock.others # Unlock others' profiles +playerprofiles.toggle # Toggle profile viewing +playerprofiles.bypass.cooldown # Bypass profile cooldown +playerprofiles.bypass.distance # Bypass distance check +playerprofiles.bypass.combat # Bypass combat restriction +playerprofiles.bypass.disabledworld # Bypass disabled worlds +playerprofiles.bypass.disabledregion # Bypass disabled regions +``` + +## Custom GUI Creation + +Create custom GUIs in `gui-creator.yml` or use the in-game creator: + +1. Run `/playerprofiles opengui ` to open a custom GUI +2. Use `/playerprofiles listgui` to see available GUIs +3. Configure custom GUIs in `custom-gui/` folder + +## Placeholders + +With PlaceholderAPI installed, these placeholders are available: + +- `%playerprofiles_%` - Player statistics +- `%playerprofiles_kills%` - Player kills (if supported) +- `%playerprofiles_deaths%` - Player deaths (if supported) +- Any PAPI placeholder can be used in GUI items and messages + +## Building from Source + +```bash +# Requires Java 21 and Maven 3.8+ +git clone https://github.com/evnrca/PlayerProfiles26.git +cd PlayerProfiles26 +mvn clean package -DskipTests +# Output: target/PlayerProfiles-8.0.5.jar +``` + +## Module Structure + +``` +PlayerProfiles/ +├── api/ # Core API interfaces +├── core/ # Main plugin logic +├── worldguard-wrapper/ # WorldGuard version abstraction +├── worldguard7/ # WorldGuard 7.x implementation +└── dist/ # Final shaded JAR assembly +``` + +## Changes in This Fork (v8.0.5) + +- **Java 21** baseline +- **Paper 1.21.11** support +- **WorldGuard 7.0.10** / **WorldEdit 7.3.12** +- Removed legacy WorldGuard 6.x module +- Simplified WorldGuard wrapper (single implementation) +- Fixed DependencyManager brace issues +- Updated plugin.yml api-version to 1.21 +- Modernized RegionFinder for WG7 API + +## License + You can do whatever you want with the source code, just don't redistribute it. + +## Support + +For support, join the Discord and contact **mdaffa**. + +--- + +**Original Author**: aglerr, Starfruit2210 +**Fork Maintainer**: evnrca \ No newline at end of file