Bulk Item Gui, Drop on Death, Admin Panel Updates, and started documenting it.
This commit is contained in:
724
Documentation.md
Normal file
724
Documentation.md
Normal file
@@ -0,0 +1,724 @@
|
||||
|
||||
# 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
|
||||
|
||||
1. [Installation & Setup](#installation--setup)
|
||||
2. [Core Concepts](#core-concepts)
|
||||
3. [Item Tags System](#item-tags-system)
|
||||
4. [Global Rules Engine](#global-rules-engine)
|
||||
5. [Duplication GUIs](#duplication-guis)
|
||||
6. [Permissions System](#permissions-system)
|
||||
7. [Configuration](#configuration)
|
||||
8. [Commands](#commands)
|
||||
9. [Common Scenarios](#common-scenarios)
|
||||
10. [Troubleshooting](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## Installation & Setup
|
||||
|
||||
### Prerequisites
|
||||
- Minecraft 1.21 or higher
|
||||
- Paper, Purpur, or compatible server software
|
||||
- Java 17 or higher
|
||||
|
||||
### Installation Steps
|
||||
1. Download the DupeAlias JAR file
|
||||
2. Place it in your server's `plugins/` directory
|
||||
3. Restart your server
|
||||
4. The plugin will generate default configuration files in `plugins/DupeAlias/`
|
||||
|
||||
### First-Time Configuration
|
||||
1. Join your server as an operator
|
||||
2. Run `/da` to open the admin panel
|
||||
3. Navigate to Configuration → Common Config to customize colors and branding
|
||||
4. Set up your first global rules or start tagging items manually
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Item Tags
|
||||
DupeAlias uses four primary tags that can be applied to items:
|
||||
|
||||
- **UNIQUE**: Prevents the item from being duplicated
|
||||
- **FINAL**: Prevents the item from being modified in any way
|
||||
- **INFINITE**: Keeps the item at maximum stack size (99)
|
||||
- **PROTECTED**: Prevents the item from being used, consumed, or crafted with
|
||||
|
||||
### Tag Priority System
|
||||
Individual item tags **always override** global rules. This allows for fine-grained control where you can set global rules for item types while making exceptions for specific items.
|
||||
|
||||
### Global Rules vs Individual Tags
|
||||
- **Individual Tags**: Stored directly on the item's metadata, apply only to that specific item instance
|
||||
- **Global Rules**: Server-wide rules that apply tags based on item properties like material, name, enchantments, etc.
|
||||
|
||||
---
|
||||
|
||||
## Item Tags System
|
||||
|
||||
### UNIQUE Tag
|
||||
**Purpose**: Prevents item duplication through intended methods
|
||||
|
||||
**Use Cases**:
|
||||
- Crate keys and special tokens
|
||||
- Rare items and rewards
|
||||
- Admin-only equipment
|
||||
- Currency items
|
||||
|
||||
**Conflicts**: Cannot be combined with INFINITE (creates a logical paradox)
|
||||
|
||||
**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
|
||||
|
||||
**Blocks**:
|
||||
- Renaming items
|
||||
- Enchanting items
|
||||
- Repairing items
|
||||
- Anvil operations
|
||||
- Any metadata changes
|
||||
|
||||
**Use Cases**:
|
||||
- Quest items with specific names
|
||||
- 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
|
||||
|
||||
**Behavior**:
|
||||
- Sets item stack to 99 (max stackable amount)
|
||||
- Refills automatically after use
|
||||
- Works with blocks, consumables, and projectiles
|
||||
|
||||
**Use Cases**:
|
||||
- Creative-style building materials
|
||||
- Infinite arrows for archery ranges
|
||||
- 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
|
||||
|
||||
**Blocks**:
|
||||
- Using items (right-click)
|
||||
- Consuming food/potions
|
||||
- Placing blocks
|
||||
- Attacking with weapons
|
||||
- Crafting with the item
|
||||
- Trading with villagers
|
||||
|
||||
**Use Cases**:
|
||||
- Display items
|
||||
- Coupons and vouchers
|
||||
- Decorative rewards
|
||||
- Placeholder items
|
||||
|
||||
**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
|
||||
|
||||
1. Open admin panel with `/da`
|
||||
2. Navigate to **Global Rules**
|
||||
3. Click **+ Create New Rule**
|
||||
4. Configure criteria and applied tags
|
||||
5. Save the rule
|
||||
|
||||
### Rule Components
|
||||
|
||||
#### Applied Tags
|
||||
Select which tags this rule will apply to matching items. Multiple tags can be selected for a single rule.
|
||||
|
||||
#### Match Mode
|
||||
- **AND**: All criteria must match
|
||||
- **OR**: Any criteria must match
|
||||
- **NAND**: Not all criteria match
|
||||
- **XOR**: Exactly one criteria matches
|
||||
|
||||
#### Material Matching
|
||||
- **IGNORE**: Apply to all materials
|
||||
- **WHITELIST**: Only apply to selected materials
|
||||
- **BLACKLIST**: Apply to all except selected materials
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
#### Lore Regex
|
||||
Match items based on lore content using regular expressions.
|
||||
```
|
||||
Example: ".*Special.*" matches items with "Special" anywhere in lore
|
||||
```
|
||||
|
||||
#### Enchantments
|
||||
Match items that have specific enchantments at minimum levels.
|
||||
```
|
||||
Example: Sharpness V → matches items with Sharpness 5 or higher
|
||||
```
|
||||
|
||||
#### Attributes
|
||||
Match items with specific attribute modifiers.
|
||||
```
|
||||
Example: Attack Damage ≥ 10.0 → matches weapons with high damage
|
||||
```
|
||||
|
||||
#### Potion Effects
|
||||
Match potions/foods with specific effects and amplifiers.
|
||||
```
|
||||
Example: Strength II → matches items giving Strength 2 or higher
|
||||
```
|
||||
|
||||
#### Model Data
|
||||
Match items with specific custom model data values.
|
||||
```
|
||||
Example: 12345 → matches items with CustomModelData: 12345
|
||||
```
|
||||
|
||||
#### Item Flags
|
||||
Match items with specific visibility flags.
|
||||
```
|
||||
Example: HIDE_ENCHANTS → matches items that hide enchantments
|
||||
```
|
||||
|
||||
#### Armor Trim
|
||||
Match armor pieces with specific trim patterns or materials.
|
||||
```
|
||||
Example: Silence Pattern + Gold Material → matches gold silence trim armor
|
||||
```
|
||||
|
||||
### Example Rules
|
||||
|
||||
#### Protect All Crate Keys
|
||||
```
|
||||
Applied Tags: UNIQUE, PROTECTED, FINAL
|
||||
Match Mode: OR
|
||||
Name Regex: ".*[Kk]ey.*"
|
||||
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.
|
||||
```
|
||||
|
||||
#### Infinite Creative Blocks
|
||||
```
|
||||
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
|
||||
|
||||
**Features**:
|
||||
- Single-item focused duplication
|
||||
- Animated visual feedback
|
||||
- Input cooldown system
|
||||
- Progress bars for item refresh
|
||||
|
||||
**How to Use**:
|
||||
1. Open the GUI
|
||||
2. Drag an item into the left input slot
|
||||
3. Take copies from the output slot
|
||||
4. Input refreshes after cooldown period
|
||||
|
||||
**Best For**: Quick duplication of single item types
|
||||
|
||||
### Chest GUI
|
||||
**Access**: `/dupe chest` or through main menu
|
||||
|
||||
**Features**:
|
||||
- Multi-item container interface
|
||||
- 4 input columns, 4 output columns
|
||||
- Individual item refresh timers
|
||||
- Session persistence (if enabled)
|
||||
|
||||
**How to Use**:
|
||||
1. Open the GUI
|
||||
2. Place items in the left 4 columns
|
||||
3. Take duplicated copies from the right 4 columns
|
||||
4. Items refresh based on configured delays
|
||||
|
||||
**Best For**: Bulk duplication of multiple item types
|
||||
|
||||
### Inventory GUI
|
||||
**Access**: `/dupe inventory` or through main menu
|
||||
|
||||
**Features**:
|
||||
- Mirror of your actual inventory
|
||||
- Includes armor slots and offhand
|
||||
- Real-time synchronization
|
||||
- Individual slot refresh timers
|
||||
|
||||
**How to Use**:
|
||||
1. Open the GUI
|
||||
2. Your inventory is mirrored in the interface
|
||||
3. Take copies of any items you're carrying
|
||||
4. GUI updates as you change your inventory
|
||||
|
||||
**Best For**: Easy access to copies of everything you're carrying
|
||||
|
||||
### Menu GUI
|
||||
**Access**: `/dupe gui` or `/dupe` (if set as default)
|
||||
|
||||
**Features**:
|
||||
- Central hub for all GUI types
|
||||
- Permission-based access control
|
||||
- Clean navigation interface
|
||||
|
||||
**How to Use**:
|
||||
1. Open the main menu
|
||||
2. Click on the GUI type you want to use
|
||||
3. Access is controlled by permissions
|
||||
|
||||
---
|
||||
|
||||
## Permissions System
|
||||
|
||||
### Core Permissions
|
||||
|
||||
#### Admin Access
|
||||
```yaml
|
||||
dupealias.admin: true # Access to admin panel and configuration
|
||||
```
|
||||
|
||||
#### 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
|
||||
```
|
||||
|
||||
### Advanced Permissions
|
||||
|
||||
#### Session Persistence
|
||||
```yaml
|
||||
dupealias.gui.replicator.keep: false # Keep replicator items on close
|
||||
dupealias.gui.chest.keep: false # Keep chest items on close
|
||||
dupealias.gui.chest.keepondeath: false # Keep chest items on death
|
||||
```
|
||||
|
||||
#### Permission Based Refresh Rates
|
||||
```yaml
|
||||
dupealias.gui.replicator.refresh.<integer>: false # Ticks of refresh cooldown
|
||||
dupealias.gui.replicator.cooldown.<integer>: false # Ticks of re-input cooldown
|
||||
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.
|
||||
|
||||
### Example Permission Sets
|
||||
|
||||
#### VIP Player
|
||||
```yaml
|
||||
groups:
|
||||
vip:
|
||||
permissions:
|
||||
- dupealias.dupe - dupealias.gui - dupealias.gui.replicator.refresh.5 # Faster refresh
|
||||
- dupealias.gui.replicator.cooldown.10 # Shorter cooldown
|
||||
- dupealias.dupe.cooldown.0 # No command cooldown
|
||||
```
|
||||
|
||||
#### Staff Member
|
||||
```yaml
|
||||
groups:
|
||||
staff:
|
||||
permissions: - dupealias.admin # Full admin access
|
||||
- dupealias.gui.replicator.refresh.1 # Instant refresh
|
||||
- dupealias.gui.*.keep # Session persistence
|
||||
- dupealias.final.bypass # Can modify final items
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Main Configuration (`config.json`)
|
||||
|
||||
#### Duplication Settings
|
||||
```json
|
||||
{
|
||||
"dupeCooldownMillis": 1000, // Command cooldown in milliseconds
|
||||
"defaultDupeGui": "REPLICATOR" // Default GUI (REPLICATOR/INVENTORY/CHEST/MENU)
|
||||
}
|
||||
```
|
||||
|
||||
#### GUI Refresh Rates
|
||||
```json
|
||||
{
|
||||
"replicator": {
|
||||
"baseRefreshDelayTicks": 1, // Base item refresh delay
|
||||
"baseInputCooldownTicks": 20 // Base input change cooldown
|
||||
}, "chest": {
|
||||
"baseRefreshDelayTicks": 1 // Base item refresh delay
|
||||
},
|
||||
"inventory": {
|
||||
"baseRefreshDelayTicks": 1 // Base item refresh delay
|
||||
}}
|
||||
```
|
||||
|
||||
#### Command Blocking
|
||||
```json
|
||||
{
|
||||
"finalCommandRegex": [
|
||||
"\"(?:itemname|iname)\"gmi", // Block item naming commands "\"(?:itemlore|lore)\"gmi" // Block lore modification commands ]}
|
||||
```
|
||||
|
||||
#### Tag Lore Customization (MiniMessage)
|
||||
```json
|
||||
{
|
||||
"trueTagLore": {
|
||||
"UNIQUE": "<dark_blue><bold>|</bold><blue> Unique",
|
||||
"FINAL": "<dark_red><bold>|</bold><red> Final",
|
||||
"INFINITE": "<dark_green><bold>|</bold><green> Infinite",
|
||||
"PROTECTED": "<dark_purple><bold>|</bold><light_purple> Protected"
|
||||
},
|
||||
"falseTagLore": {
|
||||
"UNIQUE": "<dark_blue><bold>|</bold><blue> Dupeable",
|
||||
"FINAL": "<dark_red><bold>|</bold><red> Mutable",
|
||||
"INFINITE": "<dark_green><bold>|</bold><green> Finite",
|
||||
"PROTECTED": "<dark_purple><bold>|</bold><light_purple> Unprotected"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Common Configuration (`common.json`)
|
||||
|
||||
#### Visual Customization
|
||||
```json
|
||||
{
|
||||
"mainColor": 11184895, // Primary color (hex: AAAAFF)
|
||||
"secondaryColor": 909055, // Secondary color (hex: 00DDFF)
|
||||
"pluginName": "DupeAlias", // Display name
|
||||
"flatPrefix": "&9DupeAlias> &7", // Legacy chat prefix
|
||||
"flat": false // Use legacy formatting
|
||||
}
|
||||
```
|
||||
|
||||
#### Debug Settings
|
||||
```json
|
||||
{
|
||||
"debugMode": false, // Enable debug output
|
||||
"debuggerExclusions": [] // Methods to exclude from debug
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Commands
|
||||
|
||||
### Administrative Commands
|
||||
|
||||
#### `/dupealias` (Aliases: `/da`)
|
||||
**Permission**: `dupealias.admin`
|
||||
**Description**: Main administrative command
|
||||
|
||||
**Subcommands**:
|
||||
- `/da` - Open admin panel GUI
|
||||
- `/da gui` - Open admin panel GUI
|
||||
- `/da debug toggle` - Toggle debug mode
|
||||
- `/da debug exclude <method>` - Exclude method from debugging
|
||||
- `/da debug include <method>` - Include method in debugging
|
||||
|
||||
#### Tag Management
|
||||
- `/da tag <tag> [material|global] [remove]` - Manage item tags
|
||||
- `/da tag <tag>` - Tag held item
|
||||
- `/da tag <tag> remove` - Remove tag from held item
|
||||
- `/da tag <tag> false` - Set tag to false on held item
|
||||
- `/da tag <tag> global` - Create global rule for held item material
|
||||
- `/da tag <tag> <material>` - Create global rule for specific material
|
||||
- `/da tag <tag> <material> remove` - Remove global rule
|
||||
|
||||
#### Rule Management
|
||||
- `/da rule create <tag>` - Create new global rule
|
||||
- `/da rule list` - List all global rules
|
||||
- `/da rule remove <index>` - Remove rule by index
|
||||
- `/da rule info <index>` - Show detailed rule information
|
||||
|
||||
### Player Commands
|
||||
|
||||
#### `/dupe`
|
||||
**Permission**: `dupealias.dupe`
|
||||
**Description**: Main duplication command
|
||||
|
||||
**Usage**:
|
||||
- `/dupe` - Duplicate held item once OR open default GUI
|
||||
- `/dupe <amount>` - Duplicate held item multiple times
|
||||
- `/dupe gui` - Open main GUI menu
|
||||
- `/dupe replicator` - Open replicator GUI
|
||||
- `/dupe inventory` - Open inventory GUI
|
||||
- `/dupe chest` - Open chest GUI
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### "You cannot dupe unique items"
|
||||
**Cause**: Item has UNIQUE tag or matches global rule
|
||||
**Solution**:
|
||||
- Check `/da` → Held Item Actions to see current tags
|
||||
- Review global rules that might be applying UNIQUE tag
|
||||
- Use `/da tag UNIQUE remove` to remove individual tag
|
||||
|
||||
#### Items not refreshing in GUI
|
||||
**Cause**: Long refresh delay or permission issues
|
||||
**Solution**:
|
||||
- Check player has appropriate GUI permissions
|
||||
- Verify refresh rate permissions (lower numbers = faster)
|
||||
- Ensure player isn't hitting cooldown limits
|
||||
|
||||
#### "You cannot modify final items"
|
||||
**Cause**: Item has FINAL tag blocking modifications
|
||||
**Solution**:
|
||||
- Check item tags in admin panel
|
||||
- Use `/da tag FINAL remove` if needed
|
||||
- Grant `dupealias.final.bypass` permission for admins
|
||||
|
||||
#### Global rules not applying
|
||||
**Cause**: Rule criteria not matching or individual tags overriding
|
||||
**Solution**:
|
||||
- Test rule criteria with `/da rule info <index>`
|
||||
- Check match mode (AND vs OR)
|
||||
- Remember individual tags override global rules
|
||||
|
||||
#### GUI won't open
|
||||
**Cause**: Missing permissions or plugin conflicts
|
||||
**Solution**:
|
||||
- Verify player has `dupealias.gui` permission
|
||||
- Check for inventory plugin conflicts
|
||||
- Ensure player inventory isn't full
|
||||
|
||||
### Debug Mode
|
||||
|
||||
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>
|
||||
```
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
#### Large Player Counts
|
||||
- Use session persistence sparingly
|
||||
- Monitor server TPS with `/tps`
|
||||
|
||||
#### Complex Global Rules
|
||||
- Avoid overly complex regex patterns
|
||||
- Use material whitelisting instead of complex criteria when possible
|
||||
- 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
|
||||
|
||||
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.
|
||||
3
Ideas.md
3
Ideas.md
@@ -1,2 +1 @@
|
||||
match based on certain features such as enchant, trim, lore string, or name.
|
||||
on death, drop any items stored in chest GUI.
|
||||
Bulk item tag adder
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -5,7 +5,6 @@ import me.trouper.alias.AliasContextProvider;
|
||||
import me.trouper.alias.data.Common;
|
||||
import me.trouper.dupealias.data.files.CommonConfig;
|
||||
import me.trouper.dupealias.data.files.DupeConfig;
|
||||
import me.trouper.dupealias.data.files.NBTStorage;
|
||||
import me.trouper.dupealias.server.DupeManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
@@ -29,7 +28,6 @@ public final class DupeAlias extends JavaPlugin {
|
||||
alias.initialize();
|
||||
alias.getDataManager().load(CommonConfig.class).save();
|
||||
alias.getDataManager().load(DupeConfig.class).save();
|
||||
alias.getDataManager().load(NBTStorage.class).save();
|
||||
updateCommon();
|
||||
|
||||
dupe = new DupeManager();
|
||||
@@ -39,7 +37,6 @@ public final class DupeAlias extends JavaPlugin {
|
||||
public void onDisable() {
|
||||
alias.getDataManager().save(CommonConfig.class);
|
||||
alias.getDataManager().save(DupeConfig.class);
|
||||
alias.getDataManager().save(NBTStorage.class);
|
||||
alias.shutdown();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import me.trouper.alias.server.ContextAware;
|
||||
import me.trouper.alias.server.events.listeners.GuiInputListener;
|
||||
import me.trouper.dupealias.data.files.CommonConfig;
|
||||
import me.trouper.dupealias.data.files.DupeConfig;
|
||||
import me.trouper.dupealias.data.files.NBTStorage;
|
||||
import me.trouper.dupealias.server.DupeManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
@@ -26,10 +25,6 @@ public interface DupeContext extends ContextAware {
|
||||
return getDataManager().get(DupeConfig.class);
|
||||
}
|
||||
|
||||
default NBTStorage getNbtStorage() {
|
||||
return getDataManager().get(NBTStorage.class);
|
||||
}
|
||||
|
||||
default GuiInputListener getGuiListener() {
|
||||
return getContext().getGuiInputListener();
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
package me.trouper.dupealias.data;
|
||||
|
||||
import me.trouper.alias.utils.ItemSimilarity;
|
||||
import me.trouper.dupealias.server.ItemTag;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.*;
|
||||
|
||||
public class ItemCapture {
|
||||
|
||||
private String serializedItem;
|
||||
private double similarityThreshold;
|
||||
private final ItemSimilarity.SimilarityConfiguration configuration;
|
||||
private final Map<ItemTag, Boolean> tags;
|
||||
|
||||
public ItemCapture() {
|
||||
this.similarityThreshold = 1;
|
||||
this.configuration = new ItemSimilarity.SimilarityConfiguration();
|
||||
this.tags = new HashMap<>();
|
||||
}
|
||||
|
||||
public ItemCapture(ItemStack stack) {
|
||||
this.serializedItem = serialize(stack);
|
||||
this.similarityThreshold = 1;
|
||||
this.configuration = new ItemSimilarity.SimilarityConfiguration();
|
||||
this.tags = new HashMap<>();
|
||||
}
|
||||
|
||||
private String serialize(ItemStack itemStack) {
|
||||
try {
|
||||
return Base64.getEncoder().encodeToString(itemStack.serializeAsBytes());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Unable to serialize ItemStack", e);
|
||||
}
|
||||
}
|
||||
|
||||
private ItemStack deserialize(String serializedItemStack) {
|
||||
try {
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(serializedItemStack));
|
||||
byte[] itemBytes = inputStream.readAllBytes();
|
||||
return ItemStack.deserializeBytes(itemBytes);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Unable to deserialize ItemStack", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<ItemTag, Boolean> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public ItemStack getStack() {
|
||||
return deserialize(serializedItem);
|
||||
}
|
||||
|
||||
public ItemMeta getMeta() {
|
||||
return getStack().getItemMeta();
|
||||
}
|
||||
|
||||
public boolean matches(ItemStack item) {
|
||||
if (similarityThreshold >= 1) return item.isSimilar(getStack());
|
||||
return similarityThreshold <= similarityTo(item);
|
||||
}
|
||||
|
||||
public double similarityTo(ItemStack item) {
|
||||
return ItemSimilarity.calculateSimilarity(item,getStack());
|
||||
}
|
||||
|
||||
public double getThreshold() {
|
||||
return similarityThreshold;
|
||||
}
|
||||
|
||||
public ItemSimilarity.SimilarityConfiguration getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public void setThreshold(double similarityThreshold) {
|
||||
this.similarityThreshold = similarityThreshold;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import me.trouper.alias.data.JsonSerializable;
|
||||
import me.trouper.dupealias.DupeContext;
|
||||
import me.trouper.dupealias.data.GlobalRule;
|
||||
import me.trouper.dupealias.server.ItemTag;
|
||||
import org.bukkit.Material;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
@@ -15,7 +14,7 @@ public class DupeConfig implements JsonSerializable<DupeConfig>, DupeContext {
|
||||
return new File(getInstance().getDataFolder(),"config.json");
|
||||
}
|
||||
|
||||
public long dupeCooldownMillis = 1000;
|
||||
public int baseDupeCooldownMillis = 1000;
|
||||
|
||||
public String defaultDupeGui = "REPLICATOR";
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package me.trouper.dupealias.data.files;
|
||||
|
||||
import me.trouper.alias.data.JsonSerializable;
|
||||
import me.trouper.dupealias.DupeContext;
|
||||
import me.trouper.dupealias.data.ItemCapture;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public class NBTStorage implements JsonSerializable<NBTStorage>, DupeContext {
|
||||
@Override
|
||||
public File getFile() {
|
||||
return new File(getInstance().getDataFolder(), ".nbtstorage.json");
|
||||
}
|
||||
|
||||
public List<ItemCapture> captures = new ArrayList<>();
|
||||
|
||||
public ItemCapture getCapture(ItemStack input) {
|
||||
if (getNbtStorage().captures.isEmpty()) return null;
|
||||
ItemCapture match = null;
|
||||
double closest = -1;
|
||||
|
||||
for (ItemCapture capture : getNbtStorage().captures) {
|
||||
boolean isSimilar = capture.getStack().isSimilar(input);
|
||||
if (isSimilar) return capture;
|
||||
double threshold = capture.getThreshold();
|
||||
|
||||
if (threshold >= 1) continue; // Don't bother calculating similarity if the item isn't similar.
|
||||
double sim = capture.similarityTo(input);
|
||||
if (sim >= threshold && sim >= closest) {
|
||||
closest = sim;
|
||||
match = capture;
|
||||
}
|
||||
}
|
||||
|
||||
if (closest == -1) return null;
|
||||
|
||||
return match;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package me.trouper.dupealias.server;
|
||||
import me.trouper.alias.utils.ItemBuilder;
|
||||
import me.trouper.dupealias.DupeContext;
|
||||
import me.trouper.dupealias.data.GlobalRule;
|
||||
import me.trouper.dupealias.data.ItemCapture;
|
||||
import me.trouper.dupealias.server.functions.UniqueCheck;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
@@ -19,35 +18,8 @@ import java.util.*;
|
||||
|
||||
public class DupeManager implements DupeContext {
|
||||
|
||||
/**
|
||||
* @return false if the item was modified.
|
||||
*/
|
||||
public boolean verifyTag(ItemStack item) {
|
||||
if (getNbtStorage().captures.isEmpty()) return true;
|
||||
ItemCapture capture = getNbtStorage().getCapture(item);
|
||||
if (capture == null) return true;
|
||||
boolean modified = false;
|
||||
|
||||
for (Map.Entry<ItemTag, Boolean> tagEntry : capture.getTags().entrySet()) {
|
||||
ItemTag tag = tagEntry.getKey();
|
||||
boolean value = tagEntry.getValue();
|
||||
boolean set = hasIndividualTag(item,tag);
|
||||
if (set && checkIndividualTag(item,tag) == value) {
|
||||
continue;
|
||||
} else if (!set) {
|
||||
setTag(item,tagEntry.getKey(),value);
|
||||
} else {
|
||||
removeTag(item,tag);
|
||||
setTag(item,tag,value);
|
||||
}
|
||||
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return !modified;
|
||||
}
|
||||
|
||||
public boolean isUnique(ItemStack item) {
|
||||
if (item == null || item.isEmpty()) return false;
|
||||
return !new UniqueCheck().passes(item);
|
||||
}
|
||||
|
||||
@@ -186,7 +158,7 @@ public class DupeManager implements DupeContext {
|
||||
public boolean removeTag(ItemStack item, ItemTag tag) {
|
||||
ItemBuilder builder = ItemBuilder.of(item);
|
||||
|
||||
if (hasIndividualTag(item, tag) && !checkIndividualTag(item, tag)) return false;
|
||||
if (!hasIndividualTag(item, tag)) return false;
|
||||
|
||||
builder.modifyMeta(itemMeta->{
|
||||
if (itemMeta.hasLore()) {
|
||||
|
||||
@@ -183,7 +183,7 @@ public class AdminCommand implements QuickCommand, DupeContext {
|
||||
GlobalRule rule = getConfig().globalRules.get(i);
|
||||
StringBuilder tagList = new StringBuilder();
|
||||
for (ItemTag tag : rule.appliedTags) {
|
||||
if (tagList.length() > 0) tagList.append(", ");
|
||||
if (!tagList.isEmpty()) tagList.append(", ");
|
||||
tagList.append(tag.getName());
|
||||
}
|
||||
infoAny(sender, " #{0}: Tags: {1}, Match Mode: {2}, Material Mode: {3}",
|
||||
@@ -209,7 +209,7 @@ public class AdminCommand implements QuickCommand, DupeContext {
|
||||
|
||||
StringBuilder tagList = new StringBuilder();
|
||||
for (ItemTag tag : removedRule.appliedTags) {
|
||||
if (tagList.length() > 0) tagList.append(", ");
|
||||
if (!tagList.isEmpty()) tagList.append(", ");
|
||||
tagList.append(tag.getName());
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import java.util.UUID;
|
||||
|
||||
@CommandRegistry(
|
||||
value = "dupe",
|
||||
permission = @Permission(value = "dupealias.dupe", message = "You do not have permission to duplicate items."),
|
||||
usage = "/dupe [integer|gui]",
|
||||
printStackTrace = true,
|
||||
blocksAllowed = false,
|
||||
@@ -25,21 +24,15 @@ import java.util.UUID;
|
||||
)
|
||||
public class DupeCommand implements QuickCommand, DupeContext {
|
||||
|
||||
private final DupeGui dupeGui = new DupeGui();
|
||||
public final DupeGui dupeGui = new DupeGui();
|
||||
private final Cooldown<UUID> dupeCooldown = new Cooldown<>();
|
||||
|
||||
@Override
|
||||
public void handleCommand(CommandSender sender, Command command, String label, Args args) {
|
||||
Player player = (Player) sender;
|
||||
if (!player.hasPermission("dupealias.dupe.cooldownbypass") && dupeCooldown.isOnCooldown(player.getUniqueId())) {
|
||||
warningAny(player,"You can run /dupe again in {0}.", dupeCooldown.formatLong(player.getUniqueId()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.isEmpty()) {
|
||||
if (dupeHeld(player,1)) {
|
||||
dupeCooldown.setCooldown(player.getUniqueId(), getConfig().dupeCooldownMillis);
|
||||
} else {
|
||||
if (!verifyDupe(player,1)) {
|
||||
dupeGui.openDefaultGui(player);
|
||||
}
|
||||
return;
|
||||
@@ -66,9 +59,7 @@ public class DupeCommand implements QuickCommand, DupeContext {
|
||||
|
||||
try {
|
||||
int amount = args.get(0).toInt();
|
||||
if (dupeHeld(player,amount)) {
|
||||
dupeCooldown.setCooldown(player.getUniqueId(), getConfig().dupeCooldownMillis);
|
||||
} else {
|
||||
if (!verifyDupe(player,amount)) {
|
||||
dupeGui.openDefaultGui(player);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
@@ -83,16 +74,50 @@ public class DupeCommand implements QuickCommand, DupeContext {
|
||||
);
|
||||
}
|
||||
|
||||
private boolean dupeHeld(Player player, int amount) {
|
||||
ItemStack inHand = player.getInventory().getItemInMainHand();
|
||||
if (getDupe().isUnique(inHand)) {
|
||||
warningAny(player,"Your {0} is or contains a unique item that cannot be duped!", inHand.getType());
|
||||
private boolean verifyDupe(Player player, int amount) {
|
||||
if (!player.hasPermission("dupealias.dupe")) {
|
||||
warningAny(player,"You are not allowed to dupe via commands.");
|
||||
return false;
|
||||
}
|
||||
if (dupeCooldown.isOnCooldown(player.getUniqueId())) {
|
||||
warningAny(player,"You can command dupe again in {0}.", dupeCooldown.formatLong(player.getUniqueId()));
|
||||
return false;
|
||||
}
|
||||
if (inHand.isEmpty()) return false;
|
||||
|
||||
int baseCount = inHand.getAmount();
|
||||
int maxPerStack = inHand.getMaxStackSize();
|
||||
int playerMax = getDupe().getPermissionValue(player,"dupealias.dupe.limit.",Integer.MAX_VALUE);
|
||||
if (amount > playerMax) {
|
||||
warningAny(player,"Your maximum permitted dupe amplifier is {0}!", playerMax);
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemStack toDupe = player.getInventory().getItemInMainHand();
|
||||
ItemStack offHand = player.getInventory().getItemInOffHand();
|
||||
|
||||
if (toDupe.isEmpty() && offHand.isEmpty()) {
|
||||
warningAny(player,"You must hold an item to duplicate it with commands.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (toDupe.isEmpty() || getDupe().isUnique(toDupe)) {
|
||||
if (getDupe().isUnique(offHand)) {
|
||||
warningAny(player,"Your {0} is or contains a unique item that cannot be duped!", toDupe.getType());
|
||||
return false;
|
||||
} else {
|
||||
toDupe = offHand;
|
||||
}
|
||||
}
|
||||
|
||||
dupeStack(player,toDupe,amount);
|
||||
|
||||
int playerCooldown = getDupe().getPermissionValue(player,"dupealias.dupe.cooldown.",getConfig().baseDupeCooldownMillis);
|
||||
dupeCooldown.setCooldown(player.getUniqueId(), playerCooldown);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void dupeStack(Player player, ItemStack heldStack, int amount) {
|
||||
int baseCount = heldStack.getAmount();
|
||||
int maxPerStack = heldStack.getMaxStackSize();
|
||||
|
||||
for (int i = 0; i <= amount - 1; i++) {
|
||||
int remaining = baseCount * (1 << i);
|
||||
@@ -101,18 +126,17 @@ public class DupeCommand implements QuickCommand, DupeContext {
|
||||
int stackAmt = Math.min(remaining, maxPerStack);
|
||||
remaining -= stackAmt;
|
||||
|
||||
ItemStack batch = inHand.clone();
|
||||
ItemStack batch = heldStack.clone();
|
||||
batch.setAmount(stackAmt);
|
||||
|
||||
if (!player.getInventory().addItem(batch).isEmpty()) {
|
||||
infoAny(player,"Your inventory is now full.");
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int totalGiven = baseCount * ((1 << amount) - 1);
|
||||
successAny(player,"You have duplicated {0} items!", totalGiven);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package me.trouper.dupealias.server.events;
|
||||
|
||||
import me.trouper.alias.server.events.QuickListener;
|
||||
import me.trouper.dupealias.DupeContext;
|
||||
import me.trouper.dupealias.server.commands.DupeCommand;
|
||||
import me.trouper.dupealias.server.gui.dupe.DupeGui;
|
||||
import me.trouper.dupealias.server.gui.dupe.sub.DupeChestGui;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DeathEvent implements QuickListener, DupeContext {
|
||||
|
||||
@EventHandler
|
||||
public void onDeath(PlayerDeathEvent e) {
|
||||
Player p = e.getPlayer();
|
||||
if (p.hasPermission("dupealias.gui.chest.keepondeath")) return;
|
||||
|
||||
DupeGui gui = getContext().getAutoRegistrar().getQuickCommand(DupeCommand.class).dupeGui;
|
||||
DupeChestGui.ChestSession session = gui.chestGui.getSession(p);
|
||||
if (session == null) return;
|
||||
|
||||
List<ItemStack> items = session.getInputItems();
|
||||
e.getDrops().addAll(items);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,10 @@ public class AdminPanelManager implements DupeContext, CommonItems {
|
||||
public void openHeldItemGui(Player player) {
|
||||
new HeldItemGui(this).open(player);
|
||||
}
|
||||
|
||||
public void openBulkTagGui(Player player) {
|
||||
new BulkTagGui(this).open(player);
|
||||
}
|
||||
|
||||
public void openHelpGui(Player player) {
|
||||
new HelpGui(this).open(player);
|
||||
@@ -289,28 +293,9 @@ public class AdminPanelManager implements DupeContext, CommonItems {
|
||||
.build();
|
||||
}
|
||||
|
||||
public ItemStack createPreviewItem(ItemStack stack) {
|
||||
if (stack.getType().isAir()) {
|
||||
return ItemBuilder.create(Material.GRAY_STAINED_GLASS_PANE)
|
||||
.displayName("<gray><bold>No Item Held</bold>")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Hold an item to see",
|
||||
"<gray>its current tag status",
|
||||
"",
|
||||
"<yellow>💡 <white>Hold an item and reopen this GUI"
|
||||
))
|
||||
.build();
|
||||
}
|
||||
|
||||
return ItemBuilder.create(stack.getType())
|
||||
.displayName("<white><bold>Currently Held: " + stack.getType().name() + "</bold>")
|
||||
.loreMiniMessage(getItemTagStatus(stack))
|
||||
.build();
|
||||
}
|
||||
|
||||
public List<String> getItemTagStatus(ItemStack item) {
|
||||
List<String> lore = new ArrayList<>();
|
||||
lore.add("<white>Item: <yellow>" + item.getType().name());
|
||||
lore.add("<white>Held Item: <yellow>" + item.getType().name());
|
||||
lore.add("");
|
||||
|
||||
List<String> individualTags = new ArrayList<>();
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
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.server.ItemTag;
|
||||
import me.trouper.dupealias.server.gui.CommonItems;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class BulkTagGui implements DupeContext, CommonItems {
|
||||
|
||||
private final AdminPanelManager manager;
|
||||
|
||||
public BulkTagGui(AdminPanelManager manager) {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
public void open(Player player) {
|
||||
QuickGui gui = QuickGui.create()
|
||||
.titleMini("<gradient:#9b59b6:#8e44ad><bold>Bulk Tagging Menu</bold></gradient>")
|
||||
.allowDrag()
|
||||
.onGlobalClick((g,e)->{
|
||||
if (e.getSlot() >= 45) return;
|
||||
e.setCancelled(false);
|
||||
})
|
||||
.size(54)
|
||||
.fillSlots(EMPTY(),null,49,51,52)
|
||||
.item(53,BACK(),(g,e)->manager.openMainGui(player))
|
||||
|
||||
.item(45, ItemBuilder.create(Material.EMERALD)
|
||||
.displayName("<green><bold>UNIQUE Tag</bold>")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Makes all items in the gui",
|
||||
"<red>unable to be duplicated",
|
||||
"",
|
||||
"<yellow>▶ <white>Left click to apply tag",
|
||||
"<yellow>▶ <white>Right click to remove tag",
|
||||
"<yellow>▶ <white>Shift click to set tag to false"
|
||||
))
|
||||
.build(),
|
||||
(g,e) -> handleTag(g.getInventory(),ItemTag.UNIQUE,e.getClick())
|
||||
)
|
||||
|
||||
.item(46, ItemBuilder.create(Material.BARRIER)
|
||||
.displayName("<red><bold>FINAL Tag</bold>")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Makes all items in the gui",
|
||||
"<red>unable to be modified",
|
||||
"",
|
||||
"<yellow>▶ <white>Left click to apply tag",
|
||||
"<yellow>▶ <white>Right click to remove tag",
|
||||
"<yellow>▶ <white>Shift click to set tag to false"
|
||||
))
|
||||
.build(),
|
||||
(g,e) -> handleTag(g.getInventory(),ItemTag.FINAL,e.getClick()))
|
||||
|
||||
|
||||
|
||||
.item(47, ItemBuilder.create(Material.WATER_BUCKET)
|
||||
.displayName("<blue><bold>INFINITE Tag</bold>")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Makes all items in the gui",
|
||||
"<green>always have max stack size",
|
||||
"",
|
||||
"<yellow>▶ <white>Left click to apply tag",
|
||||
"<yellow>▶ <white>Right click to remove tag",
|
||||
"<yellow>▶ <white>Shift click to set tag to false"
|
||||
))
|
||||
.build(),
|
||||
(g,e) -> handleTag(g.getInventory(),ItemTag.INFINITE,e.getClick()))
|
||||
|
||||
.item(48, ItemBuilder.create(Material.STRUCTURE_VOID)
|
||||
.displayName("<dark_purple><bold>PROTECTED Tag</bold>")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Makes all items in the gui",
|
||||
"<red>not able to be manually created",
|
||||
"",
|
||||
"<yellow>▶ <white>Left click to apply tag",
|
||||
"<yellow>▶ <white>Right click to remove tag",
|
||||
"<yellow>▶ <white>Shift click to set tag to false"
|
||||
))
|
||||
.build(),
|
||||
(g,e) -> handleTag(g.getInventory(),ItemTag.PROTECTED,e.getClick()))
|
||||
|
||||
.item(50, ItemBuilder.create(Material.TNT)
|
||||
.displayName("<dark_red><bold>Remove All Tags</bold>")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Removes all tags from",
|
||||
"<gray>all items in the gui",
|
||||
"",
|
||||
"<red>⚠ <white>This cannot be undone!",
|
||||
"<yellow>▶ <white>Click to remove tags"
|
||||
))
|
||||
.build(),
|
||||
(g, e) -> removeTags(g.getInventory()))
|
||||
|
||||
.build();
|
||||
|
||||
gui.open(player);
|
||||
}
|
||||
|
||||
private void handleTag(Inventory bulkGui, ItemTag tag, ClickType click) {
|
||||
switch (click) {
|
||||
case LEFT -> tagItems(bulkGui,tag,true);
|
||||
case RIGHT -> removeTag(bulkGui,tag);
|
||||
case SHIFT_LEFT, SHIFT_RIGHT -> tagItems(bulkGui,tag,false);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeTags(Inventory bulkGui) {
|
||||
for (ItemTag value : ItemTag.values()) {
|
||||
removeTag(bulkGui,value);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeTag(Inventory bulkGui, ItemTag tag) {
|
||||
for (int i = 0; i < 45; i++) {
|
||||
ItemStack toTag = bulkGui.getItem(i);
|
||||
if (toTag == null || toTag.isEmpty()) continue;
|
||||
getDupe().removeTag(toTag,tag);
|
||||
}
|
||||
}
|
||||
|
||||
private void tagItems(Inventory bulkGui, ItemTag tag, boolean value) {
|
||||
for (int i = 0; i < 45; i++) {
|
||||
ItemStack toTag = bulkGui.getItem(i);
|
||||
if (toTag == null || toTag.isEmpty()) continue;
|
||||
getDupe().setTag(toTag,tag,value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,22 +187,24 @@ public class HelpGui implements CommonItems {
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<white>Access Permissions:",
|
||||
"<green>The root permission node is <bold>dupealias",
|
||||
"<gray>• <white>.dupe<gray> - Use /dupe command",
|
||||
"<gray>• <white>.dupe<gray> - Use command duping",
|
||||
"<gray>• <white>.dupe.cooldown.<n><gray> - Sets the command dupe cooldown",
|
||||
"<gray>• <white>.dupe.limit.<n><gray> - Sets the command dupe amplifier limit",
|
||||
"<gray>• <white>.gui<gray> - Access duplication GUI",
|
||||
"",
|
||||
"<white>Dupe GUI & Sessions:",
|
||||
"<gray>• <white>.gui.<type>.refresh.<n><gray> - GUI refill time",
|
||||
"<gray>• <white>.gui.<type>.keep<gray> - Retain items in GUI session",
|
||||
"<gray>• <white>.gui.replicator<gray> - Shift-click duplicate",
|
||||
"<gray>• <white>.gui.replicator.cooldown<gray> - Item input cooldown",
|
||||
"<gray>• <white>.gui.replicator.cooldown.<n><gray> - Item input cooldown",
|
||||
"<gray>• <white>.gui.inventory<gray> - View personal inventory",
|
||||
"<gray>• <white>.gui.chest<gray> - Dupe via container",
|
||||
"<gray>• <white>.gui.chest.keepondeath<gray> - Don't drop items on death",
|
||||
"",
|
||||
"<white>Bypass Permissions:",
|
||||
"<gray>• <white>.unique.bypass<gray> - Dupe unique items",
|
||||
"<gray>• <white>.final.bypass<gray> - Modify final items",
|
||||
"<gray>• <white>.protected.bypass<gray> - Use protected items",
|
||||
"<gray>• <white>.dupe.cooldownbypass<gray> - Skip /dupe cooldown",
|
||||
"",
|
||||
"<white>Other:",
|
||||
"<gray>• <white>.infinite<gray> - Use infinite-tagged items",
|
||||
|
||||
@@ -7,6 +7,10 @@ import me.trouper.dupealias.server.gui.admin.config.ConfigGui;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class MainAdminGui implements CommonItems {
|
||||
|
||||
@@ -21,18 +25,6 @@ public class MainAdminGui implements CommonItems {
|
||||
.titleMini("<gradient:#6b6bff:#9999ff><bold>DupeAlias Admin Panel</gradient>")
|
||||
.rows(5)
|
||||
|
||||
.item(11, ItemBuilder.create(player.getInventory().getItemInMainHand().isEmpty() ? Material.BARRIER : Material.DIAMOND_SWORD)
|
||||
.displayName("<gradient:#4ecdc4:#45b7d1><bold>Held Item Actions</bold></gradient>")
|
||||
.loreMiniMessage(
|
||||
"<gray>Manage tags for the item",
|
||||
"<gray>you're currently holding",
|
||||
"",
|
||||
"<yellow>▶ <white>Click to open menu"
|
||||
)
|
||||
.hideAllFlags()
|
||||
.build(),
|
||||
(q, event) -> manager.openHeldItemGui(player))
|
||||
|
||||
.item(13, ItemBuilder.create(Material.BOOKSHELF)
|
||||
.displayName("<gradient:#ff6b6b:#ffa726><bold>Global Rules</bold></gradient>")
|
||||
.loreMiniMessage(
|
||||
@@ -57,6 +49,17 @@ public class MainAdminGui implements CommonItems {
|
||||
.build(),
|
||||
(q, event) -> manager.openHelpGui(player))
|
||||
|
||||
.item(11, ItemBuilder.create(Material.CHEST)
|
||||
.displayName("<gradient:#4ecdc4:#45b7d1><bold>Bulk Item Actions</bold></gradient>")
|
||||
.loreMiniMessage(
|
||||
"<gray>Manage tags for multiple",
|
||||
"<gray>items at a time",
|
||||
"",
|
||||
"<yellow>▶ <white>Click to open menu"
|
||||
)
|
||||
.build(),
|
||||
(q,event) -> manager.openBulkTagGui(player))
|
||||
|
||||
.item(29, ItemBuilder.create(Material.COMPARATOR)
|
||||
.displayName("<gradient:#ff6bff:#ffa7ff><bold>Configuration</bold></gradient>")
|
||||
.loreMiniMessage(
|
||||
@@ -67,7 +70,8 @@ public class MainAdminGui implements CommonItems {
|
||||
)
|
||||
.build(), (q,event) -> manager.openConfigGui(player))
|
||||
|
||||
.item(31, manager.createPreviewItem(player.getInventory().getItemInMainHand()))
|
||||
.item(31, createPreviewItem(player.getInventory().getItemInMainHand()),
|
||||
(q,event) -> manager.openHeldItemGui(player))
|
||||
|
||||
.item(33, ItemBuilder.create(Material.DIAMOND)
|
||||
.displayName("<#AAAAFF><bold>Dupe<#00DDFF>Alias</bold> <white>Credits")
|
||||
@@ -87,4 +91,32 @@ public class MainAdminGui implements CommonItems {
|
||||
|
||||
gui.open(player);
|
||||
}
|
||||
|
||||
private ItemStack createPreviewItem(ItemStack stack) {
|
||||
if (stack.getType().isAir()) {
|
||||
return ItemBuilder.create(Material.GRAY_STAINED_GLASS_PANE)
|
||||
.displayName("<gray><bold>No Item Held</bold>")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
"<gray>Hold an item to see",
|
||||
"<gray>its current tag status",
|
||||
"",
|
||||
"<yellow>💡 <white>Hold an item and reopen this GUI"
|
||||
))
|
||||
.build();
|
||||
}
|
||||
|
||||
List<String> lore = manager.getItemTagStatus(stack);
|
||||
lore.addAll(List.of(
|
||||
"",
|
||||
"<gray>Manage tags for the item",
|
||||
"<gray>you're currently holding",
|
||||
"",
|
||||
"<yellow>▶ <white>Click to open menu"
|
||||
));
|
||||
|
||||
return ItemBuilder.create(stack.getType())
|
||||
.displayName("<gradient:#4ecdc4:#45b7d1><bold>Held Item Actions</bold></gradient>")
|
||||
.loreMiniMessage(lore)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import me.trouper.dupealias.DupeContext;
|
||||
import me.trouper.dupealias.server.ItemTag;
|
||||
import me.trouper.dupealias.server.gui.CommonItems;
|
||||
import me.trouper.dupealias.server.gui.admin.AdminPanelManager;
|
||||
import me.trouper.dupealias.server.gui.admin.config.sub.CommandRegexGui;
|
||||
import me.trouper.dupealias.server.gui.admin.config.sub.CommonConfigGui;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -28,21 +30,19 @@ public class ConfigGui implements DupeContext, CommonItems {
|
||||
.rows(5)
|
||||
.clickSound(Sound.UI_BUTTON_CLICK, 0.5f, 1.0f)
|
||||
|
||||
// Back button
|
||||
.item(0, BACK(), (g, e) -> manager.openMainGui(player))
|
||||
|
||||
// Dupe Cooldown
|
||||
.callback("dupe_cooldown", new QuickGui.GuiCallback() {
|
||||
@Override
|
||||
public void onInput(QuickGui gui, Player player, String input, QuickGui.InputSource source) {
|
||||
try {
|
||||
long millis = Long.parseLong(input);
|
||||
int millis = Integer.parseInt(input);
|
||||
if (millis < 0) {
|
||||
errorAny(player, "Cooldown cannot be negative!");
|
||||
return;
|
||||
}
|
||||
infoAny(player, "You have set the dupe cooldown to {0}ms.", input);
|
||||
getConfig().dupeCooldownMillis = millis;
|
||||
getConfig().baseDupeCooldownMillis = millis;
|
||||
getConfig().save();
|
||||
open(player);
|
||||
} catch (NumberFormatException ex) {
|
||||
@@ -52,17 +52,17 @@ public class ConfigGui implements DupeContext, CommonItems {
|
||||
}
|
||||
})
|
||||
.item(11, ItemBuilder.integerItem(Material.DIAMOND, "<aqua><bold>Dupe Command Cooldown</bold>", List.of(
|
||||
"<gray>How long players have to wait",
|
||||
"<gray>before running the /dupe command again.",
|
||||
"<gray>The base delay for command duping.",
|
||||
"<gray>use the permission dupealias.dupe.cooldown.<n>",
|
||||
"<gray>to configure cooldowns per-player",
|
||||
"",
|
||||
"<yellow>Current: <white>" + getConfig().dupeCooldownMillis + "ms",
|
||||
"<yellow>Current: <white>" + getConfig().baseDupeCooldownMillis + "ms",
|
||||
"",
|
||||
"<yellow>▶ <white>Click to modify"
|
||||
), (int) getConfig().dupeCooldownMillis), (g, e) ->
|
||||
), (int) getConfig().baseDupeCooldownMillis), (g, e) ->
|
||||
getDupe().getGuiListener().requestChatInput(g, player, "dupe_cooldown",
|
||||
"<aqua>Insert a long value of Milliseconds.\n<gray> 1000ms = 1 Second\n\n<yellow>Current value: <white>" + getConfig().dupeCooldownMillis + "ms"))
|
||||
"<aqua>Insert a long value of Milliseconds.\n<gray> 1000ms = 1 Second\n\n<yellow>Current value: <white>" + getConfig().baseDupeCooldownMillis + "ms"))
|
||||
|
||||
// Default Dupe GUI
|
||||
.item(12, ItemBuilder.create(Material.CHEST)
|
||||
.displayName("<green><bold>Default Dupe GUI</bold>")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
@@ -77,7 +77,6 @@ public class ConfigGui implements DupeContext, CommonItems {
|
||||
))
|
||||
.build(), (g, e) -> cycleDefaultGui(player))
|
||||
|
||||
// Final Command Regex
|
||||
.item(13, ItemBuilder.create(Material.BARRIER)
|
||||
.displayName("<red><bold>Final Command Regex</bold>")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
@@ -90,7 +89,6 @@ public class ConfigGui implements DupeContext, CommonItems {
|
||||
))
|
||||
.build(), (g, e) -> openFinalCommandRegexGui(player))
|
||||
|
||||
// Global Rules Editor
|
||||
.item(14, ItemBuilder.create(Material.COMMAND_BLOCK)
|
||||
.displayName("<light_purple><bold>Global Rules Editor</bold>")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
@@ -103,7 +101,6 @@ public class ConfigGui implements DupeContext, CommonItems {
|
||||
))
|
||||
.build(), (g, e) -> openGlobalRulesGui(player))
|
||||
|
||||
// Tag Lore Settings
|
||||
.item(15, ItemBuilder.create(Material.NAME_TAG)
|
||||
.displayName("<yellow><bold>Tag Lore Settings</bold>")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
@@ -114,7 +111,6 @@ public class ConfigGui implements DupeContext, CommonItems {
|
||||
))
|
||||
.build(), (g, e) -> openTagLoreGui(player, g))
|
||||
|
||||
// Common Settings
|
||||
.item(22,ItemBuilder.create(Material.LIGHT)
|
||||
.displayName("<gold><bold>Common Config</bold>")
|
||||
.loreMiniMessage(
|
||||
@@ -126,7 +122,6 @@ public class ConfigGui implements DupeContext, CommonItems {
|
||||
.build(),
|
||||
(g,e) -> openCommonGui(player, g))
|
||||
|
||||
// Replicator Settings
|
||||
.item(30, ItemBuilder.create(Material.REPEATER)
|
||||
.displayName("<blue><bold>Replicator Settings</bold>")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
@@ -139,7 +134,6 @@ public class ConfigGui implements DupeContext, CommonItems {
|
||||
))
|
||||
.build(), (g, e) -> openReplicatorGui(player, g))
|
||||
|
||||
// Chest Settings
|
||||
.item(31, ItemBuilder.create(Material.CHEST)
|
||||
.displayName("<gold><bold>Chest GUI Settings</bold>")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
@@ -151,7 +145,6 @@ public class ConfigGui implements DupeContext, CommonItems {
|
||||
))
|
||||
.build(), (g, e) -> openChestGui(player, g))
|
||||
|
||||
// Inventory Settings
|
||||
.item(32, ItemBuilder.create(Material.ENDER_CHEST)
|
||||
.displayName("<dark_purple><bold>Inventory GUI Settings</bold>")
|
||||
.loreMiniMessage(Arrays.asList(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.trouper.dupealias.server.gui.admin.config;
|
||||
package me.trouper.dupealias.server.gui.admin.config.sub;
|
||||
|
||||
import me.trouper.alias.server.systems.gui.QuickGui;
|
||||
import me.trouper.alias.server.systems.gui.QuickPaginatedGUI;
|
||||
@@ -1,8 +1,7 @@
|
||||
package me.trouper.dupealias.server.gui.admin.config;
|
||||
package me.trouper.dupealias.server.gui.admin.config.sub;
|
||||
|
||||
import me.trouper.alias.server.systems.gui.QuickGui;
|
||||
import me.trouper.alias.utils.ItemBuilder;
|
||||
import me.trouper.dupealias.DupeAlias;
|
||||
import me.trouper.dupealias.DupeContext;
|
||||
import me.trouper.dupealias.data.files.CommonConfig;
|
||||
import me.trouper.dupealias.server.gui.CommonItems;
|
||||
@@ -13,54 +12,34 @@ import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A GUI for modifying the settings within the common.json file.
|
||||
* This class provides an interface for changing general plugin settings
|
||||
* such as colors, prefixes, and display names.
|
||||
*/
|
||||
public class CommonConfigGui implements DupeContext, CommonItems {
|
||||
|
||||
private final AdminPanelManager manager;
|
||||
|
||||
/**
|
||||
* Constructs a new CommonConfigGui.
|
||||
*
|
||||
* @param manager The AdminPanelManager to handle navigation.
|
||||
*/
|
||||
public CommonConfigGui(AdminPanelManager manager) {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the Common Config GUI for a player.
|
||||
*
|
||||
* @param player The player to open the GUI for.
|
||||
*/
|
||||
public void open(Player player) {
|
||||
// Retrieve the current common configuration instance.
|
||||
CommonConfig config = getCommonConfig();
|
||||
|
||||
// Create the GUI using the QuickGui builder.
|
||||
QuickGui gui = QuickGui.create()
|
||||
.titleMini("<dark_aqua><bold>Common Config</bold></dark_aqua>")
|
||||
.defaultTimeout(30000)
|
||||
.rows(3)
|
||||
.clickSound(Sound.UI_BUTTON_CLICK, 0.5f, 1.0f)
|
||||
|
||||
// Back button to return to the main admin panel.
|
||||
.item(0, BACK(), (g, e) -> manager.openMainGui(player))
|
||||
|
||||
// Item and callback for modifying the main color.
|
||||
.callback("main_color", (g, p, input, source) -> {
|
||||
try {
|
||||
// Parse the hex string into an integer color value.
|
||||
int color = Integer.parseInt(input.replace("#", ""), 16);
|
||||
config.mainColor = color;
|
||||
config.save();
|
||||
// Reload the common settings in the core to apply changes immediately.
|
||||
|
||||
getInstance().getCommon().update(config.generateCommon());
|
||||
successAny(p, "Main color set to <#{0}>#{0}</#{0}>.", Integer.toHexString(color));
|
||||
open(p); // Re-open the GUI to show the updated value.
|
||||
open(p);
|
||||
} catch (NumberFormatException ex) {
|
||||
errorAny(p, "Invalid hex color format. Use RRGGBB (e.g., AAAAFF).");
|
||||
}
|
||||
@@ -74,11 +53,10 @@ public class CommonConfigGui implements DupeContext, CommonItems {
|
||||
"",
|
||||
"<yellow>▶ <white>Click to modify"
|
||||
)).build(), (g, e) ->
|
||||
// Request chat input from the player.
|
||||
|
||||
getDupe().getGuiListener().requestChatInput(g, player, "main_color",
|
||||
"<blue>Enter a hex color code for the main color.\n<gray>Example: AAAAFF"))
|
||||
|
||||
// Item and callback for modifying the secondary color.
|
||||
.callback("secondary_color", (g, p, input, source) -> {
|
||||
try {
|
||||
int color = Integer.parseInt(input.replace("#", ""), 16);
|
||||
@@ -103,7 +81,6 @@ public class CommonConfigGui implements DupeContext, CommonItems {
|
||||
getDupe().getGuiListener().requestChatInput(g, player, "secondary_color",
|
||||
"<aqua>Enter a hex color code for the secondary color.\n<gray>Example: 00DDFF"))
|
||||
|
||||
// Item and callback for modifying the plugin name.
|
||||
.callback("plugin_name", (g, p, input, source) -> {
|
||||
config.pluginName = input;
|
||||
config.save();
|
||||
@@ -123,7 +100,6 @@ public class CommonConfigGui implements DupeContext, CommonItems {
|
||||
getDupe().getGuiListener().requestChatInput(g, player, "plugin_name",
|
||||
"<green>Enter the new plugin name."))
|
||||
|
||||
// Item and callback for modifying the flat prefix.
|
||||
.callback("flat_prefix", (g, p, input, source) -> {
|
||||
config.flatPrefix = input;
|
||||
config.save();
|
||||
@@ -144,7 +120,6 @@ public class CommonConfigGui implements DupeContext, CommonItems {
|
||||
getDupe().getGuiListener().requestChatInput(g, player, "flat_prefix",
|
||||
"<gray>Enter the new flat prefix."))
|
||||
|
||||
// Item and click handler for toggling flat mode.
|
||||
.item(14, ItemBuilder.create(config.flat ? Material.LIME_DYE : Material.GRAY_DYE)
|
||||
.displayName("<white><bold>Flat Mode</bold>")
|
||||
.loreMiniMessage(List.of(
|
||||
@@ -159,14 +134,12 @@ public class CommonConfigGui implements DupeContext, CommonItems {
|
||||
config.save();
|
||||
getInstance().getCommon().update(config.generateCommon());
|
||||
infoAny(player, "Flat mode set to: {0}", config.flat);
|
||||
open(player); // Re-open to update the item's appearance.
|
||||
open(player);
|
||||
})
|
||||
|
||||
// Fill the rest of the GUI with empty panes.
|
||||
.fillEmpty(EMPTY())
|
||||
.build();
|
||||
|
||||
// Display the GUI to the player.
|
||||
gui.open(player);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,10 @@ import me.trouper.alias.server.systems.gui.QuickGui;
|
||||
import me.trouper.alias.utils.ItemBuilder;
|
||||
import me.trouper.dupealias.DupeContext;
|
||||
import me.trouper.dupealias.server.gui.CommonItems;
|
||||
import me.trouper.dupealias.server.gui.dupe.sub.AbstractDupeGui;
|
||||
import me.trouper.dupealias.server.gui.dupe.sub.DupeChestGui;
|
||||
import me.trouper.dupealias.server.gui.dupe.sub.DupeInventoryGui;
|
||||
import me.trouper.dupealias.server.gui.dupe.sub.DupeReplicatorGui;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
@@ -18,6 +22,11 @@ public class DupeGui implements DupeContext, CommonItems {
|
||||
public final DupeChestGui chestGui = new DupeChestGui();
|
||||
|
||||
public void openMainGui(Player player) {
|
||||
if (!player.hasPermission("dupealias.gui")) {
|
||||
warningAny(player,"You do not have permission to use the main dupe gui.");
|
||||
return;
|
||||
}
|
||||
|
||||
QuickGui gui = QuickGui.create()
|
||||
.rows(5)
|
||||
.titleMini("<aqua><bold>Available GUIs")
|
||||
@@ -85,7 +94,7 @@ public class DupeGui implements DupeContext, CommonItems {
|
||||
}
|
||||
} else {
|
||||
player.closeInventory();
|
||||
warningAny(player,"You do not have permission to use that GUI!");
|
||||
warningAny(player,"You do not have permission to use that GUI.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.trouper.dupealias.server.gui.dupe;
|
||||
package me.trouper.dupealias.server.gui.dupe.sub;
|
||||
|
||||
import me.trouper.dupealias.DupeContext;
|
||||
import me.trouper.dupealias.server.gui.CommonItems;
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.trouper.dupealias.server.gui.dupe;
|
||||
package me.trouper.dupealias.server.gui.dupe.sub;
|
||||
|
||||
import me.trouper.alias.server.systems.gui.QuickGui;
|
||||
import me.trouper.dupealias.DupeContext;
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.trouper.dupealias.server.gui.dupe;
|
||||
package me.trouper.dupealias.server.gui.dupe.sub;
|
||||
|
||||
import me.trouper.alias.server.systems.gui.QuickGui;
|
||||
import org.bukkit.Material;
|
||||
@@ -6,7 +6,9 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DupeChestGui extends AbstractDupeGui<DupeChestGui.ChestSession> {
|
||||
@@ -61,6 +63,23 @@ public class DupeChestGui extends AbstractDupeGui<DupeChestGui.ChestSession> {
|
||||
populateInventory(getGui().getInventory());
|
||||
}
|
||||
|
||||
public List<ItemStack> getInputItems() {
|
||||
List<ItemStack> items = new ArrayList<>();
|
||||
Inventory inv = getGui().getInventory();
|
||||
|
||||
for (int row = 0; row < 6; row++) {
|
||||
int rowStart = row * 9;
|
||||
for (int col = 0; col < 4; col++) {
|
||||
int index = rowStart + col;
|
||||
ItemStack item = inv.getItem(index);
|
||||
if (item == null || item.isEmpty()) continue;
|
||||
items.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private void resetItemDelay(int slot) {
|
||||
ItemDelayInfo info = itemDelays.get(slot);
|
||||
if (info != null) {
|
||||
@@ -1,8 +1,7 @@
|
||||
package me.trouper.dupealias.server.gui.dupe;
|
||||
package me.trouper.dupealias.server.gui.dupe.sub;
|
||||
|
||||
import me.trouper.alias.server.systems.gui.QuickGui;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.EntityEquipment;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.trouper.dupealias.server.gui.dupe;
|
||||
package me.trouper.dupealias.server.gui.dupe.sub;
|
||||
|
||||
import me.trouper.alias.server.systems.gui.QuickGui;
|
||||
import me.trouper.alias.utils.FormatUtils;
|
||||
@@ -14,12 +14,14 @@ commands:
|
||||
- da
|
||||
dupe:
|
||||
description: A command to duplicate items.
|
||||
permission: dupealias.dupe
|
||||
usage: /dupe [gui|<integer>]
|
||||
permissions:
|
||||
dupealias.admin:
|
||||
description: Allows access to the /dupealias admin command.
|
||||
default: op
|
||||
dupealias.infinite:
|
||||
description: Allows the use of items tagged as "infinite".
|
||||
default: true
|
||||
dupealias.final.bypass:
|
||||
description: Allows the bypassing of final item restrictions
|
||||
default: op
|
||||
@@ -30,37 +32,33 @@ permissions:
|
||||
description: Allows the duping of unique items
|
||||
default: op
|
||||
dupealias.dupe:
|
||||
description: Allows duplication of items through the command.
|
||||
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.cooldownbypass: false
|
||||
dupealias.dupe.cooldownbypass:
|
||||
description: Bypass the cooldown for /dupe
|
||||
default: op
|
||||
dupealias.infinite:
|
||||
description: Allows the use of items tagged as "infinite".
|
||||
default: true
|
||||
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.gui:
|
||||
description: Allows access to the full dupe 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
|
||||
children:
|
||||
dupealias.gui.replicator: true
|
||||
dupealias.gui.inventory: true
|
||||
dupealias.gui.chest: true
|
||||
dupealias.gui.replicator:
|
||||
description: The gui which lets you shift click copies of a single item.
|
||||
description: An animated GUI which replicates a single input item.
|
||||
default: true
|
||||
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 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.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.inventory:
|
||||
description: The gui which shows your inventory and armor on top.
|
||||
default: true
|
||||
children:
|
||||
dupealias.gui.inventory.refresh.integerhere: false # Controls the time it will take a duplicated item to refill or refresh in the GUI. Always takes the lowest number on a permission holder.
|
||||
dupealias.gui.inventory.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.chest:
|
||||
description: A gui which items can be put in and taken out as copies.
|
||||
default: true
|
||||
children:
|
||||
dupealias.gui.chest.keep: false # Controls if a player should keep their previous chest session with items in it. This does not persist across reboots.
|
||||
dupealias.gui.chest.refresh.integerhere: false # Controls the time it will take a duplicated item to refill or refresh in the GUI. Always takes the lowest number on a permission holder.
|
||||
dupealias.gui.chest.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.chest.keepondeath: false # Controls if a player should keep their chest GUI when they die. This only overrides the globally configured value if it is granted as true.
|
||||
Reference in New Issue
Block a user