diff --git a/Documentation.md b/Documentation.md index 46ec1b0..bbb110b 100644 --- a/Documentation.md +++ b/Documentation.md @@ -1,128 +1,3 @@ - -# DupeAlias - An Advanced Dupe Plugin -**Make your server stand out by switching to DupeAlias, a powerful dupe plugin with niche features for unique servers.** - ---- - -## 🌟 Why Choose DupeAlias? - -DupeAlias isn't just another dupe plugin - it's a complete ecosystem for managing item behavior on your server. Whether you're running a creative build server, a unique survival experience, or a custom game mode. DupeAlias gives you unprecedented control over how items behave. - -### ✨ Key Features - -**🎯 Smart Item Tagging System** -- **UNIQUE** - Prevent specific items from being duplicated -- **FINAL** - Lock items against any modifications -- **INFINITE** - Create truly infinite resources that never run out -- **PROTECTED** - Make items completely inert and unusable - -**🔧 Advanced Global Rules Engine** -- Create complex rules based on item properties -- Match by material, enchantments, name patterns, lore, and more -- Support for armor trims, potion effects, attributes, and custom model data -- Flexible matching modes (AND, OR, NAND, XOR) - -**🖥️ Multiple Duplication Interfaces** -- **Replicator GUI** - Single-item duplication with visual feedback -- **Chest GUI** - Multi-item container-style duplication -- **Inventory GUI** - Mirror your entire inventory for easy access -- **Menu GUI** - Central hub for all duplication options - -**⚡ Performance & Customization** -- Per-permission refresh rates and cooldowns -- Extensive configuration options -- Session persistence (optional) -- Beautiful, modern GUIs with progress indicators - ---- - -## 🎮 Perfect For These Server Types - -- **Creative Servers** - Give builders infinite blocks while protecting special items -- **Survival+** - Create unique economies with controlled item flow -- **Minigames** - Provide infinite consumables while preventing exploitation -- **RPG Servers** - Protect quest items and create unbreakable gear -- **Prison Servers** - Control contraband while allowing resource flow - ---- - -## 📸 Screenshots - -*[Image: Main admin panel showing the clean, modern interface with gradient backgrounds and intuitive navigation]* - -*[Image: Global rules editor displaying the complex criteria system with material selection, enchantment matching, and tag application]* - -*[Image: Replicator GUI in action showing the animated rings, progress bars, and real-time item duplication]* - -*[Image: Held item management interface demonstrating individual tag application with conflict warnings]* - -*[Image: Configuration menu showcasing the extensive customization options and color-coded settings]* - ---- - -## ⚙️ Quick Setup - -1. Drop `DupeAlias.jar` into your plugins folder -2. Restart your server -3. Use `/da` to open the admin panel -4. Configure your global rules and permissions -5. Let your players use `/dupe` to start duplicating! - ---- - -## 🔑 Permissions Overview - -- `dupealias.admin` - Access to admin panel and configuration -- `dupealias.dupe` - Basic duplication command access -- `dupealias.gui.*` - Access to specific GUI types -- `dupealias.*.bypass` - Bypass tag restrictions (use carefully!) -- Permission-based refresh rates: `dupealias.gui.replicator.refresh.1` - ---- - -## 💡 Advanced Use Cases - -**Crate Key Protection** -Create a global rule that makes all items containing "key" in their name both UNIQUE and PROTECTED, preventing duplication and accidental use. - -**Infinite Building Materials** -Set up INFINITE tags on common building blocks, giving your builders unlimited resources while maintaining server economy balance. - -**Quest Item Security** -Use FINAL tags on story items to prevent players from renaming or modifying important quest objects. - -**Admin Tool Management** -Combine PROTECTED and UNIQUE tags to create admin-only items that can't be duplicated or used by regular players. - ---- - -## 🛠️ Developer-Friendly - -Built on the robust Alias Development Framework with: -- Clean, documented API -- Event-driven architecture -- Extensive configuration options -- JSON-based data storage -- Full backward compatibility - ---- - -## 📋 Requirements - -- **Minecraft Version**: 1.21+ -- **Server Software**: Paper, Purpur, or compatible -- **Java Version**: 17+ - ---- - -## 🤝 Support & Updates - -DupeAlias is actively maintained with regular updates and feature additions. Join our community for support, suggestions, and to see what's coming next! - -**Get DupeAlias today and revolutionize how items work on your server!** - ---- - # DupeAlias Documentation ## Table of Contents @@ -135,17 +10,15 @@ DupeAlias is actively maintained with regular updates and feature additions. Joi 6. [Permissions System](#permissions-system) 7. [Configuration](#configuration) 8. [Commands](#commands) -9. [Common Scenarios](#common-scenarios) -10. [Troubleshooting](#troubleshooting) - ---- +9. [Troubleshooting](#troubleshooting) +--- ## Installation & Setup ### Prerequisites -- Minecraft 1.21 or higher -- Paper, Purpur, or compatible server software -- Java 17 or higher +- Minecraft 1.21.5 +- Paper server software +- Java 21 or higher ### Installation Steps 1. Download the DupeAlias JAR file @@ -159,8 +32,7 @@ DupeAlias is actively maintained with regular updates and feature additions. Joi 3. Navigate to Configuration → Common Config to customize colors and branding 4. Set up your first global rules or start tagging items manually ---- - +--- ## Core Concepts ### Item Tags @@ -178,8 +50,7 @@ Individual item tags **always override** global rules. This allows for fine-grai - **Individual Tags**: Stored directly on the item's metadata, apply only to that specific item instance - **Global Rules**: Server-wide rules that apply tags based on item properties like material, name, enchantments, etc. ---- - +--- ## Item Tags System ### UNIQUE Tag @@ -191,13 +62,9 @@ Individual item tags **always override** global rules. This allows for fine-grai - Admin-only equipment - Currency items -**Conflicts**: Cannot be combined with INFINITE (creates a logical paradox) +**Conflicts**: Cannot be combined with INFINITE. **Example**: A crate key that should never be duplicated -``` -Individual: Right-click key in hand → Apply UNIQUE tag -Global: All items with "key" in name → Apply UNIQUE tag -``` ### FINAL Tag **Purpose**: Prevents any modification to the item @@ -214,12 +81,6 @@ Global: All items with "key" in name → Apply UNIQUE tag - Rank kits that shouldn't be modified - Event rewards with special formatting -**Example**: A quest sword that must keep its name -``` -Individual: Apply FINAL tag to specific sword -Global: All items with "Quest" in lore → Apply FINAL tag -``` - ### INFINITE Tag **Purpose**: Maintains maximum stack size and refills items @@ -230,17 +91,11 @@ Global: All items with "Quest" in lore → Apply FINAL tag **Use Cases**: - Creative-style building materials -- Infinite arrows for archery ranges +- "Infinity" enchantment for tipped arrows - Unlimited consumables for events **Conflicts**: Cannot be combined with UNIQUE or PROTECTED -**Example**: Infinite building blocks for creative areas -``` -Individual: Apply INFINITE to held stone blocks -Global: All concrete blocks → Apply INFINITE tag -``` - ### PROTECTED Tag **Purpose**: Makes items completely inert and unusable @@ -260,14 +115,7 @@ Global: All concrete blocks → Apply INFINITE tag **Conflicts**: Cannot be combined with INFINITE -**Example**: A decorative trophy that can't be used -``` -Individual: Apply PROTECTED to specific trophy -Global: All items with "Display" in name → Apply PROTECTED tag -``` - ---- - + --- ## Global Rules Engine ### Creating Global Rules @@ -289,92 +137,64 @@ Select which tags this rule will apply to matching items. Multiple tags can be s - **NAND**: Not all criteria match - **XOR**: Exactly one criteria matches +Note that if no criteria are selected, any material or ItemsAdder item will match. + #### Material Matching -- **IGNORE**: Apply to all materials -- **WHITELIST**: Only apply to selected materials -- **BLACKLIST**: Apply to all except selected materials +- **IGNORE**: Apply to all materials and ItemsAdder items +- **WHITELIST**: Only apply to selected materials and ItemsAdder items +- **BLACKLIST**: Apply to all except selected materials and ItemsAdder items ### Criteria Types #### Name Regex -Match items based on display name patterns using regular expressions. -``` -Example: ".*[Kk]ey.*" matches any item with "key" or "Key" in the name -``` - +Match items based on display name patterns using regular expressions. +``` Example: ".*[Kk]ey.*" matches any item with "key" or "Key" in the name ``` #### Lore Regex -Match items based on lore content using regular expressions. -``` -Example: ".*Special.*" matches items with "Special" anywhere in lore -``` - +Match items based on lore content using regular expressions. +``` Example: ".*Special.*" matches items with "Special" anywhere in lore ``` +#### Compound Tag Regex +Match an item based on its compound tag generated from `ItemStack#getAsComponentString()` +```Example: ".*(nutrition).*|.*(saturation).*" Matches any custom food item``` +#### NBT Tag Regex +Match an item based on its NBT tag generated from `ItemStack#getAsString()` +```Example: .*{dupenotallowed: 1b}.* matches items from DupePlus's blacklist``` #### Enchantments -Match items that have specific enchantments at minimum levels. -``` -Example: Sharpness V → matches items with Sharpness 5 or higher -``` - +Match items that have specific enchantments at minimum levels. +``` Example: Sharpness V → matches items with Sharpness 5 or higher ``` #### Attributes -Match items with specific attribute modifiers. -``` -Example: Attack Damage ≥ 10.0 → matches weapons with high damage -``` - +Match items with specific attribute modifiers. +``` Example: Attack Damage ≥ 10.0 → matches weapons with high damage ``` #### Potion Effects -Match potions/foods with specific effects and amplifiers. -``` -Example: Strength II → matches items giving Strength 2 or higher -``` - +Match potions/foods with specific effects and amplifiers. +``` Example: Strength II → matches items giving Strength 2 or higher ``` #### Model Data -Match items with specific custom model data values. -``` -Example: 12345 → matches items with CustomModelData: 12345 -``` - +Match items with specific custom model data values. +``` Example: 12345 → matches items with CustomModelData: 12345 ``` #### Item Flags -Match items with specific visibility flags. -``` -Example: HIDE_ENCHANTS → matches items that hide enchantments -``` - +Match items with specific visibility flags. +``` Example: HIDE_ENCHANTS → matches items that hide enchantments ``` #### Armor Trim -Match armor pieces with specific trim patterns or materials. -``` -Example: Silence Pattern + Gold Material → matches gold silence trim armor -``` - +Match armor pieces with specific trim patterns or materials. +``` Example: Silence Pattern + Gold Material → matches gold silence trim armor ``` ### Example Rules #### Protect All Crate Keys -``` -Applied Tags: UNIQUE, PROTECTED, FINAL -Match Mode: OR -Name Regex: ".*[Kk]ey.*" -Lore Regex: ".*[Cc]rate.*" -``` - +``` Applied Tags: UNIQUE, PROTECTED, FINAL Match Mode: OR NBT Tag Regex: ".*excellentcrates.*" Lore Regex: ".*[Cc]rate.*" ``` #### Make Netherite Gear Unmodifiable -``` -Applied Tags: FINAL -Match Mode: AND -Material Mode: WHITELIST -Materials: NETHERITE_SWORD, NETHERITE_AXE, NETHERITE_PICKAXE, etc. -``` - +``` Applied Tags: FINAL Match Mode: AND Material Mode: WHITELIST Materials: NETHERITE_SWORD, NETHERITE_AXE, NETHERITE_PICKAXE, etc. ``` #### Infinite Creative Blocks -``` -Applied Tags: INFINITE -Match Mode: AND -Material Mode: WHITELIST Materials: STONE, DIRT, WOOD, CONCRETE variants -``` - ---- - +``` Applied Tags: INFINITE Match Mode: AND Material Mode: WHITELIST Materials: STONE, DIRT, WOOD, CONCRETE variants ``` +--- ## Duplication GUIs ### Replicator GUI -**Access**: `/dupe replicator` or through main menu +**Access**: `/dupe replicator` or through main menu +**Permissions**: +- `dupealias.gui.replicator`: Main Access +- `dupealias.gui.replicator.cooldown.`: Sets the input swap cooldown time (milliseconds). +- `dupealias.gui.replicator.refresh.`: Sets the amount of ticks it takes for the output item to restock. +- `dupealias.gui.replicator.keep`: Determines if the player should keep the items in the chest when they close it. + **Features**: - Single-item focused duplication @@ -391,13 +211,18 @@ Material Mode: WHITELIST Materials: STONE, DIRT, WOOD, CONCRETE variants **Best For**: Quick duplication of single item types ### Chest GUI -**Access**: `/dupe chest` or through main menu +**Access**: `/dupe chest` or through main menu +**Permissions**: +- `dupealias.gui.chest`: Main Access +- `dupealias.gui.chest.cooldown.`: Sets the input swap cooldown time (milliseconds). +- `dupealias.gui.chest.keep`: Determines if the player should keep the items in the chest when they close it. +- `dupealias.gui.chest.keepondeath`: If set to false, all items in the chest will be dropped on the ground on death. **Features**: - Multi-item container interface - 4 input columns, 4 output columns - Individual item refresh timers -- Session persistence (if enabled) +- Session persistence (doesn't persist over reboots) **How to Use**: 1. Open the GUI @@ -405,10 +230,13 @@ Material Mode: WHITELIST Materials: STONE, DIRT, WOOD, CONCRETE variants 3. Take duplicated copies from the right 4 columns 4. Items refresh based on configured delays -**Best For**: Bulk duplication of multiple item types +**Best For**: Duplication of items you'll commonly need through session persistence. ### Inventory GUI -**Access**: `/dupe inventory` or through main menu +**Access**: `/dupe inventory` or through main menu +**Permissions**: +- `dupealias.gui.chest`: Main Access +- `dupealias.gui.chest.refresh.`: Sets the amount of ticks for the output items to restock **Features**: - Mirror of your actual inventory @@ -425,7 +253,8 @@ Material Mode: WHITELIST Materials: STONE, DIRT, WOOD, CONCRETE variants **Best For**: Easy access to copies of everything you're carrying ### Menu GUI -**Access**: `/dupe gui` or `/dupe` (if set as default) +**Access**: `/dupe gui` or `/dupe` (if set as default) +**Permission**: `dupealias.gui` If granted, all other GUIs will be granted too unless specifically set to false. **Features**: - Central hub for all GUI types @@ -437,167 +266,170 @@ Material Mode: WHITELIST Materials: STONE, DIRT, WOOD, CONCRETE variants 2. Click on the GUI type you want to use 3. Access is controlled by permissions ---- - +--- ## Permissions System ### Core Permissions #### Admin Access -```yaml -dupealias.admin: true # Access to admin panel and configuration -``` - +```yaml +dupealias.admin: true # Access to admin panel and configuration +``` #### Basic Duplication -```yaml -dupealias.dupe: true # Access to /dupe command -dupealias.dupe.cooldown.: false # Cooldown for command duping (milliseconds) -``` - +```yaml +dupealias.dupe: true # Access to /dupe command +dupealias.dupe.cooldown.: false # Cooldown for command duping (milliseconds) +``` #### GUI Access -```yaml -dupealias.gui: true # Access to all GUIs -dupealias.gui.replicator: true # Access to replicator GUI -dupealias.gui.inventory: true # Access to inventory GUI dupealias.gui.chest: true # Access to chest GUI -``` - +```yaml +dupealias.gui: true # Access to all GUIs +dupealias.gui.replicator: true # Access to replicator GUI +dupealias.gui.inventory: true # Access to inventory GUI +dupealias.gui.chest: true # Access to chest GUI +``` ### Advanced Permissions #### Session Persistence -```yaml -dupealias.gui.replicator.keep: false # Keep replicator items on close -dupealias.gui.chest.keep: false # Keep chest items on close -dupealias.gui.chest.keepondeath: false # Keep chest items on death -``` - +```yaml +dupealias.gui.replicator.keep: false # Keep replicator items on close +dupealias.gui.chest.keep: false # Keep chest items on close +dupealias.gui.chest.keepondeath: false # Keep chest items on death +``` #### Permission Based Refresh Rates -```yaml -dupealias.gui.replicator.refresh.: false # Ticks of refresh cooldown -dupealias.gui.replicator.cooldown.: false # Ticks of re-input cooldown -dupealias.gui.inventory.refresh.: false # Ticks of refresh cooldown -dupealias.gui.chest.refresh.: false # Ticks of refresh cooldown -``` - +```yaml +dupealias.gui.replicator.refresh.: false # Ticks of refresh cooldown +dupealias.gui.replicator.cooldown.: false # Ticks of re-input cooldown +dupealias.gui.inventory.refresh.: false # Ticks of refresh cooldown +dupealias.gui.chest.refresh.: false # Ticks of refresh cooldown +``` #### Tag Bypasses (Use Carefully!) -```yaml -dupealias.unique.bypass: false # Can dupe UNIQUE items +```yaml +dupealias.unique.bypass: false # Can dupe UNIQUE items dupealias.final.bypass: false # Can modify FINAL items -dupealias.protected.bypass: false # Can use PROTECTED items -``` - +dupealias.protected.bypass: false # Can use PROTECTED items +``` #### Special Permissions -```yaml -dupealias.infinite: true # Can use INFINITE items -``` - +```yaml +dupealias.infinite: true # Can use INFINITE items +``` ### Permission Hierarchy -The plugin uses a "lowest value wins" system for numeric permissions. If a player has both `refresh.20` and `refresh.5`, they will get the 5-tick refresh cooldown. +The plugin generally uses a "lowest value wins" system for numeric permissions. If a player has both `refresh.20` and `refresh.5`, they will get the 5-tick refresh cooldown. There is an exception to this rule however. For the permission `dupealias.dupe.limit.`, it will take the highest value given. This is because I am lazy and did not add a default limit for non ranked players. ### Example Permission Sets #### VIP Player -```yaml -groups: - vip: - permissions: - - dupealias.dupe - dupealias.gui - dupealias.gui.replicator.refresh.5 # Faster refresh - - dupealias.gui.replicator.cooldown.10 # Shorter cooldown - - dupealias.dupe.cooldown.0 # No command cooldown -``` - +```yaml +groups: + vip: + permissions: + - dupealias.dupe + - dupealias.gui + - dupealias.gui.replicator.refresh.5 # Faster refresh + - dupealias.gui.replicator.cooldown.10 # Shorter cooldown + - dupealias.dupe.cooldown.0 # No command cooldown +``` #### Staff Member -```yaml -groups: - staff: - permissions: - dupealias.admin # Full admin access - - dupealias.gui.replicator.refresh.1 # Instant refresh - - dupealias.gui.*.keep # Session persistence - - dupealias.final.bypass # Can modify final items -``` - ---- - +```yaml +groups: + staff: + permissions: + - dupealias.admin # Full admin access + - dupealias.gui.replicator.refresh.1 # Instant refresh + - dupealias.gui.*.keep # Session persistence + - dupealias.final.bypass # Can modify final items +``` + --- ## Configuration ### Main Configuration (`config.json`) #### Duplication Settings -```json -{ - "dupeCooldownMillis": 1000, // Command cooldown in milliseconds - "defaultDupeGui": "REPLICATOR" // Default GUI (REPLICATOR/INVENTORY/CHEST/MENU) -} -``` - +```json +{ + "dupeCooldownMillis": 1000, // Command cooldown in milliseconds + "defaultDupeGui": "REPLICATOR" // Default GUI (REPLICATOR/INVENTORY/CHEST/MENU) + } +``` #### GUI Refresh Rates -```json -{ - "replicator": { - "baseRefreshDelayTicks": 1, // Base item refresh delay - "baseInputCooldownTicks": 20 // Base input change cooldown - }, "chest": { - "baseRefreshDelayTicks": 1 // Base item refresh delay - }, - "inventory": { - "baseRefreshDelayTicks": 1 // Base item refresh delay - }} -``` +```json +{ + "replicator": { + "baseRefreshDelayTicks": 1, // Base item refresh delay + "baseInputCooldownTicks": 20 // Base input change cooldown + }, + "chest": { + "baseRefreshDelayTicks": 1 // Base item refresh delay + }, + "inventory": { + "baseRefreshDelayTicks": 1 // Base item refresh delay + } +} +``` #### Command Blocking -```json -{ - "finalCommandRegex": [ - "\"(?:itemname|iname)\"gmi", // Block item naming commands "\"(?:itemlore|lore)\"gmi" // Block lore modification commands ]} -``` + +```json +{ + "finalCommandRegex": [ + "\"(?:itemname|iname)\"gmi", // Block item naming commands + "\"(?:itemlore|lore)\"gmi" // Block lore modification commands + ] +} + +``` #### Tag Lore Customization (MiniMessage) -```json -{ - "trueTagLore": { - "UNIQUE": "| Unique", - "FINAL": "| Final", - "INFINITE": "| Infinite", - "PROTECTED": "| Protected" - }, - "falseTagLore": { - "UNIQUE": "| Dupeable", - "FINAL": "| Mutable", - "INFINITE": "| Finite", - "PROTECTED": "| Unprotected" - } - } -``` + +```json +{ + "trueTagLore": { + "UNIQUE": "| Unique", + "FINAL": "| Final", + "INFINITE": "| Infinite", + "PROTECTED": "| Protected" + }, + "falseTagLore": { + "UNIQUE": "| Dupeable", + "FINAL": "| Mutable", + "INFINITE": "| Finite", + "PROTECTED": "| Unprotected" + } +} + +``` ### Common Configuration (`common.json`) #### Visual Customization -```json -{ - "mainColor": 11184895, // Primary color (hex: AAAAFF) - "secondaryColor": 909055, // Secondary color (hex: 00DDFF) - "pluginName": "DupeAlias", // Display name - "flatPrefix": "&9DupeAlias> &7", // Legacy chat prefix - "flat": false // Use legacy formatting -} -``` + +```json +{ + "mainColor": 11184895, // Primary color (hex: AAAAFF) + "secondaryColor": 909055, // Secondary color (hex: 00DDFF) + "pluginName": "DupeAlias", // Display name + "flatPrefix": "&9DupeAlias> &7", // Legacy chat prefix + "flat": false // Use legacy formatting +} + +``` #### Debug Settings -```json -{ - "debugMode": false, // Enable debug output - "debuggerExclusions": [] // Methods to exclude from debug -} -``` - ---- +```json +{ + "debugMode": false, // Enable debug output + "debuggerExclusions": [] // Methods to exclude from debug +} + +``` + --- ## Commands ### Administrative Commands #### `/dupealias` (Aliases: `/da`) -**Permission**: `dupealias.admin` +**Permission**: `dupealias.admin` **Description**: Main administrative command **Subcommands**: @@ -625,7 +457,7 @@ groups: ### Player Commands #### `/dupe` -**Permission**: `dupealias.dupe` +**Permission**: `dupealias.dupe` **Description**: Main duplication command **Usage**: @@ -642,35 +474,35 @@ groups: ### Common Issues #### "You cannot dupe unique items" -**Cause**: Item has UNIQUE tag or matches global rule +**Cause**: Item has UNIQUE tag or matches global rule **Solution**: - Check `/da` → Held Item Actions to see current tags - Review global rules that might be applying UNIQUE tag - Use `/da tag UNIQUE remove` to remove individual tag #### Items not refreshing in GUI -**Cause**: Long refresh delay or permission issues +**Cause**: Long refresh delay or permission issues **Solution**: - Check player has appropriate GUI permissions - Verify refresh rate permissions (lower numbers = faster) - Ensure player isn't hitting cooldown limits #### "You cannot modify final items" -**Cause**: Item has FINAL tag blocking modifications +**Cause**: Item has FINAL tag blocking modifications **Solution**: - Check item tags in admin panel - Use `/da tag FINAL remove` if needed - Grant `dupealias.final.bypass` permission for admins #### Global rules not applying -**Cause**: Rule criteria not matching or individual tags overriding +**Cause**: Rule criteria not matching or individual tags overriding **Solution**: - Test rule criteria with `/da rule info ` - Check match mode (AND vs OR) - Remember individual tags override global rules #### GUI won't open -**Cause**: Missing permissions or plugin conflicts +**Cause**: Missing permissions or plugin conflicts **Solution**: - Verify player has `dupealias.gui` permission - Check for inventory plugin conflicts @@ -678,26 +510,19 @@ groups: ### Debug Mode -Enable debug mode to troubleshoot issues: -``` -/da debug toggle -``` - +Enable debug mode to troubleshoot issues: ``` /da debug toggle ``` This will show detailed information about: - Tag checking processes - Global rule matching - Permission calculations - GUI state changes -Exclude noisy methods: -``` -/da debug exclude -``` - +Exclude noisy methods: ``` /da debug exclude ``` ### Performance Considerations #### Large Player Counts - Use session persistence sparingly +- Don't use GUI refresh or input cooldowns. - Monitor server TPS with `/tps` #### Complex Global Rules @@ -706,19 +531,17 @@ Exclude noisy methods: - Limit the number of active global rules #### GUI Optimization -- Set appropriate refresh rates based on server performance - Consider disabling session persistence for busy servers - Use cooldowns to prevent spam ---- - +--- ### Getting Help If you encounter issues not covered in this documentation: 1. Enable debug mode and check console logs -2. Test with a minimal permission set -3. Verify your global rules are correctly configured -4. Check for conflicts with other plugins +2. Verify your global rules are correctly configured +3. Check for conflicts with other plugins +4. Join the [Alias Development](https://trouper.me/alias) discord -Remember that individual item tags always override global rules, and bypass permissions should be used carefully as they can compromise your server's item security system. +Remember that individual item tags always override global rules, and bypass permissions should be used carefully as they can compromise your server's item security system. \ No newline at end of file diff --git a/Ideas.md b/Ideas.md index 29e3e75..3f82bae 100644 --- a/Ideas.md +++ b/Ideas.md @@ -1 +1 @@ -Bulk item tag adder +Messages and Language Configuration File \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..692fdc1 --- /dev/null +++ b/README.md @@ -0,0 +1,87 @@ +# DupeAlias - An Advanced Dupe Plugin +**Make your server stand out by switching to DupeAlias, a powerful dupe plugin with niche features for unique servers.** + +--- +## Why Choose DupeAlias? +DupeAlias gives you unprecedented control over how items can be copied or duplicated on your server. Configurable through a sleek in-game GUI, DupeAlias provides a friction-free administration experience. + +### Key Features +**Smart Item Tagging System** +- **UNIQUE** - Prevent specific items from being duplicated +- **FINAL** - Lock items against any modifications +- **INFINITE** - Create stacks that never run out +- **PROTECTED** - Make items completely inert and unusable + +**Advanced Global Rules Engine** +- Create complex rules based on item properties +- Match by material, enchantments, name patterns, lore, attributes, and more +- Support for ItemsAdder +- Flexible matching modes (AND, OR, NAND, XOR) + +**Multiple Duplication Interfaces** +- **Replicator GUI** - Single-item duplication with visual feedback +- **Chest GUI** - Multi-item container-style duplication +- **Inventory GUI** - Mirror your entire inventory for easy access +- **Menu GUI** - Central hub for all duplication options + +**Performance & Customization** +- Per-permission refresh rates and cooldowns +- Extensive configuration options +- Session persistence (optional) +- Beautiful, modern GUIs with progress indicators + +--- +## Perfect For These Server Types +- **Survival/Creative** - Give players infinite building blocks while protecting valuable items +- **Dupe Servers** - Create economies in an otherwise chaotic server class +- **Minigames** - Provide infinite consumables while preventing exploitation +- **RPG Servers** - Protect quest items and create unbreakable gear (You don't *have* to use the dupe feature!) + +--- +## Screenshots + + +The main Admin Panel, click on the green book for a quick guide on how to use the plugin. +![Main Admin Gui](images/maingui.gif) +The Replicator GUI is a great choice for the default dupe GUI. It is clean, simple, and you have good control over how its used. +![Replicator Dupe Gui](images/replicator.gif) +The Global Rule Editor is designed to be intuitive to use, with quick buttons for changing match modes and toggling tags. +![Rule Manager Gui](images/rules.gif) +Want to know how an item is processed? Head over to the help GUI and hover on you held item's icon. An explanation will be generated for you. +![Conflict Detection](images/conflicts.gif) + +--- +## Quick Setup +1. Drop `DupeAlias.jar` into your plugins folder +2. Restart your server +3. Use `/da` to open the admin panel +4. Configure your global rules and permissions +5. Let your players use `/dupe` to start duplicating! + +--- +## Permissions Overview +- `dupealias.admin` - Access to admin panel and configuration +- `dupealias.dupe` - Basic duplication command access +- `dupealias.gui.*` - Access to specific GUI types +- `dupealias.*.bypass` - Bypass tag restrictions (use carefully!) +- Permission-based refresh rates: `dupealias.gui.replicator.refresh.1` + +--- +## Use Cases +**Crate Key Protection** Create a global rule that makes all items containing `"excellentcrates:crate_key.id"` in their NBT tag, UNIQUE, FINAL, and PROTECTED, preventing duplication and accidental use. + +**Infinite Building Materials** Set up INFINITE tags on common building blocks, giving your players unlimited resources for creative purposes. + +**Rank Kit Exclusivity** Combine both FINAL and UNIQUE tags on rank crates to prevent ranked players from modifying and redistributing exclusive branded kits. + +**Admin Tool Management** Add the UNIQUE tag to admin-only items so they cannot be duplicated by regular players. + +--- + +## Requirements +- **Minecraft Version**: 1.21.5 +- **Server Software**: Paper +- **Java Version**: 21+ + +## Documentation +- [Documentation is available in Documentation.md](Documentation.md) diff --git a/build.gradle.kts b/build.gradle.kts index 8bbe11b..3c3546c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,11 +16,16 @@ java { repositories { mavenLocal() + maven { + name = "matteodev" + url = uri("https://maven.devs.beer/") + } } dependencies { paperweight.paperDevBundle("1.21.5-R0.1-SNAPSHOT") implementation("me.trouper:alias:1.0-1.21.5-SNAPSHOT") + compileOnly("dev.lone:api-itemsadder:4.0.10") } tasks { diff --git a/images/conflicts.gif b/images/conflicts.gif new file mode 100644 index 0000000..b52b391 Binary files /dev/null and b/images/conflicts.gif differ diff --git a/images/maingui.gif b/images/maingui.gif new file mode 100644 index 0000000..f4e9ce8 Binary files /dev/null and b/images/maingui.gif differ diff --git a/images/replicator.gif b/images/replicator.gif new file mode 100644 index 0000000..61a9a83 Binary files /dev/null and b/images/replicator.gif differ diff --git a/images/rules.gif b/images/rules.gif new file mode 100644 index 0000000..5e65340 Binary files /dev/null and b/images/rules.gif differ diff --git a/src/main/java/me/trouper/dupealias/data/GlobalRule.java b/src/main/java/me/trouper/dupealias/data/GlobalRule.java index 29f7603..a052d92 100644 --- a/src/main/java/me/trouper/dupealias/data/GlobalRule.java +++ b/src/main/java/me/trouper/dupealias/data/GlobalRule.java @@ -2,6 +2,7 @@ package me.trouper.dupealias.data; import me.trouper.alias.data.enums.*; import me.trouper.alias.utils.misc.MapUtils; +import me.trouper.dupealias.DupeContext; import me.trouper.dupealias.server.ItemTag; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; @@ -21,7 +22,7 @@ import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; -public class GlobalRule { +public class GlobalRule implements DupeContext { public enum MatchMode { AND, OR, NAND, XOR @@ -36,9 +37,12 @@ public class GlobalRule { public MatchMode matchMode = MatchMode.AND; public MaterialMatchMode materialMode = MaterialMatchMode.IGNORE; public Set effectedMaterials = EnumSet.noneOf(Material.class); + public List effectedItemsAdderMaterials = new ArrayList<>(); public String nameContainsRegex = ""; public String loreContainsRegex = ""; + public String compoundTagContainsRegex = ""; + public String nbtTagContainsRegex = ""; public Set legacyModelData = new HashSet<>(); public Set itemFlags = EnumSet.noneOf(ItemFlag.class); public Map enchantments = new HashMap<>(); @@ -51,14 +55,19 @@ public class GlobalRule { @SuppressWarnings("deprecation") public boolean doesMatch(ItemStack item) { - if (item == null || item.getType() == Material.AIR) return false; + if (item == null || item.isEmpty()) return false; + + ItemsAdderItem iai = new ItemsAdderItem(); + try { + iai = new ItemsAdderItem(item); + } catch (IllegalArgumentException ignored) {} switch (materialMode) { case WHITELIST -> { - if (!effectedMaterials.contains(item.getType())) return false; + if (!effectedMaterials.contains(item.getType()) && !effectedItemsAdderMaterials.contains(iai)) return false; } case BLACKLIST -> { - if (effectedMaterials.contains(item.getType())) return false; + if (effectedMaterials.contains(item.getType()) || effectedItemsAdderMaterials.contains(iai)) return false; } case IGNORE -> {} } @@ -66,6 +75,7 @@ public class GlobalRule { ItemMeta meta = item.getItemMeta(); if (meta == null) return false; + List results = new ArrayList<>(); if (!nameContainsRegex.isEmpty()) { @@ -82,6 +92,18 @@ public class GlobalRule { results.add(found); } + if (!compoundTagContainsRegex.isEmpty()) { + Pattern compoundPatten = safeCompileRegex(compoundTagContainsRegex); + String compound = meta.getAsComponentString(); + results.add(compoundPatten.matcher(compound).find()); + } + + if (!nbtTagContainsRegex.isEmpty()) { + Pattern nbtPattern = safeCompileRegex(compoundTagContainsRegex); + String nbt = meta.getAsString(); + results.add(nbtPattern.matcher(nbt).find()); + } + if (!enchantments.isEmpty()) { Map itemEnchants = item.getEnchantments(); results.add(MapUtils.allValuesMatch(itemEnchants, enchantments.entrySet().stream().collect(Collectors.toMap( @@ -135,6 +157,8 @@ public class GlobalRule { int trueCount = (int) results.stream().filter(Boolean::booleanValue).count(); int total = results.size(); + if (getCriteriaCount() == 0) return true; + return switch (matchMode) { case AND -> trueCount == total; case OR -> trueCount > 0; @@ -156,11 +180,13 @@ public class GlobalRule { int criteriaCount = 0; if (!nameContainsRegex.isEmpty()) criteriaCount++; if (!loreContainsRegex.isEmpty()) criteriaCount++; + if (!nbtTagContainsRegex.isEmpty()) criteriaCount++; + if (!compoundTagContainsRegex.isEmpty()) criteriaCount++; + if (!legacyModelData.isEmpty()) criteriaCount++; if (!enchantments.isEmpty()) criteriaCount++; if (!potionEffects.isEmpty()) criteriaCount++; if (!attributes.isEmpty()) criteriaCount++; if (!itemFlags.isEmpty()) criteriaCount++; - if (!legacyModelData.isEmpty()) criteriaCount++; if (!trimPatterns.isEmpty()) criteriaCount++; if (!trimMaterials.isEmpty()) criteriaCount++; return criteriaCount; diff --git a/src/main/java/me/trouper/dupealias/data/ItemsAdderItem.java b/src/main/java/me/trouper/dupealias/data/ItemsAdderItem.java new file mode 100644 index 0000000..78162d7 --- /dev/null +++ b/src/main/java/me/trouper/dupealias/data/ItemsAdderItem.java @@ -0,0 +1,90 @@ +package me.trouper.dupealias.data; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.Map; +import java.util.Objects; + +public class ItemsAdderItem { + + public final String namespace; + + public final String id; + + private static final Gson GSON = new Gson(); + + public ItemsAdderItem() { + this.namespace = null; + this.id = null; + } + + @SuppressWarnings("unchecked") + public ItemsAdderItem(ItemStack itemStack) throws IllegalArgumentException { + if (itemStack == null) { + throw new IllegalArgumentException("ItemStack provided cannot be null."); + } + + ItemMeta itemMeta = itemStack.getItemMeta(); + if (itemMeta == null) { + throw new IllegalArgumentException("ItemStack has no ItemMeta. It cannot be an ItemsAdder item."); + } + + String itemMetaJson = itemMeta.getAsString(); + if (itemMetaJson.trim().isEmpty()) { + throw new IllegalArgumentException("ItemMeta.getAsString() returned null or an empty string. No NBT data found."); + } + + Map components; + try { + components = GSON.fromJson(itemMetaJson, Map.class); + } catch (JsonSyntaxException e) { + throw new IllegalArgumentException("Failed to parse ItemMeta JSON string: " + e.getMessage()); + } + + Object customDataObj = components.get("minecraft:custom_data"); + if (!(customDataObj instanceof Map)) { + throw new IllegalArgumentException("Missing or invalid 'minecraft:custom_data' component in ItemStack NBT. Expected a JSON object."); + } + + Map customData = (Map) customDataObj; + + Object itemsAdderObj = customData.get("itemsadder"); + if (!(itemsAdderObj instanceof Map)) { + throw new IllegalArgumentException("Missing or invalid 'itemsadder' object within 'minecraft:custom_data'. Expected a JSON object."); + } + Map itemsAdderData = (Map) itemsAdderObj; + + Object namespaceObj = itemsAdderData.get("namespace"); + Object idObj = itemsAdderData.get("id"); + + if (!(namespaceObj instanceof String) || ((String) namespaceObj).trim().isEmpty()) { + throw new IllegalArgumentException("Missing, invalid, or empty 'namespace' field in ItemsAdder custom data. Expected a non-empty string."); + } + this.namespace = (String) namespaceObj; + + if (!(idObj instanceof String) || ((String) idObj).trim().isEmpty()) { + throw new IllegalArgumentException("Missing, invalid, or empty 'id' field in ItemsAdder custom data. Expected a non-empty string."); + } + this.id = (String) idObj; + } + @Override + public String toString() { + return "ItemsAdderItem{namespace='" + namespace + "', id='" + id + "'}"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ItemsAdderItem that = (ItemsAdderItem) o; + return Objects.equals(namespace, that.namespace) && + Objects.equals(id, that.id); + } + @Override + public int hashCode() { + return Objects.hash(namespace, id); + } +} diff --git a/src/main/java/me/trouper/dupealias/data/files/DupeConfig.java b/src/main/java/me/trouper/dupealias/data/files/DupeConfig.java index 0305837..24e8cf2 100644 --- a/src/main/java/me/trouper/dupealias/data/files/DupeConfig.java +++ b/src/main/java/me/trouper/dupealias/data/files/DupeConfig.java @@ -6,7 +6,10 @@ import me.trouper.dupealias.data.GlobalRule; import me.trouper.dupealias.server.ItemTag; import java.io.File; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class DupeConfig implements JsonSerializable, DupeContext { @Override @@ -36,6 +39,8 @@ public class DupeConfig implements JsonSerializable, DupeContext { ItemTag.UNIQUE, "| Dupeable", ItemTag.INFINITE, "| Finite" )); + + public boolean blockDupePlus = false; public List globalRules = new ArrayList<>(); diff --git a/src/main/java/me/trouper/dupealias/server/DupeManager.java b/src/main/java/me/trouper/dupealias/server/DupeManager.java index 8a3eaa5..a900274 100644 --- a/src/main/java/me/trouper/dupealias/server/DupeManager.java +++ b/src/main/java/me/trouper/dupealias/server/DupeManager.java @@ -14,7 +14,9 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.permissions.PermissionAttachmentInfo; import org.bukkit.persistence.PersistentDataType; -import java.util.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; public class DupeManager implements DupeContext { @@ -77,7 +79,9 @@ public class DupeManager implements DupeContext { * Checks if any global rule applies this tag to the given item */ public boolean checkGlobalRuleTag(ItemStack input, ItemTag tag) { + getVerbose().send("Checking tag {0} on item {1}",tag,input.getType()); for (GlobalRule rule : getConfig().globalRules) { + getVerbose().send("Scanning rule with tags {0}",rule.appliedTags.toString()); if (rule.appliedTags.contains(tag) && rule.doesMatch(input)) { return true; } @@ -122,12 +126,11 @@ public class DupeManager implements DupeContext { * Adds a global rule for a specific material and tag */ public boolean addGlobalRuleForMaterial(Material material, ItemTag tag) { - // Check if rule already exists for (GlobalRule rule : getConfig().globalRules) { if (rule.appliedTags.contains(tag) && rule.materialMode == GlobalRule.MaterialMatchMode.WHITELIST && rule.effectedMaterials.contains(material)) { - return false; // Rule already exists + return false; } } @@ -236,8 +239,8 @@ public class DupeManager implements DupeContext { throw new IllegalArgumentException("Invalid NameSpacedKey '%s'".formatted(key.value())); } - public int getPermissionValue(Player player, String rootPermission, int fallback) { - int lowestCooldown = Integer.MAX_VALUE; + public int getPermissionValue(Player player, String rootPermission, int fallback, boolean takeHighest) { + int result = takeHighest ? Integer.MIN_VALUE : Integer.MAX_VALUE; for (PermissionAttachmentInfo permInfo : player.getEffectivePermissions()) { String perm = permInfo.getPermission(); @@ -246,13 +249,24 @@ public class DupeManager implements DupeContext { String valueStr = perm.substring(rootPermission.length()); try { int value = Integer.parseInt(valueStr); - if (value < lowestCooldown) { - lowestCooldown = value; + if (takeHighest) { + if (value > result) { + result = value; + } + } else { + if (value < result) { + result = value; + } } } catch (NumberFormatException ignored) {} } } - return (lowestCooldown == Integer.MAX_VALUE) ? fallback : lowestCooldown; + if (takeHighest) { + return (result == Integer.MIN_VALUE) ? fallback : result; + } else { + return (result == Integer.MAX_VALUE) ? fallback : result; + } } + } \ No newline at end of file diff --git a/src/main/java/me/trouper/dupealias/server/commands/AdminCommand.java b/src/main/java/me/trouper/dupealias/server/commands/AdminCommand.java index e827856..20f9cb4 100644 --- a/src/main/java/me/trouper/dupealias/server/commands/AdminCommand.java +++ b/src/main/java/me/trouper/dupealias/server/commands/AdminCommand.java @@ -7,6 +7,7 @@ import me.trouper.alias.server.commands.QuickCommand; import me.trouper.alias.server.commands.completions.CompletionBuilder; import me.trouper.dupealias.DupeContext; import me.trouper.dupealias.data.GlobalRule; +import me.trouper.dupealias.data.ItemsAdderItem; import me.trouper.dupealias.server.ItemTag; import me.trouper.dupealias.server.gui.admin.AdminPanelManager; import me.trouper.dupealias.server.gui.admin.MainAdminGui; @@ -49,6 +50,10 @@ public class AdminCommand implements QuickCommand, DupeContext { handleRule(sender,args); } + case "test" -> { + handleDebugTests(sender,args); + } + default -> { errorAny(sender,"Invalid subcommand!"); } @@ -93,13 +98,20 @@ public class AdminCommand implements QuickCommand, DupeContext { ) ).then( b.arg("gui") + ).then( + + b.arg("test") + .then( + b.arg("nbt","component","itemsadder") + ) + ); } private void handleDebug(CommandSender sender, Args args) { if (args.getSize() < 2) { - errorAny(sender, "Usage: debug "); + errorAny(sender, "Usage: debug "); return; } @@ -141,6 +153,43 @@ public class AdminCommand implements QuickCommand, DupeContext { successAny(sender, "Removed exclusion for {0} on the debugger.", exclusion); } + case "test" -> handleDebugTests(sender, args); + } + } + + private void handleDebugTests(CommandSender sender, Args args) { + if (args.getSize() < 2) { + errorAny(sender, "Usage: test "); + return; + } + + final String sub = args.get(1).toString(); + + switch (sub) { + case "nbt" -> { + Player player = (Player) sender; + String tag = player.getInventory().getItemInMainHand().getItemMeta().getAsString(); + infoAny(player,"The item you are holding has a visible NBT String of {0} to the plugin.", tag); + getInstance().getLogger().info(tag); + } + case "component" -> { + Player player = (Player) sender; + String component = player.getInventory().getItemInMainHand().getItemMeta().getAsComponentString(); + infoAny(player,"The item you are holding has a visible Component String of {0} to the plugin.", component); + getInstance().getLogger().info(component); + } + case "itemsadder" -> { + Player player = (Player) sender; + ItemsAdderItem item; + try { + item = new ItemsAdderItem(player.getInventory().getItemInMainHand()); + } catch (IllegalArgumentException ex) { + ex.printStackTrace(); + errorAny(player,"That item is not being detected as from ItemsAdder."); + return; + } + successAny(player,"Your item has an ID of {0} in the namespace of {1}.",item.id,item.namespace); + } } } diff --git a/src/main/java/me/trouper/dupealias/server/commands/DupeCommand.java b/src/main/java/me/trouper/dupealias/server/commands/DupeCommand.java index 23c7531..79485aa 100644 --- a/src/main/java/me/trouper/dupealias/server/commands/DupeCommand.java +++ b/src/main/java/me/trouper/dupealias/server/commands/DupeCommand.java @@ -2,7 +2,6 @@ package me.trouper.dupealias.server.commands; import me.trouper.alias.server.commands.Args; import me.trouper.alias.server.commands.CommandRegistry; -import me.trouper.alias.server.commands.Permission; import me.trouper.alias.server.commands.QuickCommand; import me.trouper.alias.server.commands.completions.CompletionBuilder; import me.trouper.alias.utils.misc.Cooldown; @@ -84,7 +83,7 @@ public class DupeCommand implements QuickCommand, DupeContext { return false; } - int playerMax = getDupe().getPermissionValue(player,"dupealias.dupe.limit.",Integer.MAX_VALUE); + int playerMax = getDupe().getPermissionValue(player,"dupealias.dupe.limit.",Integer.MAX_VALUE,true); if (amount > playerMax) { warningAny(player,"Your maximum permitted dupe amplifier is {0}!", playerMax); return false; @@ -109,7 +108,7 @@ public class DupeCommand implements QuickCommand, DupeContext { dupeStack(player,toDupe,amount); - int playerCooldown = getDupe().getPermissionValue(player,"dupealias.dupe.cooldown.",getConfig().baseDupeCooldownMillis); + int playerCooldown = getDupe().getPermissionValue(player,"dupealias.dupe.cooldown.",getConfig().baseDupeCooldownMillis,false); dupeCooldown.setCooldown(player.getUniqueId(), playerCooldown); return true; diff --git a/src/main/java/me/trouper/dupealias/server/functions/UniqueCheck.java b/src/main/java/me/trouper/dupealias/server/functions/UniqueCheck.java index a8424cd..bfbb8f5 100644 --- a/src/main/java/me/trouper/dupealias/server/functions/UniqueCheck.java +++ b/src/main/java/me/trouper/dupealias/server/functions/UniqueCheck.java @@ -2,17 +2,12 @@ package me.trouper.dupealias.server.functions; import me.trouper.dupealias.server.ItemTag; import org.bukkit.inventory.ItemStack; -import org.bukkit.persistence.PersistentDataType; public class UniqueCheck implements Check { @Override public boolean passes(ItemStack input) { - boolean globallyUnique = getDupe().checkGlobalRuleTag(input,ItemTag.UNIQUE); - boolean set = input.hasItemMeta() && input.getPersistentDataContainer().has(ItemTag.UNIQUE.getKey()); - boolean individuallyUnique = Boolean.TRUE.equals(input.getPersistentDataContainer().get(ItemTag.UNIQUE.getKey(), PersistentDataType.BOOLEAN)); - - if (set && individuallyUnique) return false; - if (!set && globallyUnique) return false; + boolean isUnique = getDupe().checkEffectiveTag(input,ItemTag.UNIQUE); + if (isUnique) return false; return new ItemInventoryCheck(this).passes(input); } diff --git a/src/main/java/me/trouper/dupealias/server/gui/CommonItems.java b/src/main/java/me/trouper/dupealias/server/gui/CommonItems.java index 9b53db4..b34035e 100644 --- a/src/main/java/me/trouper/dupealias/server/gui/CommonItems.java +++ b/src/main/java/me/trouper/dupealias/server/gui/CommonItems.java @@ -5,13 +5,9 @@ import me.trouper.alias.utils.ItemBuilder; import me.trouper.dupealias.DupeAlias; import me.trouper.dupealias.DupeContext; import net.kyori.adventure.text.format.TextColor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.block.BlockState; -import org.bukkit.block.data.type.Light; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BlockStateMeta; import org.bukkit.persistence.PersistentDataType; public interface CommonItems extends DupeContext { diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/AdminPanelManager.java b/src/main/java/me/trouper/dupealias/server/gui/admin/AdminPanelManager.java index 92b6f87..672edb6 100644 --- a/src/main/java/me/trouper/dupealias/server/gui/admin/AdminPanelManager.java +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/AdminPanelManager.java @@ -1,20 +1,25 @@ package me.trouper.dupealias.server.gui.admin; -import me.trouper.alias.server.systems.gui.QuickGui; import me.trouper.alias.utils.ItemBuilder; import me.trouper.dupealias.DupeContext; import me.trouper.dupealias.data.GlobalRule; +import me.trouper.dupealias.data.ItemsAdderItem; import me.trouper.dupealias.server.ItemTag; import me.trouper.dupealias.server.gui.CommonItems; import me.trouper.dupealias.server.gui.admin.config.ConfigGui; -import me.trouper.dupealias.server.gui.admin.globalrule.*; +import me.trouper.dupealias.server.gui.admin.globalrule.GlobalRuleEditorGui; +import me.trouper.dupealias.server.gui.admin.globalrule.GlobalRuleListGui; +import me.trouper.dupealias.server.gui.admin.globalrule.criteria.*; +import me.trouper.dupealias.server.gui.admin.globalrule.criteria.regex.*; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; -import java.util.*; -import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class AdminPanelManager implements DupeContext, CommonItems { @@ -50,73 +55,6 @@ public class AdminPanelManager implements DupeContext, CommonItems { new GlobalRuleMaterialSelector(this, rule).createGUI(player).open(player); } - public void openNameCriteriaEditor(Player player, GlobalRule rule) { - QuickGui gui = QuickGui.create() - .titleMini("Name Contains") - .rows(3) - .item(13, ItemBuilder.create(Material.NAME_TAG) - .displayName("Current Pattern") - .loreMiniMessage(Arrays.asList( - "Set a regex pattern to match", - "against item display names", - "", - "Current: " + (rule.nameContainsRegex.isEmpty() ? "Not set" : rule.nameContainsRegex), - "", - "Click to set pattern", - "Right-click to clear" - )) - .build(), (g, e) -> { - if (e.isRightClick()) { - rule.nameContainsRegex = ""; - getConfig().save(); - successAny(player, "Cleared name pattern"); - openGlobalRuleEditor(player, rule); - } else { - g.requestInput(player, "modelData"); - } - }) - .item(31, BACK(), (g, e) -> openGlobalRuleEditor(player, rule)) - .fillEmpty(EMPTY()) - .callback("modelData", new QuickGui.GuiCallback() { - @Override - public void onInput(QuickGui gui, Player player, String input, QuickGui.InputSource source) { - try { - int value = Integer.parseInt(input); - if (rule.legacyModelData.contains(value)) { - infoAny(player, "Model data value {0} already exists", value); - } else { - rule.legacyModelData.add(value); - getConfig().save(); - successAny(player, "Added model data value: {0}", value); - } - } catch (NumberFormatException ex) { - errorAny(player, "Invalid number: {0}", input); - } - openModelDataEditor(player, rule); - } - }) - .build(); - - int slot = 19; - for (Integer value : rule.legacyModelData.stream().limit(5).toList()) { - gui.updateItem(slot++, ItemBuilder.create(Material.FILLED_MAP) - .displayName("Value: " + value) - .loreMiniMessage(Arrays.asList( - "Model data value", - "", - "Click to remove" - )) - .build(), (g, e) -> { - rule.legacyModelData.remove(value); - getConfig().save(); - successAny(player, "Removed model data value: {0}", value); - openModelDataEditor(player, rule); - }); - } - - gui.open(player); - } - public void openPotionEffectEditor(Player player, GlobalRule rule) { new GlobalRulePotionEffectEditor(this, rule).createGUI(player).open(player); } @@ -125,44 +63,28 @@ public class AdminPanelManager implements DupeContext, CommonItems { new GlobalRuleArmorTrimEditor(this, rule).open(player); } + public void openNameCriteriaEditor(Player player, GlobalRule rule) { + new GlobalRuleNameEditor(this,rule).open(player); + } + public void openLoreCriteriaEditor(Player player, GlobalRule rule) { - QuickGui gui = QuickGui.create() - .titleMini("Lore Contains") - .rows(3) - .item(13, ItemBuilder.create(Material.WRITABLE_BOOK) - .displayName("Current Pattern") - .loreMiniMessage(Arrays.asList( - "Set a regex pattern to match", - "against item lore lines", - "", - "Current: " + (rule.loreContainsRegex.isEmpty() ? "Not set" : rule.loreContainsRegex), - "", - "Click to set pattern", - "Right-click to clear" - )) - .build(), (g, e) -> { - if (e.isRightClick()) { - rule.loreContainsRegex = ""; - getConfig().save(); - successAny(player, "Cleared lore pattern"); - openGlobalRuleEditor(player, rule); - } else { - g.requestInput(player, "lorePattern"); - } - }) - .item(22, BACK(), (g, e) -> openGlobalRuleEditor(player, rule)) - .fillEmpty(EMPTY()) - .callback("lorePattern", new QuickGui.GuiCallback() { - @Override - public void onInput(QuickGui gui, Player player, String input, QuickGui.InputSource source) { - rule.loreContainsRegex = input; - getConfig().save(); - successAny(player, "Set lore pattern to: " + input); - openGlobalRuleEditor(player, rule); - } - }) - .build(); - gui.open(player); + new GlobalRuleLoreEditor(this,rule).open(player); + } + + public void openNbtCriteriaEditor(Player player, GlobalRule rule) { + new GlobalRuleNbtTagEditor(this,rule).open(player); + } + + public void openCompoundCriteriaEditor(Player player, GlobalRule rule) { + new GlobalRuleCompoundTagEditor(this,rule).open(player); + } + + public void openModelDataEditor(Player player, GlobalRule rule) { + new GlobalRuleModelDataEditor(this,rule).open(player); + } + + public void openItemsAdderParser(Player player, GlobalRule rule) { + new GlobalRuleItemsAdderParser(this,rule).open(player); } public void openEnchantmentEditor(Player player, GlobalRule rule) { @@ -177,51 +99,6 @@ public class AdminPanelManager implements DupeContext, CommonItems { new GlobalRuleItemFlagEditor(this, rule).open(player); } - public void openModelDataEditor(Player player, GlobalRule rule) { - QuickGui gui = QuickGui.create() - .titleMini("Model Data Values") - .rows(4) - .item(13, ItemBuilder.create(Material.COMPASS) - .displayName("Model Data Values") - .loreMiniMessage(Arrays.asList( - "Manage custom model data values", - "that items must have", - "", - "Current values: " + rule.legacyModelData.size(), - rule.legacyModelData.isEmpty() ? "" : "" + rule.legacyModelData.stream() - .limit(5) - .map(String::valueOf) - .collect(Collectors.joining(", ")), - rule.legacyModelData.size() > 5 ? "... and " + (rule.legacyModelData.size() - 5) + " more" : "", - "", - "Click to add value", - "Right-click to clear all" - )) - .build(), (g, e) -> { - if (e.isRightClick()) { - rule.legacyModelData.clear(); - getConfig().save(); - successAny(player, "Cleared all model data values"); - openModelDataEditor(player, rule); - } else { - g.requestInput(player,"namePattern"); - } - }) - .item(22, BACK(), (g, e) -> openGlobalRuleEditor(player, rule)) - .fillEmpty(EMPTY()) - .callback("namePattern", new QuickGui.GuiCallback() { - @Override - public void onInput(QuickGui gui, Player player, String input, QuickGui.InputSource source) { - rule.nameContainsRegex = input; - getConfig().save(); - successAny(player, "Set name pattern to: " + input); - openGlobalRuleEditor(player, rule); - } - }) - .build(); - gui.open(player); - } - public ItemStack createExplainedItem(ItemStack item) { if (item == null || item.isEmpty()) { return ItemBuilder.create(Material.GRAY_STAINED_GLASS_PANE) @@ -230,6 +107,11 @@ public class AdminPanelManager implements DupeContext, CommonItems { .build(); } + ItemsAdderItem iai = new ItemsAdderItem(); + try { + iai = new ItemsAdderItem(item); + } catch (IllegalArgumentException ignored) {} + List lore = new ArrayList<>(); lore.add("Held Item Explanation:"); @@ -266,6 +148,10 @@ public class AdminPanelManager implements DupeContext, CommonItems { activeTags.add(ItemTag.UNIQUE); } + if (!iai.equals(new ItemsAdderItem())) { + lore.add("• Is from ItemsAdder. (" + iai.namespace + ":" + iai.id + ")"); + } + if (lore.size() == 1) { lore.add("• No DupeAlias tags apply to this item"); } @@ -338,6 +224,4 @@ public class AdminPanelManager implements DupeContext, CommonItems { case PROTECTED -> "dark_purple"; }; } - - } \ No newline at end of file diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/MainAdminGui.java b/src/main/java/me/trouper/dupealias/server/gui/admin/MainAdminGui.java index b3e80bd..cc7cf0c 100644 --- a/src/main/java/me/trouper/dupealias/server/gui/admin/MainAdminGui.java +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/MainAdminGui.java @@ -3,7 +3,6 @@ package me.trouper.dupealias.server.gui.admin; import me.trouper.alias.server.systems.gui.QuickGui; import me.trouper.alias.utils.ItemBuilder; import me.trouper.dupealias.server.gui.CommonItems; -import me.trouper.dupealias.server.gui.admin.config.ConfigGui; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.Player; diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/config/sub/CommonConfigGui.java b/src/main/java/me/trouper/dupealias/server/gui/admin/config/sub/CommonConfigGui.java index 62546b8..937b86d 100644 --- a/src/main/java/me/trouper/dupealias/server/gui/admin/config/sub/CommonConfigGui.java +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/config/sub/CommonConfigGui.java @@ -44,7 +44,7 @@ public class CommonConfigGui implements DupeContext, CommonItems { errorAny(p, "Invalid hex color format. Use RRGGBB (e.g., AAAAFF)."); } }) - .item(10, ItemBuilder.create(Material.BLUE_WOOL) + .item(11, ItemBuilder.create(Material.BLUE_WOOL) .displayName("Main Color") .loreMiniMessage(List.of( "The color for the message border.", @@ -69,7 +69,7 @@ public class CommonConfigGui implements DupeContext, CommonItems { errorAny(p, "Invalid hex color format. Use RRGGBB (e.g., 00DDFF)."); } }) - .item(11, ItemBuilder.create(Material.CYAN_WOOL) + .item(12, ItemBuilder.create(Material.CYAN_WOOL) .displayName("Secondary Color") .loreMiniMessage(List.of( "The color used for the plugin's name.", @@ -88,7 +88,7 @@ public class CommonConfigGui implements DupeContext, CommonItems { successAny(p, "Plugin name set to: {0}", input); open(p); }) - .item(12, ItemBuilder.create(Material.NAME_TAG) + .item(13, ItemBuilder.create(Material.NAME_TAG) .displayName("Plugin Name") .loreMiniMessage(List.of( "The name of the plugin displayed in messages.", @@ -107,7 +107,7 @@ public class CommonConfigGui implements DupeContext, CommonItems { successAny(p, "Flat prefix set to: {0}", input); open(p); }) - .item(13, ItemBuilder.create(Material.PAPER) + .item(14, ItemBuilder.create(Material.PAPER) .displayName("Flat Prefix") .loreMiniMessage(List.of( "The prefix used when 'flat' mode is enabled.", @@ -120,7 +120,7 @@ public class CommonConfigGui implements DupeContext, CommonItems { getDupe().getGuiListener().requestChatInput(g, player, "flat_prefix", "Enter the new flat prefix.")) - .item(14, ItemBuilder.create(config.flat ? Material.LIME_DYE : Material.GRAY_DYE) + .item(15, ItemBuilder.create(config.flat ? Material.LIME_DYE : Material.GRAY_DYE) .displayName("Flat Mode") .loreMiniMessage(List.of( "If true, uses the simple flat message system", diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleEditorGui.java b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleEditorGui.java index a78f820..85ef60f 100644 --- a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleEditorGui.java +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleEditorGui.java @@ -15,7 +15,6 @@ import org.bukkit.inventory.ItemStack; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; public class GlobalRuleEditorGui implements DupeContext, CommonItems { @@ -33,84 +32,75 @@ public class GlobalRuleEditorGui implements DupeContext, CommonItems { .rows(6) .fillBorder(EMPTY(Material.ORANGE_STAINED_GLASS_PANE)) - // Back button + // Back button top-left .item(0, BACK(), (g, e) -> manager.openGlobalRuleList(player)) - // Applied Tags Section - .item(10, createTagItem(ItemTag.UNIQUE), - (g, e) -> toggleTag(player, ItemTag.UNIQUE)) - .item(11, createTagItem(ItemTag.FINAL), - (g, e) -> toggleTag(player, ItemTag.FINAL)) - .item(12, createTagItem(ItemTag.INFINITE), - (g, e) -> toggleTag(player, ItemTag.INFINITE)) - .item(13, createTagItem(ItemTag.PROTECTED), - (g, e) -> toggleTag(player, ItemTag.PROTECTED)) + // Item Tags + .item(10, createTagItem(ItemTag.UNIQUE), (g, e) -> toggleTag(player, ItemTag.UNIQUE)) + .item(11, createTagItem(ItemTag.FINAL), (g, e) -> toggleTag(player, ItemTag.FINAL)) + .item(12, createTagItem(ItemTag.INFINITE), (g, e) -> toggleTag(player, ItemTag.INFINITE)) + .item(13, createTagItem(ItemTag.PROTECTED), (g, e) -> toggleTag(player, ItemTag.PROTECTED)) - // Match Mode - .item(16, createMatchModeItem(), - (g, e) -> cycleMatchMode(player)) + // Material Matching + .item(16, createMaterialListItem(), (g, e) -> { + if (rule.materialMode != GlobalRule.MaterialMatchMode.IGNORE) + manager.openMaterialSelector(player, rule); + }) + .item(25, createItemsAdderListItem(), (g, e) -> { + if (rule.materialMode != GlobalRule.MaterialMatchMode.IGNORE) + manager.openItemsAdderParser(player, rule); + }) + .item(34, createMaterialModeItem(), (g, e) -> cycleMaterialMode(player)) - // Criteria Items - .item(19, createCriteriaItem("Name Regex", Material.NAME_TAG, + // Criteria Match Mode + .item(43, createMatchModeItem(), (g, e) -> cycleMatchMode(player)) + + // Criteria Section + .item(28, createCriteriaItem("Name Regex", Material.NAME_TAG, !rule.nameContainsRegex.isEmpty(), rule.nameContainsRegex), (g, e) -> manager.openNameCriteriaEditor(player, rule)) - .item(20, createCriteriaItem("Lore Regex", Material.WRITABLE_BOOK, + .item(29, createCriteriaItem("Lore Regex", Material.WRITABLE_BOOK, !rule.loreContainsRegex.isEmpty(), rule.loreContainsRegex), (g, e) -> manager.openLoreCriteriaEditor(player, rule)) - .item(21, createCriteriaItem("Enchantments", Material.ENCHANTED_BOOK, + .item(30, createCriteriaItem("NBT Regex", Material.BOOK, + !rule.nbtTagContainsRegex.isEmpty(), rule.nbtTagContainsRegex), + (g, e) -> manager.openNbtCriteriaEditor(player, rule)) + + .item(31, createCriteriaItem("Compound Regex", Material.BOOKSHELF, + !rule.compoundTagContainsRegex.isEmpty(), rule.compoundTagContainsRegex), + (g, e) -> manager.openCompoundCriteriaEditor(player, rule)) + + .item(32, createCriteriaItem("Model Data", Material.COMPASS, + !rule.legacyModelData.isEmpty(), rule.legacyModelData.size() + " values"), + + (g, e) -> manager.openModelDataEditor(player, rule)) + .item(37, createCriteriaItem("Enchantments", Material.ENCHANTED_BOOK, !rule.enchantments.isEmpty(), rule.enchantments.size() + " enchants"), (g, e) -> manager.openEnchantmentEditor(player, rule)) - .item(22, createCriteriaItem("Attributes", Material.GOLDEN_APPLE, + .item(38, createCriteriaItem("Attributes", Material.GOLDEN_APPLE, !rule.attributes.isEmpty(), rule.attributes.size() + " attributes"), (g, e) -> manager.openAttributeEditor(player, rule)) - .item(23, createCriteriaItem("Item Flags", Material.WHITE_BANNER, + .item(39, createCriteriaItem("Item Flags", Material.WHITE_BANNER, !rule.itemFlags.isEmpty(), rule.itemFlags.size() + " flags"), (g, e) -> manager.openItemFlagEditor(player, rule)) - .item(24, createCriteriaItem("Model Data", Material.COMPASS, - !rule.legacyModelData.isEmpty(), rule.legacyModelData.size() + " values"), - (g, e) -> manager.openModelDataEditor(player, rule)) - - .item(25, createCriteriaItem("Potion Effects", Material.POTION, + .item(40, createCriteriaItem("Potion Effects", Material.POTION, !rule.potionEffects.isEmpty(), rule.potionEffects.size() + " effects"), (g, e) -> manager.openPotionEffectEditor(player, rule)) - // Material Settings - .item(30, createMaterialModeItem(), - (g, e) -> cycleMaterialMode(player)) - - .item(31, createMaterialListItem(), - (g, e) -> { - if (rule.materialMode != GlobalRule.MaterialMatchMode.IGNORE) { - manager.openMaterialSelector(player, rule); - } - }) - - // Armor Trim - .item(32, createCriteriaItem("Armor Trim", Material.NETHERITE_CHESTPLATE, + .item(41, createCriteriaItem("Armor Trim", Material.NETHERITE_CHESTPLATE, !rule.trimPatterns.isEmpty() || !rule.trimMaterials.isEmpty(), (rule.trimPatterns.size() + rule.trimMaterials.size()) + " selected"), (g, e) -> manager.openArmorTrimEditor(player, rule)) - // Save button - .item(40, ItemBuilder.create(Material.LIME_DYE) - .displayName("Save & Return") - .loreMiniMessage(Arrays.asList( - "Save changes and return", - "to the rule list", - "", - "Click to save" - )) - .build(), (g, e) -> { + .onClose((g, e) -> { getConfig().save(); successAny(player, "Saved global rule"); - manager.openGlobalRuleList(player); }) - .fillEmpty(EMPTY()) .clickSound(Sound.UI_BUTTON_CLICK, 0.7f, 1.2f) .build(); @@ -118,6 +108,8 @@ public class GlobalRuleEditorGui implements DupeContext, CommonItems { gui.open(player); } + + private ItemStack createTagItem(ItemTag tag) { boolean active = rule.appliedTags.contains(tag); Material material = switch (tag) { @@ -217,8 +209,8 @@ public class GlobalRuleEditorGui implements DupeContext, CommonItems { lore.add(""); List materialNames = rule.effectedMaterials.stream() .limit(5) - .map(mat -> mat.name()) - .collect(Collectors.toList()); + .map(Enum::name) + .toList(); for (String mat : materialNames) { lore.add("• " + mat); @@ -238,6 +230,51 @@ public class GlobalRuleEditorGui implements DupeContext, CommonItems { .build(); } + private ItemStack createItemsAdderListItem() { + if (rule.materialMode == GlobalRule.MaterialMatchMode.IGNORE) { + return ItemBuilder.create(Material.GRAY_DYE) + .displayName("ItemsAdder List") + .loreMiniMessage(Arrays.asList( + "Set material mode to", + "WHITELIST or BLACKLIST", + "to configure materials" + )) + .build(); + } + + List lore = new ArrayList<>(); + lore.add("Manage custom materials for this rule"); + lore.add(""); + lore.add("Selected: " + rule.effectedItemsAdderMaterials.size() + " Custom Materials"); + + if (!rule.effectedMaterials.isEmpty()) { + lore.add(""); + List items = new ArrayList<>(); + + rule.effectedItemsAdderMaterials.stream() + .limit(5) + .forEach((iai)->{ + items.add("" + iai.namespace + ":" + iai.id); + }); + + for (String mat : items) { + lore.add("• " + mat); + } + + if (rule.effectedMaterials.size() > 5) { + lore.add("... and " + (rule.effectedMaterials.size() - 5) + " more"); + } + } + + lore.add(""); + lore.add("Click to manage"); + + return ItemBuilder.create(Material.CHEST) + .displayName("ItemsAdder List") + .loreMiniMessage(lore) + .build(); + } + private void toggleTag(Player player, ItemTag tag) { if (rule.appliedTags.contains(tag)) { rule.appliedTags.remove(tag); diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleArmorTrimEditor.java b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleArmorTrimEditor.java similarity index 98% rename from src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleArmorTrimEditor.java rename to src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleArmorTrimEditor.java index c7e94b8..aab02f9 100644 --- a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleArmorTrimEditor.java +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleArmorTrimEditor.java @@ -1,4 +1,4 @@ -package me.trouper.dupealias.server.gui.admin.globalrule; +package me.trouper.dupealias.server.gui.admin.globalrule.criteria; import me.trouper.alias.data.enums.ValidTrimMaterial; import me.trouper.alias.data.enums.ValidTrimPattern; @@ -13,7 +13,6 @@ import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemFlag; import java.util.Arrays; import java.util.List; diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleAttributeEditor.java b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleAttributeEditor.java similarity index 98% rename from src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleAttributeEditor.java rename to src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleAttributeEditor.java index b593d8f..bcf010c 100644 --- a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleAttributeEditor.java +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleAttributeEditor.java @@ -1,4 +1,4 @@ -package me.trouper.dupealias.server.gui.admin.globalrule; +package me.trouper.dupealias.server.gui.admin.globalrule.criteria; import me.trouper.alias.data.enums.ValidAttribute; import me.trouper.alias.server.systems.gui.QuickGui; @@ -13,7 +13,6 @@ import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.ItemStack; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleEnchantmentEditor.java b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleEnchantmentEditor.java similarity index 98% rename from src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleEnchantmentEditor.java rename to src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleEnchantmentEditor.java index 3c7cf3c..1be2308 100644 --- a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleEnchantmentEditor.java +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleEnchantmentEditor.java @@ -1,4 +1,4 @@ -package me.trouper.dupealias.server.gui.admin.globalrule; +package me.trouper.dupealias.server.gui.admin.globalrule.criteria; import me.trouper.alias.data.enums.ValidEnchantment; import me.trouper.alias.server.systems.gui.QuickGui; @@ -13,7 +13,9 @@ import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.ItemStack; -import java.util.*; +import java.util.Arrays; +import java.util.List; +import java.util.Set; public class GlobalRuleEnchantmentEditor extends QuickPaginatedGUI implements DupeContext, CommonItems { diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleItemFlagEditor.java b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleItemFlagEditor.java similarity index 98% rename from src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleItemFlagEditor.java rename to src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleItemFlagEditor.java index 8a5c872..cb8ae30 100644 --- a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleItemFlagEditor.java +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleItemFlagEditor.java @@ -1,4 +1,4 @@ -package me.trouper.dupealias.server.gui.admin.globalrule; +package me.trouper.dupealias.server.gui.admin.globalrule.criteria; import me.trouper.alias.server.systems.gui.QuickGui; import me.trouper.alias.utils.ItemBuilder; diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleItemsAdderParser.java b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleItemsAdderParser.java new file mode 100644 index 0000000..3d03d7f --- /dev/null +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleItemsAdderParser.java @@ -0,0 +1,121 @@ +package me.trouper.dupealias.server.gui.admin.globalrule.criteria; + +import me.trouper.alias.server.systems.gui.QuickGui; +import me.trouper.alias.utils.ItemBuilder; +import me.trouper.dupealias.DupeContext; +import me.trouper.dupealias.data.GlobalRule; +import me.trouper.dupealias.data.ItemsAdderItem; +import me.trouper.dupealias.server.gui.CommonItems; +import me.trouper.dupealias.server.gui.admin.AdminPanelManager; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; + +public class GlobalRuleItemsAdderParser implements DupeContext, CommonItems { + private final AdminPanelManager manager; + private final GlobalRule rule; + + public GlobalRuleItemsAdderParser(AdminPanelManager manager, GlobalRule rule) { + this.manager = manager; + this.rule = rule; + } + + public void open(Player player) { + QuickGui gui = QuickGui.create() + .rows(6) + .titleMini("ItemsAdder Parsing Menu") + .allowDrag() + .onGlobalClick((g,e)->{ + if (e.getSlot() >= 45) return; + e.setCancelled(false); + }) + .fillSlots(EMPTY(),null,46,48,49,50,52) + .item(45, BACK(), (g,e) -> manager.openGlobalRuleEditor(player,rule)) + .item(53, ItemBuilder.create(Material.LIGHT) + .displayName("Information") + .loreMiniMessage( + "Add custom items to this GUI and", + "click the green button to have", + "their namespaces parsed." + ) + .build()) + .item(51,ItemBuilder.create(Material.LIME_DYE) + .displayName("Parse Items") + .loreMiniMessage( + "Click to add all items", + "to the ItemsAdder material list" + ) + .build(), + (g,e) -> { + Inventory inv = g.getInventory(); + for (int row = 0; row < 5; row++) { + for (int col = 0; col < 9; col++) { + int index = row * 9 + col; + ItemStack item = inv.getItem(index); + if (item == null || item.isEmpty() || !item.hasItemMeta()) continue; + ItemsAdderItem iai; + try { + iai = new ItemsAdderItem(item); + } catch (IllegalArgumentException ignored) { + continue; + } + if (rule.effectedItemsAdderMaterials.contains(iai)) continue; + rule.effectedItemsAdderMaterials.add(iai); + inv.setItem(index,new ItemStack(Material.AIR)); + } + } + getConfig().save(); + }) + .item(47,generateItemsAdderListItem(rule),(g,e)->{ + switch (e.getClick()) { + case SHIFT_RIGHT, SHIFT_LEFT -> { + rule.effectedItemsAdderMaterials.clear(); + getConfig().save(); + open(player); + } + } + }) + .onClose((g,e)->{ + getConfig().save(); + }) + .build(); + + gui.open(player); + } + + public ItemStack generateItemsAdderListItem(GlobalRule rule) { + List lore = new ArrayList<>(); + ItemBuilder builder = ItemBuilder.create(Material.PAPER) + .displayName("Current Items"); + + lore.add("Total: " + rule.effectedItemsAdderMaterials.size()); + if (!rule.effectedMaterials.isEmpty()) { + lore.add(""); + List items = new ArrayList<>(); + + rule.effectedItemsAdderMaterials.stream() + .limit(5) + .forEach((iai)->{ + items.add("" + iai.namespace + ":" + iai.id); + }); + + for (String mat : items) { + lore.add("• " + mat); + } + + if (rule.effectedMaterials.size() > 5) { + lore.add("... and " + (rule.effectedMaterials.size() - 5) + " more"); + } + } + + if (rule.effectedItemsAdderMaterials.size() > 5) lore.add("and %s more...".formatted(rule.effectedItemsAdderMaterials.size() - 5)); + + lore.add("Shift Click to clear"); + + return builder.loreMiniMessage(lore).build(); + } +} diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleMaterialSelector.java b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleMaterialSelector.java similarity index 97% rename from src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleMaterialSelector.java rename to src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleMaterialSelector.java index e47e308..5873f6d 100644 --- a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRuleMaterialSelector.java +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRuleMaterialSelector.java @@ -1,4 +1,4 @@ -package me.trouper.dupealias.server.gui.admin.globalrule; +package me.trouper.dupealias.server.gui.admin.globalrule.criteria; import me.trouper.alias.server.systems.gui.QuickGui; import me.trouper.alias.server.systems.gui.QuickPaginatedGUI; @@ -11,10 +11,11 @@ import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; -import java.util.*; +import java.util.Arrays; +import java.util.List; +import java.util.Set; public class GlobalRuleMaterialSelector extends QuickPaginatedGUI implements DupeContext, CommonItems { diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRulePotionEffectEditor.java b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRulePotionEffectEditor.java similarity index 98% rename from src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRulePotionEffectEditor.java rename to src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRulePotionEffectEditor.java index 4d1ce72..abd964b 100644 --- a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/GlobalRulePotionEffectEditor.java +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/GlobalRulePotionEffectEditor.java @@ -1,4 +1,4 @@ -package me.trouper.dupealias.server.gui.admin.globalrule; +package me.trouper.dupealias.server.gui.admin.globalrule.criteria; import me.trouper.alias.data.enums.ValidPotionEffectType; import me.trouper.alias.server.systems.gui.QuickGui; @@ -13,7 +13,9 @@ import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.ItemStack; -import java.util.*; +import java.util.Arrays; +import java.util.List; +import java.util.Set; public class GlobalRulePotionEffectEditor extends QuickPaginatedGUI implements DupeContext, CommonItems { diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleCompoundTagEditor.java b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleCompoundTagEditor.java new file mode 100644 index 0000000..933e793 --- /dev/null +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleCompoundTagEditor.java @@ -0,0 +1,64 @@ +package me.trouper.dupealias.server.gui.admin.globalrule.criteria.regex; + +import me.trouper.alias.server.systems.gui.QuickGui; +import me.trouper.alias.utils.ItemBuilder; +import me.trouper.dupealias.DupeContext; +import me.trouper.dupealias.data.GlobalRule; +import me.trouper.dupealias.server.gui.CommonItems; +import me.trouper.dupealias.server.gui.admin.AdminPanelManager; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.Arrays; + +public class GlobalRuleCompoundTagEditor implements DupeContext, CommonItems { + private final AdminPanelManager manager; + private final GlobalRule rule; + + public GlobalRuleCompoundTagEditor(AdminPanelManager manager, GlobalRule rule) { + this.manager = manager; + this.rule = rule; + } + + public void open(Player player) { + QuickGui gui = QuickGui.create() + .titleMini("Lore Contains") + .rows(3) + .item(0, BACK(), (g, e) -> manager.openGlobalRuleEditor(player, rule)) + .callback("compoundPattern", new QuickGui.GuiCallback() { + @Override + public void onInput(QuickGui gui, Player player, String input, QuickGui.InputSource source) { + rule.compoundTagContainsRegex = input; + getConfig().save(); + successAny(player, "Set lore pattern to: " + input); + manager.openGlobalRuleEditor(player, rule); + } + }) + .item(13, ItemBuilder.create(Material.WRITABLE_BOOK) + .displayName("Current Pattern") + .loreMiniMessage(Arrays.asList( + "Set a regex pattern to match", + "against the item's compound tag", + "", + "Current: " + (rule.loreContainsRegex.isEmpty() ? "Not set" : rule.loreContainsRegex), + "", + "Click to set pattern", + "Right-click to clear" + )) + .build(), (g, e) -> { + if (e.isRightClick()) { + rule.nbtTagContainsRegex = ""; + getConfig().save(); + successAny(player, "Cleared lore pattern"); + manager.openGlobalRuleEditor(player, rule); + } else { + getDupe().getGuiListener().requestChatInput(g,player,"compoundPattern","Input a regex pattern for matching item compound tags."); + } + }) + + .fillEmpty(EMPTY()) + + .build(); + gui.open(player); + } +} diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleLoreEditor.java b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleLoreEditor.java new file mode 100644 index 0000000..330fec5 --- /dev/null +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleLoreEditor.java @@ -0,0 +1,64 @@ +package me.trouper.dupealias.server.gui.admin.globalrule.criteria.regex; + +import me.trouper.alias.server.systems.gui.QuickGui; +import me.trouper.alias.utils.ItemBuilder; +import me.trouper.dupealias.DupeContext; +import me.trouper.dupealias.data.GlobalRule; +import me.trouper.dupealias.server.gui.CommonItems; +import me.trouper.dupealias.server.gui.admin.AdminPanelManager; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.Arrays; + +public class GlobalRuleLoreEditor implements DupeContext, CommonItems { + private final AdminPanelManager manager; + private final GlobalRule rule; + + public GlobalRuleLoreEditor(AdminPanelManager manager, GlobalRule rule) { + this.manager = manager; + this.rule = rule; + } + + public void open(Player player) { + QuickGui gui = QuickGui.create() + .titleMini("Lore Contains") + .rows(3) + .item(0, BACK(), (g, e) -> manager.openGlobalRuleEditor(player, rule)) + .callback("lorePattern", new QuickGui.GuiCallback() { + @Override + public void onInput(QuickGui gui, Player player, String input, QuickGui.InputSource source) { + rule.loreContainsRegex = input; + getConfig().save(); + successAny(player, "Set lore pattern to: " + input); + manager.openGlobalRuleEditor(player, rule); + } + }) + .item(13, ItemBuilder.create(Material.WRITABLE_BOOK) + .displayName("Current Pattern") + .loreMiniMessage(Arrays.asList( + "Set a regex pattern to match", + "against item lore lines", + "", + "Current: " + (rule.loreContainsRegex.isEmpty() ? "Not set" : rule.loreContainsRegex), + "", + "Click to set pattern", + "Right-click to clear" + )) + .build(), (g, e) -> { + if (e.isRightClick()) { + rule.loreContainsRegex = ""; + getConfig().save(); + successAny(player, "Cleared lore pattern"); + manager.openGlobalRuleEditor(player, rule); + } else { + getDupe().getGuiListener().requestChatInput(g,player,"lorePattern","Input a regex pattern for matching item lore (by line)."); + } + }) + + .fillEmpty(EMPTY()) + + .build(); + gui.open(player); + } +} diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleModelDataEditor.java b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleModelDataEditor.java new file mode 100644 index 0000000..594d934 --- /dev/null +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleModelDataEditor.java @@ -0,0 +1,96 @@ +package me.trouper.dupealias.server.gui.admin.globalrule.criteria.regex; + +import me.trouper.alias.server.systems.gui.QuickGui; +import me.trouper.alias.utils.ItemBuilder; +import me.trouper.dupealias.DupeContext; +import me.trouper.dupealias.data.GlobalRule; +import me.trouper.dupealias.server.gui.CommonItems; +import me.trouper.dupealias.server.gui.admin.AdminPanelManager; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class GlobalRuleModelDataEditor implements DupeContext, CommonItems { + private final AdminPanelManager manager; + private final GlobalRule rule; + + public GlobalRuleModelDataEditor(AdminPanelManager manager, GlobalRule rule) { + this.manager = manager; + this.rule = rule; + } + + public void open(Player player) { + QuickGui gui = QuickGui.create() + .titleMini("Model Data Values") + .rows(5) + .item(0, BACK(), (g, e) -> manager.openGlobalRuleEditor(player, rule)) + .fillSlots(EMPTY(Material.GRAY_STAINED_GLASS_PANE),null,28,29,30,31,32,33,34) + .callback("modelData", new QuickGui.GuiCallback() { + @Override + public void onInput(QuickGui gui, Player player, String input, QuickGui.InputSource source) { + try { + int value = Integer.parseInt(input); + if (rule.legacyModelData.contains(value)) { + infoAny(player, "Model data value {0} already exists", value); + } else { + rule.legacyModelData.add(value); + getConfig().save(); + successAny(player, "Added model data value: {0}", value); + } + } catch (NumberFormatException ex) { + errorAny(player, "Invalid number: {0}", input); + } + open(player); + } + }) + .item(13, ItemBuilder.create(Material.COMPASS) + .displayName("Model Data Values") + .loreMiniMessage(Arrays.asList( + "Manage custom model data values", + "that items must have", + "", + "Current values: " + rule.legacyModelData.size(), + rule.legacyModelData.isEmpty() ? "" : "" + rule.legacyModelData.stream() + .limit(5) + .map(String::valueOf) + .collect(Collectors.joining(", ")), + rule.legacyModelData.size() > 5 ? "... and " + (rule.legacyModelData.size() - 5) + " more" : "", + "", + "Click to add value", + "Right-click to clear all" + )) + .build(), (g, e) -> { + if (e.isRightClick()) { + rule.legacyModelData.clear(); + getConfig().save(); + successAny(player, "Cleared all model data values"); + open(player); + } else { + getDupe().getGuiListener().requestChatInput(g,player,"modelData","Input the the legacy model data ID number."); + } + }) + .fillEmpty(EMPTY()) + .build(); + + int slot = 28; + for (Integer value : rule.legacyModelData.stream().limit(7).toList()) { + gui.updateItem(slot++, ItemBuilder.create(Material.FILLED_MAP) + .displayName("Value: " + value) + .loreMiniMessage(Arrays.asList( + "Model data value", + "", + "Click to remove" + )) + .build(), (g, e) -> { + rule.legacyModelData.remove(value); + getConfig().save(); + successAny(player, "Removed model data value: {0}", value); + open(player); + }); + } + + gui.open(player); + } +} diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleNameEditor.java b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleNameEditor.java new file mode 100644 index 0000000..7cb8c9f --- /dev/null +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleNameEditor.java @@ -0,0 +1,63 @@ +package me.trouper.dupealias.server.gui.admin.globalrule.criteria.regex; + +import me.trouper.alias.server.systems.gui.QuickGui; +import me.trouper.alias.utils.ItemBuilder; +import me.trouper.dupealias.DupeContext; +import me.trouper.dupealias.data.GlobalRule; +import me.trouper.dupealias.server.gui.CommonItems; +import me.trouper.dupealias.server.gui.admin.AdminPanelManager; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.Arrays; + +public class GlobalRuleNameEditor implements DupeContext, CommonItems { + private final AdminPanelManager manager; + private final GlobalRule rule; + + public GlobalRuleNameEditor(AdminPanelManager manager, GlobalRule rule) { + this.manager = manager; + this.rule = rule; + } + + public void open(Player player) { + QuickGui gui = QuickGui.create() + .titleMini("Name Contains") + .rows(3) + .item(0, BACK(), (g, e) -> manager.openGlobalRuleEditor(player, rule)) + .callback("namePattern", new QuickGui.GuiCallback() { + @Override + public void onInput(QuickGui gui, Player player, String input, QuickGui.InputSource source) { + rule.nameContainsRegex = input; + getConfig().save(); + successAny(player, "Set name pattern to: " + input); + manager.openGlobalRuleEditor(player, rule); + } + }) + .item(13, ItemBuilder.create(Material.NAME_TAG) + .displayName("Current Pattern") + .loreMiniMessage(Arrays.asList( + "Set a regex pattern to match", + "against item display names", + "", + "Current: " + (rule.nameContainsRegex.isEmpty() ? "Not set" : rule.nameContainsRegex), + "", + "Click to set pattern", + "Right-click to clear" + )) + .build(), (g, e) -> { + if (e.isRightClick()) { + rule.nameContainsRegex = ""; + getConfig().save(); + successAny(player, "Cleared name pattern"); + manager.openGlobalRuleEditor(player, rule); + } else { + getDupe().getGuiListener().requestChatInput(g,player,"namePattern","Input a regex pattern for matching item names."); + } + }) + .fillEmpty(EMPTY()) + .build(); + + gui.open(player); + } +} diff --git a/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleNbtTagEditor.java b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleNbtTagEditor.java new file mode 100644 index 0000000..335e167 --- /dev/null +++ b/src/main/java/me/trouper/dupealias/server/gui/admin/globalrule/criteria/regex/GlobalRuleNbtTagEditor.java @@ -0,0 +1,64 @@ +package me.trouper.dupealias.server.gui.admin.globalrule.criteria.regex; + +import me.trouper.alias.server.systems.gui.QuickGui; +import me.trouper.alias.utils.ItemBuilder; +import me.trouper.dupealias.DupeContext; +import me.trouper.dupealias.data.GlobalRule; +import me.trouper.dupealias.server.gui.CommonItems; +import me.trouper.dupealias.server.gui.admin.AdminPanelManager; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.Arrays; + +public class GlobalRuleNbtTagEditor implements DupeContext, CommonItems { + private final AdminPanelManager manager; + private final GlobalRule rule; + + public GlobalRuleNbtTagEditor(AdminPanelManager manager, GlobalRule rule) { + this.manager = manager; + this.rule = rule; + } + + public void open(Player player) { + QuickGui gui = QuickGui.create() + .titleMini("NBT Tag Contains") + .rows(3) + .item(0, BACK(), (g, e) -> manager.openGlobalRuleEditor(player, rule)) + .callback("nbtPattern", new QuickGui.GuiCallback() { + @Override + public void onInput(QuickGui gui, Player player, String input, QuickGui.InputSource source) { + rule.nbtTagContainsRegex = input; + getConfig().save(); + successAny(player, "Set NBT pattern to: " + input); + manager.openGlobalRuleEditor(player, rule); + } + }) + .item(13, ItemBuilder.create(Material.WRITABLE_BOOK) + .displayName("Current Pattern") + .loreMiniMessage(Arrays.asList( + "Set a regex pattern to match", + "against mojangson NBT", + "", + "Current: " + (rule.loreContainsRegex.isEmpty() ? "Not set" : rule.loreContainsRegex), + "", + "Click to set pattern", + "Right-click to clear" + )) + .build(), (g, e) -> { + if (e.isRightClick()) { + rule.nbtTagContainsRegex = ""; + getConfig().save(); + successAny(player, "Cleared lore pattern"); + manager.openGlobalRuleEditor(player, rule); + } else { + getDupe().getGuiListener().requestChatInput(g,player,"nbtPattern","Input a regex pattern for matching nbt (mojangson)."); + } + }) + + .fillEmpty(EMPTY()) + + .build(); + gui.open(player); + } +} diff --git a/src/main/java/me/trouper/dupealias/server/gui/dupe/sub/DupeChestGui.java b/src/main/java/me/trouper/dupealias/server/gui/dupe/sub/DupeChestGui.java index 7d4e081..7dedbfb 100644 --- a/src/main/java/me/trouper/dupealias/server/gui/dupe/sub/DupeChestGui.java +++ b/src/main/java/me/trouper/dupealias/server/gui/dupe/sub/DupeChestGui.java @@ -21,7 +21,7 @@ public class DupeChestGui extends AbstractDupeGui { @Override public ChestSession getSession(Player player) { ChestSession session = super.getSession(player); - session.setDelayTicks(getDupe().getPermissionValue(player, "dupealias.gui.chest.refresh.", getConfig().chest.baseRefreshDelayTicks)); + session.setDelayTicks(getDupe().getPermissionValue(player, "dupealias.gui.chest.refresh.", getConfig().chest.baseRefreshDelayTicks,false)); session.open(); return session; } @@ -32,7 +32,7 @@ public class DupeChestGui extends AbstractDupeGui { public ChestSession(Player owner) { super(owner, "DUPE CHEST", 6); - this.delayTicks = getDupe().getPermissionValue(owner, "dupealias.gui.chest.refresh.", getConfig().chest.baseRefreshDelayTicks); + this.delayTicks = getDupe().getPermissionValue(owner, "dupealias.gui.chest.refresh.", getConfig().chest.baseRefreshDelayTicks,false); } @Override diff --git a/src/main/java/me/trouper/dupealias/server/gui/dupe/sub/DupeInventoryGui.java b/src/main/java/me/trouper/dupealias/server/gui/dupe/sub/DupeInventoryGui.java index 288b768..203d8f3 100644 --- a/src/main/java/me/trouper/dupealias/server/gui/dupe/sub/DupeInventoryGui.java +++ b/src/main/java/me/trouper/dupealias/server/gui/dupe/sub/DupeInventoryGui.java @@ -18,7 +18,7 @@ public class DupeInventoryGui extends AbstractDupeGuiYOUR INVENTORY", 6); - this.delayTicks = getDupe().getPermissionValue(owner, "dupealias.gui.inventory.refresh.", getConfig().inventory.baseRefreshDelayTicks); + this.delayTicks = getDupe().getPermissionValue(owner, "dupealias.gui.inventory.refresh.", getConfig().inventory.baseRefreshDelayTicks,false); } @Override diff --git a/src/main/java/me/trouper/dupealias/server/gui/dupe/sub/DupeReplicatorGui.java b/src/main/java/me/trouper/dupealias/server/gui/dupe/sub/DupeReplicatorGui.java index b5c9216..5d13020 100644 --- a/src/main/java/me/trouper/dupealias/server/gui/dupe/sub/DupeReplicatorGui.java +++ b/src/main/java/me/trouper/dupealias/server/gui/dupe/sub/DupeReplicatorGui.java @@ -30,8 +30,8 @@ public class DupeReplicatorGui extends AbstractDupeGuiREPLICATOR", 3); getVerbose().send("Creating a new replicator with input of {0}", input.getType().name()); setInput(input); - this.delayTicks = getDupe().getPermissionValue(owner, "dupealias.gui.replicator.refresh.", getConfig().replicator.baseRefreshDelayTicks); - this.cooldownTicks = getDupe().getPermissionValue(owner, "dupealias.gui.replicator.cooldown.", getConfig().replicator.baseInputCooldownTicks); + this.delayTicks = getDupe().getPermissionValue(owner, "dupealias.gui.replicator.refresh.", getConfig().replicator.baseRefreshDelayTicks,false); + this.cooldownTicks = getDupe().getPermissionValue(owner, "dupealias.gui.replicator.cooldown.", getConfig().replicator.baseInputCooldownTicks,false); } @Override diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 2cc0879..007f1a7 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -35,8 +35,8 @@ permissions: description: Allows duplication of items through the command. Setting this to false results in the /dupe command always displaying a GUI. default: true children: - dupealias.dupe.cooldown.integerhere: false # Controls the cooldown time in ticks it will take to command dupe again. - dupealias.dupe.limit.integerhere: false # Controls the integer argument's limit for exponential (2^n) duping. + dupealias.dupe.cooldown.integerhere: false # Controls the cooldown time in milliseconds it will take to command dupe again. Always takes the lowest number on a permission holder. + dupealias.dupe.limit.integerhere: false # Controls the integer argument's limit for exponential (2^n) duping. Always takes the highest is number on a permission holder. dupealias.gui: description: Allows access to the main dupe GUI selector. Players do not need this permission to access subsequent GUIs through the dupe command arguments. default: true @@ -50,6 +50,7 @@ permissions: children: dupealias.gui.replicator.keep: false # Controls if a player should keep their previous replicator session with items in it. This does not persist across reboots. dupealias.gui.replicator.refresh.integerhere: false # Controls the time in ticks it will take a duplicated item to refill or refresh in the GUI. Always takes the lowest number on a permission holder. + dupealias.gui.replicator.cooldown.integerhere: false # Controls the time in milliseconds between input updates. dupealias.gui.inventory: description: The gui which shows your inventory and armor on top. default: true