Added README.md and updated the global rule guis.
This commit is contained in:
327
Documentation.md
327
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
|
||||
@@ -160,7 +33,6 @@ DupeAlias is actively maintained with regular updates and feature additions. Joi
|
||||
4. Set up your first global rules or start tagging items manually
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Item Tags
|
||||
@@ -179,7 +51,6 @@ Individual item tags **always override** global rules. This allows for fine-grai
|
||||
- **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
|
||||
```
|
||||
|
||||
``` 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
|
||||
```
|
||||
|
||||
``` 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
|
||||
```
|
||||
|
||||
``` 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
|
||||
```
|
||||
|
||||
``` 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
|
||||
```
|
||||
|
||||
``` 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
|
||||
```
|
||||
|
||||
``` Example: 12345 → matches items with CustomModelData: 12345 ```
|
||||
#### Item Flags
|
||||
Match items with specific visibility flags.
|
||||
```
|
||||
Example: HIDE_ENCHANTS → matches items that hide enchantments
|
||||
```
|
||||
|
||||
``` 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
|
||||
```
|
||||
|
||||
``` 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
|
||||
**Permissions**:
|
||||
- `dupealias.gui.replicator`: Main Access
|
||||
- `dupealias.gui.replicator.cooldown.<integer>`: Sets the input swap cooldown time (milliseconds).
|
||||
- `dupealias.gui.replicator.refresh.<integer>`: 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
|
||||
@@ -392,12 +212,17 @@ Material Mode: WHITELIST Materials: STONE, DIRT, WOOD, CONCRETE variants
|
||||
|
||||
### Chest GUI
|
||||
**Access**: `/dupe chest` or through main menu
|
||||
**Permissions**:
|
||||
- `dupealias.gui.chest`: Main Access
|
||||
- `dupealias.gui.chest.cooldown.<integer>`: 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
|
||||
**Permissions**:
|
||||
- `dupealias.gui.chest`: Main Access
|
||||
- `dupealias.gui.chest.refresh.<integer>`: Sets the amount of ticks for the output items to restock
|
||||
|
||||
**Features**:
|
||||
- Mirror of your actual inventory
|
||||
@@ -426,6 +254,7 @@ Material Mode: WHITELIST Materials: STONE, DIRT, WOOD, CONCRETE variants
|
||||
|
||||
### Menu GUI
|
||||
**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
|
||||
@@ -438,7 +267,6 @@ Material Mode: WHITELIST Materials: STONE, DIRT, WOOD, CONCRETE variants
|
||||
3. Access is controlled by permissions
|
||||
|
||||
---
|
||||
|
||||
## Permissions System
|
||||
|
||||
### Core Permissions
|
||||
@@ -447,20 +275,18 @@ Material Mode: WHITELIST Materials: STONE, DIRT, WOOD, CONCRETE variants
|
||||
```yaml
|
||||
dupealias.admin: true # Access to admin panel and configuration
|
||||
```
|
||||
|
||||
#### Basic Duplication
|
||||
```yaml
|
||||
dupealias.dupe: true # Access to /dupe command
|
||||
dupealias.dupe.cooldown.<integer>: 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
|
||||
dupealias.gui.inventory: true # Access to inventory GUI
|
||||
dupealias.gui.chest: true # Access to chest GUI
|
||||
```
|
||||
|
||||
### Advanced Permissions
|
||||
|
||||
#### Session Persistence
|
||||
@@ -469,7 +295,6 @@ 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.<integer>: false # Ticks of refresh cooldown
|
||||
@@ -477,22 +302,19 @@ dupealias.gui.replicator.cooldown.<integer>: false # Ticks of re-input coold
|
||||
dupealias.gui.inventory.refresh.<integer>: false # Ticks of refresh cooldown
|
||||
dupealias.gui.chest.refresh.<integer>: false # Ticks of refresh cooldown
|
||||
```
|
||||
|
||||
#### Tag Bypasses (Use Carefully!)
|
||||
```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
|
||||
```
|
||||
|
||||
#### Special Permissions
|
||||
```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.<integer>`, 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
|
||||
|
||||
@@ -501,23 +323,23 @@ The plugin uses a "lowest value wins" system for numeric permissions. If a playe
|
||||
groups:
|
||||
vip:
|
||||
permissions:
|
||||
- dupealias.dupe - dupealias.gui - dupealias.gui.replicator.refresh.5 # Faster refresh
|
||||
- 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
|
||||
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`)
|
||||
@@ -529,29 +351,36 @@ groups:
|
||||
"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": {
|
||||
},
|
||||
"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 ]}
|
||||
"\"(?:itemname|iname)\"gmi", // Block item naming commands
|
||||
"\"(?:itemlore|lore)\"gmi" // Block lore modification commands
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### Tag Lore Customization (MiniMessage)
|
||||
|
||||
```json
|
||||
{
|
||||
"trueTagLore": {
|
||||
@@ -567,11 +396,13 @@ groups:
|
||||
"PROTECTED": "<dark_purple><bold>|</bold><light_purple> Unprotected"
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Common Configuration (`common.json`)
|
||||
|
||||
#### Visual Customization
|
||||
|
||||
```json
|
||||
{
|
||||
"mainColor": 11184895, // Primary color (hex: AAAAFF)
|
||||
@@ -580,18 +411,19 @@ groups:
|
||||
"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
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Commands
|
||||
|
||||
### Administrative Commands
|
||||
@@ -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 <method_name>
|
||||
```
|
||||
|
||||
Exclude noisy methods: ``` /da debug exclude <method_name> ```
|
||||
### 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.
|
||||
87
README.md
Normal file
87
README.md
Normal file
@@ -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.
|
||||

|
||||
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.
|
||||

|
||||
The Global Rule Editor is designed to be intuitive to use, with quick buttons for changing match modes and toggling tags.
|
||||

|
||||
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.
|
||||

|
||||
|
||||
---
|
||||
## 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)
|
||||
@@ -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 {
|
||||
|
||||
BIN
images/conflicts.gif
Normal file
BIN
images/conflicts.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 MiB |
BIN
images/maingui.gif
Normal file
BIN
images/maingui.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
BIN
images/replicator.gif
Normal file
BIN
images/replicator.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
BIN
images/rules.gif
Normal file
BIN
images/rules.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 MiB |
@@ -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<Material> effectedMaterials = EnumSet.noneOf(Material.class);
|
||||
public List<ItemsAdderItem> effectedItemsAdderMaterials = new ArrayList<>();
|
||||
|
||||
public String nameContainsRegex = "";
|
||||
public String loreContainsRegex = "";
|
||||
public String compoundTagContainsRegex = "";
|
||||
public String nbtTagContainsRegex = "";
|
||||
public Set<Integer> legacyModelData = new HashSet<>();
|
||||
public Set<ItemFlag> itemFlags = EnumSet.noneOf(ItemFlag.class);
|
||||
public Map<ValidEnchantment, Integer> 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<Boolean> 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<Enchantment, Integer> 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;
|
||||
|
||||
90
src/main/java/me/trouper/dupealias/data/ItemsAdderItem.java
Normal file
90
src/main/java/me/trouper/dupealias/data/ItemsAdderItem.java
Normal file
@@ -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<String, Object> 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<String, Object> customData = (Map<String, Object>) 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<String, Object> itemsAdderData = (Map<String, Object>) 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);
|
||||
}
|
||||
}
|
||||
@@ -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<DupeConfig>, DupeContext {
|
||||
@Override
|
||||
@@ -37,6 +40,8 @@ public class DupeConfig implements JsonSerializable<DupeConfig>, DupeContext {
|
||||
ItemTag.INFINITE, "<dark_green><bold>|</bold><green> Finite"
|
||||
));
|
||||
|
||||
public boolean blockDupePlus = false;
|
||||
|
||||
public List<GlobalRule> globalRules = new ArrayList<>();
|
||||
|
||||
public Replicator replicator = new Replicator();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <toggle|include|exclude>");
|
||||
errorAny(sender, "Usage: debug <toggle|include|exclude|test>");
|
||||
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 <nbt|component>");
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<ItemStack> {
|
||||
@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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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("<gradient:#ffeb3b:#ffc107><bold>Name Contains</bold></gradient>")
|
||||
.rows(3)
|
||||
.item(13, ItemBuilder.create(Material.NAME_TAG)
|
||||
.displayName("<yellow><bold>Current Pattern")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Set a regex pattern to match",
|
||||
"<gray>against item display names",
|
||||
"",
|
||||
"<white>Current: <yellow>" + (rule.nameContainsRegex.isEmpty() ? "Not set" : rule.nameContainsRegex),
|
||||
"",
|
||||
"<yellow>▶ <white>Click to set pattern",
|
||||
"<yellow>▶ <white>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("<aqua><bold>Value: " + value)
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Model data value",
|
||||
"",
|
||||
"<yellow>▶ <white>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("<gradient:#9c27b0:#7b1fa2><bold>Lore Contains</bold></gradient>")
|
||||
.rows(3)
|
||||
.item(13, ItemBuilder.create(Material.WRITABLE_BOOK)
|
||||
.displayName("<light_purple><bold>Current Pattern")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Set a regex pattern to match",
|
||||
"<gray>against item lore lines",
|
||||
"",
|
||||
"<white>Current: <light_purple>" + (rule.loreContainsRegex.isEmpty() ? "Not set" : rule.loreContainsRegex),
|
||||
"",
|
||||
"<yellow>▶ <white>Click to set pattern",
|
||||
"<yellow>▶ <white>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");
|
||||
new GlobalRuleLoreEditor(this,rule).open(player);
|
||||
}
|
||||
})
|
||||
.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);
|
||||
|
||||
public void openNbtCriteriaEditor(Player player, GlobalRule rule) {
|
||||
new GlobalRuleNbtTagEditor(this,rule).open(player);
|
||||
}
|
||||
})
|
||||
.build();
|
||||
gui.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("<gradient:#00bcd4:#0097a7><bold>Model Data Values</bold></gradient>")
|
||||
.rows(4)
|
||||
.item(13, ItemBuilder.create(Material.COMPASS)
|
||||
.displayName("<aqua><bold>Model Data Values")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Manage custom model data values",
|
||||
"<gray>that items must have",
|
||||
"",
|
||||
"<white>Current values: <aqua>" + rule.legacyModelData.size(),
|
||||
rule.legacyModelData.isEmpty() ? "" : "<gray>" + rule.legacyModelData.stream()
|
||||
.limit(5)
|
||||
.map(String::valueOf)
|
||||
.collect(Collectors.joining(", ")),
|
||||
rule.legacyModelData.size() > 5 ? "<gray>... and " + (rule.legacyModelData.size() - 5) + " more" : "",
|
||||
"",
|
||||
"<yellow>▶ <white>Click to add value",
|
||||
"<yellow>▶ <white>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<String> lore = new ArrayList<>();
|
||||
lore.add("<white><bold>Held Item Explanation:</bold>");
|
||||
|
||||
@@ -266,6 +148,10 @@ public class AdminPanelManager implements DupeContext, CommonItems {
|
||||
activeTags.add(ItemTag.UNIQUE);
|
||||
}
|
||||
|
||||
if (!iai.equals(new ItemsAdderItem())) {
|
||||
lore.add("<gray>• Is from ItemsAdder. (" + iai.namespace + ":" + iai.id + ")");
|
||||
}
|
||||
|
||||
if (lore.size() == 1) {
|
||||
lore.add("<gray>• No DupeAlias tags apply to this item");
|
||||
}
|
||||
@@ -338,6 +224,4 @@ public class AdminPanelManager implements DupeContext, CommonItems {
|
||||
case PROTECTED -> "dark_purple";
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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("<blue><bold>Main Color</bold>")
|
||||
.loreMiniMessage(List.of(
|
||||
"<gray>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("<aqua><bold>Secondary Color</bold>")
|
||||
.loreMiniMessage(List.of(
|
||||
"<gray>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("<green><bold>Plugin Name</bold>")
|
||||
.loreMiniMessage(List.of(
|
||||
"<gray>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("<gray><bold>Flat Prefix</bold>")
|
||||
.loreMiniMessage(List.of(
|
||||
"<gray>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",
|
||||
"<gray>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("<white><bold>Flat Mode</bold>")
|
||||
.loreMiniMessage(List.of(
|
||||
"<gray>If true, uses the simple flat message system",
|
||||
|
||||
@@ -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("<green><bold>Save & Return")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Save changes and return",
|
||||
"<gray>to the rule list",
|
||||
"",
|
||||
"<yellow>▶ <white>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<String> materialNames = rule.effectedMaterials.stream()
|
||||
.limit(5)
|
||||
.map(mat -> mat.name())
|
||||
.collect(Collectors.toList());
|
||||
.map(Enum::name)
|
||||
.toList();
|
||||
|
||||
for (String mat : materialNames) {
|
||||
lore.add("<gray>• " + 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("<gray><bold>ItemsAdder List")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Set material mode to",
|
||||
"<gray>WHITELIST or BLACKLIST",
|
||||
"<gray>to configure materials"
|
||||
))
|
||||
.build();
|
||||
}
|
||||
|
||||
List<String> lore = new ArrayList<>();
|
||||
lore.add("<gray>Manage custom materials for this rule");
|
||||
lore.add("");
|
||||
lore.add("<white>Selected: <yellow>" + rule.effectedItemsAdderMaterials.size() + " Custom Materials");
|
||||
|
||||
if (!rule.effectedMaterials.isEmpty()) {
|
||||
lore.add("");
|
||||
List<String> items = new ArrayList<>();
|
||||
|
||||
rule.effectedItemsAdderMaterials.stream()
|
||||
.limit(5)
|
||||
.forEach((iai)->{
|
||||
items.add("<dark_green>" + iai.namespace + "<gray>:<green>" + iai.id);
|
||||
});
|
||||
|
||||
for (String mat : items) {
|
||||
lore.add("<gray>• " + mat);
|
||||
}
|
||||
|
||||
if (rule.effectedMaterials.size() > 5) {
|
||||
lore.add("<gray>... and " + (rule.effectedMaterials.size() - 5) + " more");
|
||||
}
|
||||
}
|
||||
|
||||
lore.add("");
|
||||
lore.add("<yellow>▶ <white>Click to manage");
|
||||
|
||||
return ItemBuilder.create(Material.CHEST)
|
||||
.displayName("<white><bold>ItemsAdder List")
|
||||
.loreMiniMessage(lore)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void toggleTag(Player player, ItemTag tag) {
|
||||
if (rule.appliedTags.contains(tag)) {
|
||||
rule.appliedTags.remove(tag);
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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<ValidEnchantment> implements DupeContext, CommonItems {
|
||||
|
||||
@@ -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;
|
||||
@@ -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("<gradient:#9b59b6:#8e44ad><bold>ItemsAdder Parsing Menu</bold></gradient>")
|
||||
.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("<gold>Information")
|
||||
.loreMiniMessage(
|
||||
"<gray>Add custom items to this GUI and",
|
||||
"<gray>click the green button to have",
|
||||
"<gray>their namespaces parsed."
|
||||
)
|
||||
.build())
|
||||
.item(51,ItemBuilder.create(Material.LIME_DYE)
|
||||
.displayName("<green>Parse Items")
|
||||
.loreMiniMessage(
|
||||
"<gray>Click to add all items",
|
||||
"<gray>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<String> lore = new ArrayList<>();
|
||||
ItemBuilder builder = ItemBuilder.create(Material.PAPER)
|
||||
.displayName("<white><bold>Current Items");
|
||||
|
||||
lore.add("<yellow>Total: <bold>" + rule.effectedItemsAdderMaterials.size());
|
||||
if (!rule.effectedMaterials.isEmpty()) {
|
||||
lore.add("");
|
||||
List<String> items = new ArrayList<>();
|
||||
|
||||
rule.effectedItemsAdderMaterials.stream()
|
||||
.limit(5)
|
||||
.forEach((iai)->{
|
||||
items.add("<dark_green>" + iai.namespace + "<gray>:<green>" + iai.id);
|
||||
});
|
||||
|
||||
for (String mat : items) {
|
||||
lore.add("<gray>• " + mat);
|
||||
}
|
||||
|
||||
if (rule.effectedMaterials.size() > 5) {
|
||||
lore.add("<gray>... and " + (rule.effectedMaterials.size() - 5) + " more");
|
||||
}
|
||||
}
|
||||
|
||||
if (rule.effectedItemsAdderMaterials.size() > 5) lore.add("<gray>and %s more...".formatted(rule.effectedItemsAdderMaterials.size() - 5));
|
||||
|
||||
lore.add("<yellow>▶ <white>Shift Click to clear");
|
||||
|
||||
return builder.loreMiniMessage(lore).build();
|
||||
}
|
||||
}
|
||||
@@ -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<Material> implements DupeContext, CommonItems {
|
||||
|
||||
@@ -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<ValidPotionEffectType> implements DupeContext, CommonItems {
|
||||
|
||||
@@ -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("<gradient:#9c27b0:#7b1fa2><bold>Lore Contains</bold></gradient>")
|
||||
.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("<light_purple><bold>Current Pattern")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Set a regex pattern to match",
|
||||
"<gray>against the item's compound tag",
|
||||
"",
|
||||
"<white>Current: <light_purple>" + (rule.loreContainsRegex.isEmpty() ? "Not set" : rule.loreContainsRegex),
|
||||
"",
|
||||
"<yellow>▶ <white>Click to set pattern",
|
||||
"<yellow>▶ <white>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);
|
||||
}
|
||||
}
|
||||
@@ -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("<gradient:#9c27b0:#7b1fa2><bold>Lore Contains</bold></gradient>")
|
||||
.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("<light_purple><bold>Current Pattern")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Set a regex pattern to match",
|
||||
"<gray>against item lore lines",
|
||||
"",
|
||||
"<white>Current: <light_purple>" + (rule.loreContainsRegex.isEmpty() ? "Not set" : rule.loreContainsRegex),
|
||||
"",
|
||||
"<yellow>▶ <white>Click to set pattern",
|
||||
"<yellow>▶ <white>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);
|
||||
}
|
||||
}
|
||||
@@ -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("<gradient:#00bcd4:#0097a7><bold>Model Data Values</bold></gradient>")
|
||||
.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("<aqua><bold>Model Data Values")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Manage custom model data values",
|
||||
"<gray>that items must have",
|
||||
"",
|
||||
"<white>Current values: <aqua>" + rule.legacyModelData.size(),
|
||||
rule.legacyModelData.isEmpty() ? "" : "<gray>" + rule.legacyModelData.stream()
|
||||
.limit(5)
|
||||
.map(String::valueOf)
|
||||
.collect(Collectors.joining(", ")),
|
||||
rule.legacyModelData.size() > 5 ? "<gray>... and " + (rule.legacyModelData.size() - 5) + " more" : "",
|
||||
"",
|
||||
"<yellow>▶ <white>Click to add value",
|
||||
"<yellow>▶ <white>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("<aqua><bold>Value: " + value)
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Model data value",
|
||||
"",
|
||||
"<yellow>▶ <white>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);
|
||||
}
|
||||
}
|
||||
@@ -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("<gradient:#ffeb3b:#ffc107><bold>Name Contains</bold></gradient>")
|
||||
.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("<yellow><bold>Current Pattern")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Set a regex pattern to match",
|
||||
"<gray>against item display names",
|
||||
"",
|
||||
"<white>Current: <yellow>" + (rule.nameContainsRegex.isEmpty() ? "Not set" : rule.nameContainsRegex),
|
||||
"",
|
||||
"<yellow>▶ <white>Click to set pattern",
|
||||
"<yellow>▶ <white>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);
|
||||
}
|
||||
}
|
||||
@@ -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("<gradient:#9c27b0:#7b1fa2><bold>NBT Tag Contains</bold></gradient>")
|
||||
.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("<light_purple><bold>Current Pattern")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Set a regex pattern to match",
|
||||
"<gray>against mojangson NBT",
|
||||
"",
|
||||
"<white>Current: <light_purple>" + (rule.loreContainsRegex.isEmpty() ? "Not set" : rule.loreContainsRegex),
|
||||
"",
|
||||
"<yellow>▶ <white>Click to set pattern",
|
||||
"<yellow>▶ <white>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);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ public class DupeChestGui extends AbstractDupeGui<DupeChestGui.ChestSession> {
|
||||
@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<DupeChestGui.ChestSession> {
|
||||
|
||||
public ChestSession(Player owner) {
|
||||
super(owner, "<gradient:#cc22ff:#cc99ff><bold>DUPE CHEST</gradient>", 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
|
||||
|
||||
@@ -18,7 +18,7 @@ public class DupeInventoryGui extends AbstractDupeGui<DupeInventoryGui.Inventory
|
||||
@Override
|
||||
public InventorySession getSession(Player player) {
|
||||
InventorySession session = super.getSession(player);
|
||||
session.setDelayTicks(getDupe().getPermissionValue(player, "dupealias.gui.inventory.refresh.", getConfig().inventory.baseRefreshDelayTicks));
|
||||
session.setDelayTicks(getDupe().getPermissionValue(player, "dupealias.gui.inventory.refresh.", getConfig().inventory.baseRefreshDelayTicks,false));
|
||||
session.open();
|
||||
return session;
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public class DupeInventoryGui extends AbstractDupeGui<DupeInventoryGui.Inventory
|
||||
|
||||
public InventorySession(Player owner) {
|
||||
super(owner, "<gradient:#cc22ff:#cc99ff><bold>YOUR INVENTORY</gradient>", 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
|
||||
|
||||
@@ -30,8 +30,8 @@ public class DupeReplicatorGui extends AbstractDupeGui<DupeReplicatorGui.Replica
|
||||
@Override
|
||||
public ReplicatorSession getSession(Player player) {
|
||||
ReplicatorSession session = super.getSession(player);
|
||||
session.setDelayTicks(getDupe().getPermissionValue(player, "dupealias.gui.replicator.refresh.", getConfig().replicator.baseRefreshDelayTicks));
|
||||
session.setCooldownTicks(getDupe().getPermissionValue(player, "dupealias.gui.replicator.cooldown.", getConfig().replicator.baseInputCooldownTicks));
|
||||
session.setDelayTicks(getDupe().getPermissionValue(player, "dupealias.gui.replicator.refresh.", getConfig().replicator.baseRefreshDelayTicks,false));
|
||||
session.setCooldownTicks(getDupe().getPermissionValue(player, "dupealias.gui.replicator.cooldown.", getConfig().replicator.baseInputCooldownTicks,false));
|
||||
session.open();
|
||||
return session;
|
||||
}
|
||||
@@ -49,8 +49,8 @@ public class DupeReplicatorGui extends AbstractDupeGui<DupeReplicatorGui.Replica
|
||||
super(owner, "<gradient:#cc22ff:#cc99ff><bold>REPLICATOR</gradient>", 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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user