Compare commits
10 commits
5da488d2b2
...
e8d516020c
| Author | SHA1 | Date | |
|---|---|---|---|
| e8d516020c | |||
| a0c1ab8ca1 | |||
| 1d8c0ca483 | |||
| ceb4c804b7 | |||
| bfdf197de4 | |||
| b80fefa6a4 | |||
| f2be6d6ece | |||
| 0f2735f171 | |||
| 957468b6b6 | |||
| f6cccbcd33 |
181 changed files with 7830 additions and 5873 deletions
25
.classpath
Normal file
25
.classpath
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" output="bin/main" path="src/main/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="gradle_scope" value="main"/>
|
||||||
|
<attribute name="gradle_used_by_scope" value="main,test"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="bin/main" path="src/main/resources">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="gradle_scope" value="main"/>
|
||||||
|
<attribute name="gradle_used_by_scope" value="main,test"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="bin/test" path="src/test/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="gradle_scope" value="test"/>
|
||||||
|
<attribute name="gradle_used_by_scope" value="test"/>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21/"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||||
|
<classpathentry kind="output" path="bin/default"/>
|
||||||
|
</classpath>
|
||||||
34
.project
Normal file
34
.project
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>LoMines</name>
|
||||||
|
<comment>Project LoMines created by Buildship.</comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||||
|
</natures>
|
||||||
|
<filteredResources>
|
||||||
|
<filter>
|
||||||
|
<id>1783342209787</id>
|
||||||
|
<name></name>
|
||||||
|
<type>30</type>
|
||||||
|
<matcher>
|
||||||
|
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
||||||
|
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
||||||
|
</matcher>
|
||||||
|
</filter>
|
||||||
|
</filteredResources>
|
||||||
|
</projectDescription>
|
||||||
13
.settings/org.eclipse.buildship.core.prefs
Normal file
13
.settings/org.eclipse.buildship.core.prefs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
arguments=--init-script /home/loki/.cache/opencode/bin/jdtls/config_linux/org.eclipse.osgi/57/0/.cp/gradle/init/init.gradle
|
||||||
|
auto.sync=false
|
||||||
|
build.scans.enabled=false
|
||||||
|
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
|
||||||
|
connection.project.dir=
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
gradle.user.home=
|
||||||
|
java.home=/usr/lib/jvm/java-21-openjdk
|
||||||
|
jvm.arguments=
|
||||||
|
offline.mode=false
|
||||||
|
override.workspace.settings=true
|
||||||
|
show.console.view=true
|
||||||
|
show.executions.view=true
|
||||||
966
ARCHITECTURE.md
966
ARCHITECTURE.md
File diff suppressed because it is too large
Load diff
292
README.md
292
README.md
|
|
@ -1,219 +1,141 @@
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
# LoMines
|
# LoMines
|
||||||
|
|
||||||
Advanced mine management plugin for Paper 1.21.4+
|
Multi-region mine management plugin with WorldGuard integration and player statistics for Paper servers.
|
||||||
|
|
||||||
## Features
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
- **Multiple fill modes**: Cuboid (fill entire region) or Mask (fill marked positions only)
|
[English](#english) | [Русский](#russian)
|
||||||
- **Weighted block system**: Configure different blocks with spawn chances
|
|
||||||
- **Automatic resets**: Timer-based or percentage-based triggers
|
|
||||||
- **WorldGuard integration**: Auto-create regions for new mines
|
|
||||||
- **PlaceholderAPI support**: Display mine info in other plugins
|
|
||||||
- **GUI editor**: In-game mine configuration editor
|
|
||||||
- **Player statistics**: Track blocks mined, mines reset, playtime
|
|
||||||
|
|
||||||
## Commands
|
</div>
|
||||||
|
|
||||||
### Admin Commands
|
---
|
||||||
|
|
||||||
|
<a name="english"></a>
|
||||||
|
|
||||||
|
## English
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
LoMines is an advanced mine management plugin for Paper 1.19.2+ servers. It supports multiple fill modes, weighted block systems, automatic resets, WorldGuard integration, in-game GUI editor, and player statistics.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
| Feature | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| Multiple fill modes | Cuboid (fill entire region) or Mask (fill marked positions) |
|
||||||
|
| Weighted blocks | Configure spawn chances per block type |
|
||||||
|
| Automatic resets | Timer-based or percentage-based triggers |
|
||||||
|
| WorldGuard integration | Auto-create regions for new mines |
|
||||||
|
| GUI editor | In-game mine configuration |
|
||||||
|
| Player statistics | Track blocks mined, resets, playtime |
|
||||||
|
| Leaderboards | Top players per mine or global |
|
||||||
|
| Group wand | Create multiple mines from batch selections |
|
||||||
|
| PlaceholderAPI | Display mine info in other plugins |
|
||||||
|
| Holograms | DecentHolograms and HolographicDisplays support |
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
| Command | Permission | Description |
|
| Command | Permission | Description |
|
||||||
|---------|------------|-------------|
|
|---------|------------|-------------|
|
||||||
| `/lm create <name>` | `lomines.admin.create` | Create a new mine |
|
| `/lm create <name>` | `lomines.admin` | Create a new mine |
|
||||||
| `/lm delete <name>` | `lomines.admin.delete` | Delete a mine |
|
| `/lm delete <name>` | `lomines.admin` | Delete a mine |
|
||||||
| `/lm reset <name> [silent]` | `lomines.admin.reset` | Reset a mine manually |
|
| `/lm reset <name> [silent]` | `lomines.admin` | Reset a mine |
|
||||||
| `/lm reload` | `lomines.admin.reload` | Reload all mine configs |
|
| `/lm reload` | `lomines.admin` | Reload configs |
|
||||||
| `/lm list` | `lomines.admin.list` | List all mines |
|
| `/lm list` | `lomines.admin` | List all mines |
|
||||||
| `/lm edit <mine>` | `lomines.admin.edit` | Open GUI editor for a mine |
|
| `/lm edit <mine>` | `lomines.admin.edit` | Open GUI editor |
|
||||||
| `/lm setteleport <mine>` | `lomines.admin.setteleport` | Set teleport location (your current position) |
|
| `/lm wand` | `lomines.admin.wand` | Get selection wand |
|
||||||
| `/lm setspawn <mine>` | `lomines.admin.setspawn` | Set safe spawn location for stuck players |
|
| `/lm group <prefix>` | `lomines.admin.wand` | Batch mine creation |
|
||||||
| `/lm clearspawn <mine>` | `lomines.admin.setspawn` | Clear spawn location |
|
| `/lm setteleport <mine>` | `lomines.admin.setteleport` | Set teleport point |
|
||||||
| `/lm maskscan <mine>` | `lomines.admin.maskscan` | Scan mask marker blocks |
|
| `/lm setspawn <mine>` | `lomines.admin.setspawn` | Set safe spawn |
|
||||||
|
| `/lm maskscan <mine>` | `lomines.admin.maskscan` | Scan mask markers |
|
||||||
### Player Commands
|
| `/lm stats [player]` | `lomines.stats` | View statistics |
|
||||||
|
|
||||||
| Command | Permission | Description |
|
|
||||||
|---------|------------|-------------|
|
|
||||||
| `/lm stats [player]` | `lomines.stats` | View mining statistics |
|
|
||||||
| `/lm top [mine] [limit]` | `lomines.stats` | View leaderboard |
|
| `/lm top [mine] [limit]` | `lomines.stats` | View leaderboard |
|
||||||
|
|
||||||
### Wand Commands
|
### Placeholders
|
||||||
|
|
||||||
| Command | Permission | Description |
|
| Placeholder | Description |
|
||||||
|---------|------------|-------------|
|
|-------------|-------------|
|
||||||
| `/lm wand` | `lomines.admin.wand` | Get the region selection wand |
|
| `%lomines_mine_<name>_blocks%` | Current blocks in mine |
|
||||||
| `/lm group <prefix>` | `lomines.admin.wand` | Create mines from group wand selections |
|
| `%lomines_mine_<name>_percent%` | Fill percentage |
|
||||||
|
| `%lomines_mine_<name>_remaining%` | Remaining blocks |
|
||||||
|
| `%lomines_mine_<name>_resettime%` | Time since reset |
|
||||||
|
| `%lomines_player_blocksmined%` | Total blocks mined |
|
||||||
|
| `%lomines_player_rank%` | Leaderboard rank |
|
||||||
|
| `%lomines_count%` | Total mines count |
|
||||||
|
|
||||||
## Permissions
|
### Dependencies
|
||||||
|
|
||||||
```yaml
|
- Required: Paper 1.19.2+, Java 21+
|
||||||
lomines.admin:
|
- Optional: PlaceholderAPI, WorldGuard, DecentHolograms, HolographicDisplays, Oraxen, ItemsAdder
|
||||||
description: All admin commands
|
|
||||||
default: op
|
|
||||||
children:
|
|
||||||
lomines.use: true
|
|
||||||
lomines.admin.create: true
|
|
||||||
lomines.admin.delete: true
|
|
||||||
lomines.admin.reset: true
|
|
||||||
lomines.admin.reload: true
|
|
||||||
lomines.admin.list: true
|
|
||||||
lomines.admin.edit: true
|
|
||||||
lomines.admin.setteleport: true
|
|
||||||
lomines.admin.setspawn: true
|
|
||||||
lomines.admin.maskscan: true
|
|
||||||
lomines.admin.wand: true
|
|
||||||
lomines.stats: true
|
|
||||||
lomines.stats.others: true
|
|
||||||
|
|
||||||
lomines.use:
|
### Installation
|
||||||
description: Basic usage
|
|
||||||
default: true
|
|
||||||
|
|
||||||
lomines.stats:
|
1. Drop the jar into `plugins/`
|
||||||
description: View own stats and leaderboards
|
2. Restart the server
|
||||||
default: true
|
3. Configure `plugins/LoMines/mines/` and `plugins/LoMines/defaults.yml`
|
||||||
|
|
||||||
lomines.stats.others:
|
---
|
||||||
description: View other players' stats
|
|
||||||
default: op
|
|
||||||
```
|
|
||||||
|
|
||||||
## Placeholders (PlaceholderAPI)
|
<a name="russian"></a>
|
||||||
|
|
||||||
### Mine Information
|
## Русский
|
||||||
|
|
||||||
| Placeholder | Description | Example |
|
### Обзор
|
||||||
|-------------|-------------|---------|
|
|
||||||
| `%lomines_mine_<name>_name%` | Mine name | `mymine` |
|
|
||||||
| `%lomines_mine_<name>_blocks%` | Current blocks | `8542` |
|
|
||||||
| `%lomines_mine_<name>_total%` | Total volume | `10000` |
|
|
||||||
| `%lomines_mine_<name>_percent%` | Fill percentage (1 decimal) | `85.4` |
|
|
||||||
| `%lomines_mine_<name>_percentint%` | Fill percentage (integer) | `85` |
|
|
||||||
| `%lomines_mine_<name>_world%` | World name | `world` |
|
|
||||||
| `%lomines_mine_<name>_remaining%` | Remaining blocks | `1458` |
|
|
||||||
| `%lomines_mine_<name>_resettime%` | Time since reset (mm:ss) | `03:45` |
|
|
||||||
| `%lomines_mine_<name>_resetseconds%` | Seconds since reset | `225` |
|
|
||||||
|
|
||||||
### Player Statistics
|
LoMines - продвинутый плагин управления шахтами для Paper 1.19.2+ серверов. Поддерживает несколько режимов заполнения, взвешенные блоки, автоматические сбросы, интеграцию с WorldGuard, GUI-редактор и статистику игроков.
|
||||||
|
|
||||||
| Placeholder | Description | Example |
|
### Возможности
|
||||||
|-------------|-------------|---------|
|
|
||||||
| `%lomines_player_blocksmined%` | Total blocks mined | `15234` |
|
|
||||||
| `%lomines_player_minesreset%` | Mines reset count | `45` |
|
|
||||||
| `%lomines_player_playtime%` | Formatted play time | `2h 15m` |
|
|
||||||
| `%lomines_player_rank%` | Leaderboard rank | `3` |
|
|
||||||
|
|
||||||
### Global
|
| Возможность | Описание |
|
||||||
|
|-------------|----------|
|
||||||
|
| Режимы заполнения | Cuboid (весь регион) или Mask (по меткам) |
|
||||||
|
| Взвешенные блоки | Настройка шансов появления для каждого блока |
|
||||||
|
| Авто-сбросы | По таймеру или по проценту добычи |
|
||||||
|
| WorldGuard | Авто-создание регионов для новых шахт |
|
||||||
|
| GUI-редактор | Настройка шахт прямо в игре |
|
||||||
|
| Статистика | Добыто блоков, сбросы, время игры |
|
||||||
|
| Лидерборды | Топ игроков по шахте или глобально |
|
||||||
|
| Групповая палка | Создание нескольких шахт за раз |
|
||||||
|
| PlaceholderAPI | Информация о шахтах в других плагинах |
|
||||||
|
| Голограммы | Поддержка DecentHolograms и HolographicDisplays |
|
||||||
|
|
||||||
| Placeholder | Description | Example |
|
### Команды
|
||||||
|-------------|-------------|---------|
|
|
||||||
| `%lomines_count%` | Total number of mines | `12` |
|
|
||||||
|
|
||||||
## Configuration
|
| Команда | Право | Описание |
|
||||||
|
|---------|-------|----------|
|
||||||
|
| `/lm create <имя>` | `lomines.admin` | Создать шахту |
|
||||||
|
| `/lm delete <имя>` | `lomines.admin` | Удалить шахту |
|
||||||
|
| `/lm reset <имя>` | `lomines.admin` | Сбросить шахту |
|
||||||
|
| `/lm edit <имя>` | `lomines.admin.edit` | Открыть GUI-редактор |
|
||||||
|
| `/lm wand` | `lomines.admin.wand` | Получить палку выделения |
|
||||||
|
| `/lm stats [игрок]` | `lomines.stats` | Статистика |
|
||||||
|
| `/lm top [шахта]` | `lomines.stats` | Лидерборд |
|
||||||
|
|
||||||
### defaults.yml
|
### Зависимости
|
||||||
|
|
||||||
Default configuration applied to all mines:
|
- Обязательные: Paper 1.19.2+, Java 21+
|
||||||
|
- Опциональные: PlaceholderAPI, WorldGuard, DecentHolograms, Oraxen, ItemsAdder
|
||||||
|
|
||||||
```yaml
|
### Установка
|
||||||
# Reset settings
|
|
||||||
reset:
|
|
||||||
interval: "5m" # Reset interval (30s, 5m, 2h, 1d)
|
|
||||||
percent-trigger: 10.0 # Trigger reset when X% of blocks mined
|
|
||||||
percent-enabled: false # Enable percentage trigger
|
|
||||||
commands: [] # Commands to run on reset
|
|
||||||
broadcast: "" # Broadcast message on reset
|
|
||||||
|
|
||||||
# Block settings
|
1. Положите jar в папку `plugins/`
|
||||||
blocks:
|
2. Перезапустите сервер
|
||||||
fill-mode: CUBOID # CUBOID or MASK
|
3. Настройте `plugins/LoMines/mines/` и `plugins/LoMines/defaults.yml`
|
||||||
mask:
|
|
||||||
marker: "pink_concrete" # Marker block for mask mode
|
|
||||||
|
|
||||||
# Teleport settings
|
---
|
||||||
teleport:
|
|
||||||
enabled: false
|
|
||||||
location: ""
|
|
||||||
|
|
||||||
# Safe spawn for stuck players
|
### Links
|
||||||
player-spawn:
|
|
||||||
enabled: false
|
|
||||||
location: ""
|
|
||||||
|
|
||||||
# UI settings
|
- [Releases](../../releases)
|
||||||
ui:
|
- [Issues](../../issues)
|
||||||
actionbar:
|
- [License](LICENSE)
|
||||||
enabled: true
|
|
||||||
format: "<green>{mine}</green> <gray>{percent}%</gray> <dark_gray>({time})"
|
|
||||||
range: 50.0
|
|
||||||
|
|
||||||
# WorldGuard integration
|
### License
|
||||||
worldguard:
|
|
||||||
enabled: false
|
|
||||||
region-template: "{mine_name}_{random_4}"
|
|
||||||
owners: []
|
|
||||||
members: []
|
|
||||||
flags:
|
|
||||||
- "passthrough=deny"
|
|
||||||
- "build=allow"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage Examples
|
GNU General Public License v3.0
|
||||||
|
|
||||||
### Creating a Simple Mine
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Get the wand
|
|
||||||
/lm wand
|
|
||||||
|
|
||||||
# 2. Select two corners (left and right click)
|
|
||||||
# 3. Create the mine
|
|
||||||
/lm create mymine
|
|
||||||
|
|
||||||
# 4. Edit the config file or use GUI
|
|
||||||
/lm edit mymine
|
|
||||||
```
|
|
||||||
|
|
||||||
### Setting Up Teleport
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Stand where players should be teleported
|
|
||||||
/lm setteleport mymine
|
|
||||||
|
|
||||||
# Stand where STUCK players should spawn (optional, falls back to teleport location)
|
|
||||||
/lm setspawn mymine
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using Mask Fill Mode
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Paint positions with pink_concrete (or configured marker)
|
|
||||||
# 2. Scan the markers
|
|
||||||
/lm maskscan mymine
|
|
||||||
|
|
||||||
# Mine will now only fill at marked positions
|
|
||||||
```
|
|
||||||
|
|
||||||
### WorldGuard Auto-Regions
|
|
||||||
|
|
||||||
Enable in `plugins/LoMines/mines/_defaults.yml`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
worldguard:
|
|
||||||
enabled: true
|
|
||||||
region-template: "mine_{mine_name}_{random_4}"
|
|
||||||
flags:
|
|
||||||
- "passthrough=deny"
|
|
||||||
- "build=allow"
|
|
||||||
```
|
|
||||||
|
|
||||||
When you create a mine, a WorldGuard region `mine_mymine_7392` is automatically created.
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
- Paper 1.21.4+
|
|
||||||
- Java 21+
|
|
||||||
- Optional: PlaceholderAPI, WorldGuard
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
plugins {
|
plugins {
|
||||||
java
|
java
|
||||||
id("com.github.johnrengelman.shadow") version "8.1.1"
|
id("com.github.johnrengelman.shadow") version "8.1.1"
|
||||||
|
checkstyle
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "com.loki"
|
group = "com.loki"
|
||||||
|
|
@ -13,60 +14,67 @@ java {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.withType<JavaCompile> {
|
||||||
|
options.release.set(21)
|
||||||
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven("https://repo.papermc.io/repository/maven-public/")
|
maven("https://repo.papermc.io/repository/maven-public/")
|
||||||
|
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
|
||||||
|
maven("https://maven.enginehub.org/repo/")
|
||||||
|
maven("https://repo.codemc.io/repository/maven-public/")
|
||||||
maven("https://jitpack.io")
|
maven("https://jitpack.io")
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoAPI: libs/lolib*.jar — fileTree индексируется Gradle / Java LS стабильнее, чем files(singleJar)
|
|
||||||
val lolibTree = fileTree("libs") { include("lolib*.jar") }
|
|
||||||
if (lolibTree.files.isEmpty()) {
|
|
||||||
throw GradleException(
|
|
||||||
"Missing libs/lolib*.jar — add LoAPI fat jar to ${rootProject.file("libs").absolutePath}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Paper API 1.21.4 (includes Adventure API)
|
|
||||||
compileOnly("io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT")
|
compileOnly("io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT")
|
||||||
|
implementation("org.apache.commons:commons-math3:3.6.1")
|
||||||
|
implementation("commons-io:commons-io:2.18.0")
|
||||||
|
compileOnly("me.clip:placeholderapi:2.11.6")
|
||||||
|
compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.0.13")
|
||||||
|
compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.3.10")
|
||||||
|
|
||||||
// LoAPI (local)
|
|
||||||
implementation(lolibTree)
|
|
||||||
|
|
||||||
// Runtime dependencies (loaded via DependencyManager from LoAPI)
|
|
||||||
// These are marked as compileOnly because they will be downloaded at runtime
|
|
||||||
compileOnly("org.apache.commons:commons-math3:3.6.1")
|
|
||||||
compileOnly("commons-io:commons-io:2.18.0")
|
|
||||||
|
|
||||||
// Adventure API (included in Paper, but explicit for clarity)
|
|
||||||
compileOnly("net.kyori:adventure-api:4.17.0")
|
|
||||||
compileOnly("net.kyori:adventure-text-minimessage:4.17.0")
|
|
||||||
|
|
||||||
// Optional plugin integrations (soft dependencies)
|
|
||||||
// compileOnly("com.github.oraxen:oraxen:1.180.0")
|
|
||||||
// compileOnly("com.github.LoneDev6:api-itemsadder:3.6.4")
|
|
||||||
|
|
||||||
// Test dependencies
|
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter:5.11.4")
|
testImplementation("org.junit.jupiter:junit-jupiter:5.11.4")
|
||||||
testImplementation("io.kotest:kotest-runner-junit5:5.9.1")
|
testImplementation("io.kotest:kotest-runner-junit5:5.9.1")
|
||||||
testImplementation("io.kotest:kotest-assertions-core:5.9.1")
|
testImplementation("io.kotest:kotest-assertions-core:5.9.1")
|
||||||
testImplementation("io.kotest:kotest-property:5.9.1")
|
testImplementation("io.kotest:kotest-property:5.9.1")
|
||||||
testImplementation("org.mockito:mockito-core:5.14.2")
|
testImplementation("org.mockito:mockito-core:5.14.2")
|
||||||
testImplementation("io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT")
|
testImplementation("org.mockito:mockito-inline:5.2.0")
|
||||||
testImplementation("org.apache.commons:commons-math3:3.6.1")
|
testImplementation("org.mockbukkit.mockbukkit:mockbukkit-v1.21:4.110.0") {
|
||||||
testImplementation("commons-io:commons-io:2.18.0")
|
exclude(group = "org.junit.jupiter", module = "junit-jupiter-api")
|
||||||
|
}
|
||||||
|
testImplementation("io.papermc.paper:paper-api:1.21.11-R0.1-SNAPSHOT")
|
||||||
|
testImplementation("net.kyori:adventure-api:4.17.0")
|
||||||
|
testImplementation("net.kyori:adventure-text-minimessage:4.17.0")
|
||||||
|
testImplementation("com.sk89q.worldguard:worldguard-bukkit:7.0.13")
|
||||||
|
}
|
||||||
|
|
||||||
|
checkstyle {
|
||||||
|
toolVersion = "10.21.4"
|
||||||
|
configFile = file("config/checkstyle/checkstyle.xml")
|
||||||
|
isIgnoreFailures = false
|
||||||
|
maxErrors = 0
|
||||||
|
maxWarnings = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<Checkstyle>().configureEach {
|
||||||
|
reports {
|
||||||
|
xml.required.set(true)
|
||||||
|
html.required.set(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
archiveClassifier.set("")
|
archiveClassifier.set("")
|
||||||
|
|
||||||
// Relocate LoAPI to avoid conflicts
|
// Temporary: disable relocate to avoid ASM Java 21 bytecode issues
|
||||||
relocate("dev.loki.lomines.libs.lolib", "com.loki.lomines.libs.lolib")
|
// relocate(
|
||||||
|
// "dev.loki.lomines.libs.lolib",
|
||||||
// Don't minimize - causes issues with Java 21
|
// "com.loki.lomines.libs.lolib"
|
||||||
// minimize()
|
// )
|
||||||
}
|
}
|
||||||
|
|
||||||
build {
|
build {
|
||||||
|
|
@ -82,8 +90,10 @@ tasks {
|
||||||
"version" to version,
|
"version" to version,
|
||||||
"description" to description
|
"description" to description
|
||||||
)
|
)
|
||||||
|
|
||||||
inputs.properties(props)
|
inputs.properties(props)
|
||||||
filteringCharset = "UTF-8"
|
filteringCharset = "UTF-8"
|
||||||
|
|
||||||
filesMatching("plugin.yml") {
|
filesMatching("plugin.yml") {
|
||||||
expand(props)
|
expand(props)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
85
config/checkstyle/checkstyle.xml
Normal file
85
config/checkstyle/checkstyle.xml
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE module PUBLIC
|
||||||
|
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||||
|
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||||
|
|
||||||
|
<module name="Checker">
|
||||||
|
<property name="charset" value="UTF-8"/>
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
<property name="fileExtensions" value="java"/>
|
||||||
|
|
||||||
|
<module name="SuppressWarningsFilter"/>
|
||||||
|
|
||||||
|
<module name="LineLength">
|
||||||
|
<property name="max" value="150"/>
|
||||||
|
<property name="ignorePattern"
|
||||||
|
value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="TreeWalker">
|
||||||
|
<module name="SuppressWarningsHolder"/>
|
||||||
|
|
||||||
|
<module name="MethodLength">
|
||||||
|
<property name="max" value="30"/>
|
||||||
|
<property name="countEmpty" value="false"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="CyclomaticComplexity">
|
||||||
|
<property name="max" value="20"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="ParameterNumber">
|
||||||
|
<property name="max" value="7"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="AvoidStarImport"/>
|
||||||
|
<module name="UnusedImports"/>
|
||||||
|
<module name="RedundantImport"/>
|
||||||
|
|
||||||
|
<module name="PackageName">
|
||||||
|
<property name="format" value="^[a-z]+(\.[a-zA-Z][a-zA-Z0-9]*)*$"/>
|
||||||
|
</module>
|
||||||
|
<module name="TypeName"/>
|
||||||
|
<module name="MethodName"/>
|
||||||
|
<module name="MemberName"/>
|
||||||
|
<module name="ParameterName"/>
|
||||||
|
<module name="LocalVariableName"/>
|
||||||
|
<module name="ConstantName">
|
||||||
|
<property name="format" value="^log$|^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="NeedBraces"/>
|
||||||
|
<module name="LeftCurly"/>
|
||||||
|
<module name="RightCurly"/>
|
||||||
|
|
||||||
|
<module name="ModifierOrder"/>
|
||||||
|
<module name="RedundantModifier"/>
|
||||||
|
<module name="EmptyStatement"/>
|
||||||
|
<module name="EqualsHashCode"/>
|
||||||
|
<module name="SimplifyBooleanExpression"/>
|
||||||
|
<module name="SimplifyBooleanReturn"/>
|
||||||
|
<module name="StringLiteralEquality"/>
|
||||||
|
<module name="DefaultComesLast"/>
|
||||||
|
<module name="MultipleVariableDeclarations"/>
|
||||||
|
<module name="OneStatementPerLine"/>
|
||||||
|
<module name="FallThrough"/>
|
||||||
|
<module name="HideUtilityClassConstructor"/>
|
||||||
|
<module name="OneTopLevelClass"/>
|
||||||
|
<module name="UpperEll"/>
|
||||||
|
<module name="ArrayTypeStyle"/>
|
||||||
|
<module name="MissingSwitchDefault"/>
|
||||||
|
<module name="OuterTypeFilename"/>
|
||||||
|
|
||||||
|
<module name="EmptyBlock">
|
||||||
|
<property name="option" value="text"/>
|
||||||
|
</module>
|
||||||
|
<module name="EmptyCatchBlock">
|
||||||
|
<property name="exceptionVariableName" value="expected|ignore"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="EqualsAvoidNull"/>
|
||||||
|
<module name="NoFinalizer"/>
|
||||||
|
<module name="NoClone"/>
|
||||||
|
<module name="PackageDeclaration"/>
|
||||||
|
</module>
|
||||||
|
</module>
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
251
gradlew
vendored
Executable file
251
gradlew
vendored
Executable file
|
|
@ -0,0 +1,251 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH="\\\"\\\""
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
91
gradlew.bat
vendored
91
gradlew.bat
vendored
|
|
@ -1,7 +1,94 @@
|
||||||
@rem Gradle startup script for Windows
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
java -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
package dev.loki.lomines;
|
package dev.loki.lomines;
|
||||||
|
|
||||||
import dev.loki.lomines.core.mine.MineTicker;
|
import dev.loki.lomines.core.mine.service.MineTicker;
|
||||||
import dev.loki.lomines.core.mine.Mines;
|
import dev.loki.lomines.core.mine.registry.Mines;
|
||||||
import dev.loki.lomines.data.stats.StatsManager;
|
import dev.loki.lomines.data.stats.service.StatsManager;
|
||||||
import dev.loki.lomines.integration.IntegrationManager;
|
import dev.loki.lomines.integration.IntegrationManager;
|
||||||
import dev.loki.lomines.wand.group.GroupWandManager;
|
import dev.loki.lomines.wand.group.GroupWandManager;
|
||||||
import dev.lolib.commands.CommandManager;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
|
@ -28,7 +28,7 @@ final class ComponentInitializer {
|
||||||
|
|
||||||
if (!Files.exists(minesFolder)) {
|
if (!Files.exists(minesFolder)) {
|
||||||
Files.createDirectories(minesFolder);
|
Files.createDirectories(minesFolder);
|
||||||
plugin.loLogger().info("Created mines directory");
|
plugin.getLogger().info("Created mines directory");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,20 +36,19 @@ final class ComponentInitializer {
|
||||||
Mines mines = new Mines(plugin);
|
Mines mines = new Mines(plugin);
|
||||||
GroupWandManager groupWandManager = new GroupWandManager();
|
GroupWandManager groupWandManager = new GroupWandManager();
|
||||||
StatsManager statsManager = new StatsManager(plugin);
|
StatsManager statsManager = new StatsManager(plugin);
|
||||||
CommandManager commandManager = new CommandManager(plugin);
|
|
||||||
IntegrationManager integrationManager = new IntegrationManager(plugin);
|
IntegrationManager integrationManager = new IntegrationManager(plugin);
|
||||||
|
|
||||||
plugin.loLogger().info("Core components initialized");
|
plugin.getLogger().info("Core components initialized");
|
||||||
|
|
||||||
return new Components(mines, groupWandManager, statsManager, commandManager, integrationManager);
|
return new Components(mines, groupWandManager, statsManager, integrationManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadMines(Mines mines) {
|
void loadMines(Mines mines) {
|
||||||
try {
|
try {
|
||||||
mines.loadAll();
|
mines.loadAll();
|
||||||
plugin.loLogger().info("Loaded " + mines.getAll().size() + " mine(s)");
|
plugin.getLogger().info("Loaded " + mines.getAll().size() + " mine(s)");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
plugin.loLogger().error("Failed to load mines: " + e.getMessage());
|
plugin.getLogger().severe("Failed to load mines: " + e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -65,9 +64,9 @@ final class ComponentInitializer {
|
||||||
try {
|
try {
|
||||||
statsManager.load();
|
statsManager.load();
|
||||||
statsManager.startAutoSave();
|
statsManager.startAutoSave();
|
||||||
plugin.loLogger().info("Statistics system enabled");
|
plugin.getLogger().info("Statistics system enabled");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
plugin.loLogger().warn("Failed to load statistics: " + e.getMessage());
|
plugin.getLogger().warning("Failed to load statistics: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -76,7 +75,6 @@ final class ComponentInitializer {
|
||||||
Mines mines,
|
Mines mines,
|
||||||
GroupWandManager groupWandManager,
|
GroupWandManager groupWandManager,
|
||||||
StatsManager statsManager,
|
StatsManager statsManager,
|
||||||
CommandManager commandManager,
|
|
||||||
IntegrationManager integrationManager
|
IntegrationManager integrationManager
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,26 @@
|
||||||
package dev.loki.lomines;
|
package dev.loki.lomines;
|
||||||
|
|
||||||
import dev.loki.lomines.core.mine.MineTicker;
|
import dev.loki.lomines.core.mine.service.MineTicker;
|
||||||
import dev.loki.lomines.core.mine.Mines;
|
import dev.loki.lomines.core.mine.registry.Mines;
|
||||||
import dev.loki.lomines.data.stats.StatsManager;
|
import dev.loki.lomines.data.stats.service.StatsManager;
|
||||||
import dev.loki.lomines.integration.IntegrationManager;
|
import dev.loki.lomines.integration.IntegrationManager;
|
||||||
import dev.loki.lomines.integration.hologram.HologramManager;
|
import dev.loki.lomines.integration.hologram.HologramManager;
|
||||||
import dev.loki.lomines.wand.WandParticleService;
|
import dev.loki.lomines.wand.WandParticleService;
|
||||||
import dev.loki.lomines.wand.group.GroupWandManager;
|
import dev.loki.lomines.wand.group.GroupWandManager;
|
||||||
import dev.lolib.commands.CommandManager;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import dev.lolib.core.LoPlugin;
|
|
||||||
import dev.lolib.core.dependency.DependencyManager;
|
|
||||||
|
|
||||||
/**
|
public final class LoMinesPlugin extends JavaPlugin {
|
||||||
* Main plugin class for LoMines.
|
|
||||||
* Extends LoPlugin from LoAPI for Paper/Folia compatibility.
|
|
||||||
*/
|
|
||||||
public final class LoMinesPlugin extends LoPlugin {
|
|
||||||
|
|
||||||
private Mines mines;
|
private Mines mines;
|
||||||
private MineTicker mineTicker;
|
private MineTicker mineTicker;
|
||||||
private GroupWandManager groupWandManager;
|
private GroupWandManager groupWandManager;
|
||||||
private StatsManager statsManager;
|
private StatsManager statsManager;
|
||||||
private CommandManager commandManager;
|
|
||||||
private IntegrationManager integrationManager;
|
private IntegrationManager integrationManager;
|
||||||
private WandParticleService wandParticleService;
|
private WandParticleService wandParticleService;
|
||||||
private HologramManager hologramManager;
|
private HologramManager hologramManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void enable() {
|
public void onEnable() {
|
||||||
try {
|
try {
|
||||||
saveDefaultConfig();
|
saveDefaultConfig();
|
||||||
|
|
||||||
|
|
@ -38,7 +31,6 @@ public final class LoMinesPlugin extends LoPlugin {
|
||||||
this.mines = components.mines();
|
this.mines = components.mines();
|
||||||
this.groupWandManager = components.groupWandManager();
|
this.groupWandManager = components.groupWandManager();
|
||||||
this.statsManager = components.statsManager();
|
this.statsManager = components.statsManager();
|
||||||
this.commandManager = components.commandManager();
|
|
||||||
this.integrationManager = components.integrationManager();
|
this.integrationManager = components.integrationManager();
|
||||||
this.wandParticleService = new WandParticleService(this);
|
this.wandParticleService = new WandParticleService(this);
|
||||||
this.hologramManager = new HologramManager(this);
|
this.hologramManager = new HologramManager(this);
|
||||||
|
|
@ -47,65 +39,51 @@ public final class LoMinesPlugin extends LoPlugin {
|
||||||
this.mineTicker = initializer.startTicker(mines);
|
this.mineTicker = initializer.startTicker(mines);
|
||||||
|
|
||||||
RegistrationManager registrationManager = new RegistrationManager(this);
|
RegistrationManager registrationManager = new RegistrationManager(this);
|
||||||
registrationManager.registerCommands(commandManager);
|
registrationManager.registerCommands();
|
||||||
registrationManager.registerTabCompleter();
|
|
||||||
registrationManager.registerListeners();
|
registrationManager.registerListeners();
|
||||||
registrationManager.initializeIntegrations(integrationManager);
|
registrationManager.initializeIntegrations(integrationManager);
|
||||||
|
|
||||||
initializer.startStatistics(statsManager);
|
initializer.startStatistics(statsManager);
|
||||||
|
|
||||||
loLogger().info("LoMines has been enabled!");
|
getLogger().info("LoMines has been enabled!");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
loLogger().error("Failed to enable LoMines: " + e.getMessage());
|
getLogger().severe("Failed to enable LoMines: " + e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
getServer().getPluginManager().disablePlugin(this);
|
getServer().getPluginManager().disablePlugin(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void disable() {
|
public void onDisable() {
|
||||||
try {
|
try {
|
||||||
if (mineTicker != null) {
|
if (mineTicker != null) {
|
||||||
mineTicker.stop();
|
mineTicker.stop();
|
||||||
mineTicker = null;
|
mineTicker = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mines != null) {
|
if (mines != null) {
|
||||||
mines.getAll().forEach(mine -> mine.stop());
|
mines.getAll().forEach(mine -> mine.stop());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statsManager != null) {
|
if (statsManager != null) {
|
||||||
statsManager.stopAutoSave();
|
statsManager.stopAutoSave();
|
||||||
statsManager.save();
|
statsManager.save();
|
||||||
loLogger().info("Statistics saved successfully");
|
getLogger().info("Statistics saved successfully");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (integrationManager != null) {
|
if (integrationManager != null) {
|
||||||
integrationManager.shutdown();
|
integrationManager.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wandParticleService != null) {
|
if (wandParticleService != null) {
|
||||||
wandParticleService.stopAll();
|
wandParticleService.stopAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hologramManager != null) {
|
if (hologramManager != null) {
|
||||||
hologramManager.shutdown();
|
hologramManager.shutdown();
|
||||||
}
|
}
|
||||||
|
getLogger().info("LoMines has been disabled!");
|
||||||
loLogger().info("LoMines has been disabled!");
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
loLogger().error("Error during plugin shutdown: " + e.getMessage());
|
getLogger().severe("Error during plugin shutdown: " + e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("removal")
|
|
||||||
protected void dependencies(DependencyManager dependencyManager) {
|
|
||||||
dependencyManager.add("org.apache.commons", "commons-math3", "3.6.1");
|
|
||||||
dependencyManager.add("commons-io", "commons-io", "2.18.0");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mines getMines() {
|
public Mines getMines() {
|
||||||
return mines;
|
return mines;
|
||||||
}
|
}
|
||||||
|
|
@ -118,10 +96,6 @@ public final class LoMinesPlugin extends LoPlugin {
|
||||||
return statsManager;
|
return statsManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommandManager getCommandManager() {
|
|
||||||
return commandManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntegrationManager getIntegrationManager() {
|
public IntegrationManager getIntegrationManager() {
|
||||||
return integrationManager;
|
return integrationManager;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,13 @@
|
||||||
package dev.loki.lomines;
|
package dev.loki.lomines;
|
||||||
|
|
||||||
import dev.loki.lomines.command.admin.AdminCommands;
|
import dev.loki.lomines.command.LmCommand;
|
||||||
import dev.loki.lomines.command.admin.HologramCommands;
|
|
||||||
import dev.loki.lomines.command.admin.MaskCommands;
|
|
||||||
import dev.loki.lomines.command.admin.StatsCommands;
|
|
||||||
import dev.loki.lomines.command.common.LoMinesTabCompleter;
|
|
||||||
import dev.loki.lomines.command.player.PlayerCommands;
|
|
||||||
import dev.loki.lomines.integration.IntegrationManager;
|
import dev.loki.lomines.integration.IntegrationManager;
|
||||||
import dev.loki.lomines.listener.BlockBreakListener;
|
import dev.loki.lomines.listener.block.BlockBreakListener;
|
||||||
import dev.loki.lomines.listener.GroupGuiListener;
|
import dev.loki.lomines.listener.gui.GroupGuiListener;
|
||||||
import dev.loki.lomines.listener.MineEditGuiListener;
|
import dev.loki.lomines.listener.gui.MineEditGuiListener;
|
||||||
import dev.loki.lomines.listener.PlayerInteractListener;
|
import dev.loki.lomines.listener.player.PlayerInteractListener;
|
||||||
import dev.loki.lomines.listener.PlayerJoinListener;
|
import dev.loki.lomines.listener.player.PlayerJoinListener;
|
||||||
import dev.lolib.commands.CommandManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles registration of commands, listeners, and integrations.
|
|
||||||
*/
|
|
||||||
final class RegistrationManager {
|
final class RegistrationManager {
|
||||||
|
|
||||||
private final LoMinesPlugin plugin;
|
private final LoMinesPlugin plugin;
|
||||||
|
|
@ -25,24 +16,16 @@ final class RegistrationManager {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerCommands(CommandManager commandManager) {
|
void registerCommands() {
|
||||||
commandManager.register(new AdminCommands(plugin));
|
LmCommand executor = new LmCommand(plugin);
|
||||||
commandManager.register(new PlayerCommands(plugin));
|
for (String alias : new String[]{"lm", "lomines", "mine", "mines"}) {
|
||||||
commandManager.register(new StatsCommands(plugin));
|
var cmd = plugin.getCommand(alias);
|
||||||
commandManager.register(new MaskCommands(plugin));
|
if (cmd != null) {
|
||||||
commandManager.register(new HologramCommands(plugin));
|
cmd.setExecutor(executor);
|
||||||
|
cmd.setTabCompleter(executor);
|
||||||
plugin.loLogger().info("Commands registered");
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
void registerTabCompleter() {
|
plugin.getLogger().info("Commands registered");
|
||||||
LoMinesTabCompleter tabCompleter = new LoMinesTabCompleter(plugin);
|
|
||||||
plugin.getCommand("lm").setTabCompleter(tabCompleter);
|
|
||||||
plugin.getCommand("lomines").setTabCompleter(tabCompleter);
|
|
||||||
plugin.getCommand("mine").setTabCompleter(tabCompleter);
|
|
||||||
plugin.getCommand("mines").setTabCompleter(tabCompleter);
|
|
||||||
|
|
||||||
plugin.loLogger().info("Tab completer registered");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerListeners() {
|
void registerListeners() {
|
||||||
|
|
@ -54,12 +37,11 @@ final class RegistrationManager {
|
||||||
if (plugin.getConfig().getBoolean("statistics-enabled", true)) {
|
if (plugin.getConfig().getBoolean("statistics-enabled", true)) {
|
||||||
plugin.getServer().getPluginManager().registerEvents(new PlayerJoinListener(plugin), plugin);
|
plugin.getServer().getPluginManager().registerEvents(new PlayerJoinListener(plugin), plugin);
|
||||||
}
|
}
|
||||||
|
plugin.getLogger().info("Event listeners registered");
|
||||||
plugin.loLogger().info("Event listeners registered");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void initializeIntegrations(IntegrationManager integrationManager) {
|
void initializeIntegrations(IntegrationManager integrationManager) {
|
||||||
integrationManager.initAll();
|
integrationManager.initAll();
|
||||||
plugin.loLogger().info("Integrations initialized");
|
plugin.getLogger().info("Integrations initialized");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package dev.loki.lomines.block;
|
package dev.loki.lomines.block;
|
||||||
|
|
||||||
import dev.loki.lomines.util.location.Cuboid;
|
import dev.loki.lomines.util.location.geo.Cuboid;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ package dev.loki.lomines.block;
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.data.config.block.BlockKey;
|
import dev.loki.lomines.data.config.block.BlockKey;
|
||||||
import dev.loki.lomines.util.block.BlockUpdateUtil;
|
import dev.loki.lomines.util.block.BlockUpdateUtil;
|
||||||
import dev.loki.lomines.util.location.Cuboid;
|
import dev.loki.lomines.util.location.geo.Cuboid;
|
||||||
import dev.lolib.scheduler.Scheduler;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
|
@ -35,11 +35,11 @@ public final class BukkitBlockSetter extends BlockSetter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fill(Cuboid region, IntConsumer callback) {
|
public void fill(Cuboid region, IntConsumer callback) {
|
||||||
Scheduler.get(plugin).runAsync(() -> {
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
int count = fillSync(region);
|
int count = fillSync(region);
|
||||||
|
|
||||||
// Send block updates on main thread to prevent ghost blocks
|
// Send block updates on main thread to prevent ghost blocks
|
||||||
Scheduler.get(plugin).run(() -> {
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||||
BlockUpdateUtil.sendRegionUpdate(
|
BlockUpdateUtil.sendRegionUpdate(
|
||||||
region.getWorld(),
|
region.getWorld(),
|
||||||
region.getMinX(), region.getMinY(), region.getMinZ(),
|
region.getMinX(), region.getMinY(), region.getMinZ(),
|
||||||
|
|
@ -53,14 +53,14 @@ public final class BukkitBlockSetter extends BlockSetter {
|
||||||
@Override
|
@Override
|
||||||
public void fillAtLocations(List<Location> locations, IntConsumer callback) {
|
public void fillAtLocations(List<Location> locations, IntConsumer callback) {
|
||||||
if (locations == null || locations.isEmpty()) {
|
if (locations == null || locations.isEmpty()) {
|
||||||
Scheduler.get(plugin).run(() -> callback.accept(0));
|
Bukkit.getScheduler().runTask(plugin, () -> callback.accept(0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Scheduler.get(plugin).runAsync(() -> {
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
int count = fillAtLocationsSync(locations);
|
int count = fillAtLocationsSync(locations);
|
||||||
|
|
||||||
// Send block updates on main thread to prevent ghost blocks
|
// Send block updates on main thread to prevent ghost blocks
|
||||||
Scheduler.get(plugin).run(() -> {
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||||
BlockUpdateUtil.sendLocationsUpdate(locations);
|
BlockUpdateUtil.sendLocationsUpdate(locations);
|
||||||
callback.accept(count);
|
callback.accept(count);
|
||||||
});
|
});
|
||||||
|
|
@ -106,8 +106,8 @@ public final class BukkitBlockSetter extends BlockSetter {
|
||||||
currentWeight += entry.getValue();
|
currentWeight += entry.getValue();
|
||||||
if (random <= currentWeight) {
|
if (random <= currentWeight) {
|
||||||
// BlockKey is already validated to be a block
|
// BlockKey is already validated to be a block
|
||||||
if (entry.getKey() instanceof BlockKey.Vanilla vanilla) {
|
if (entry.getKey() instanceof BlockKey.Vanilla(Material material)) {
|
||||||
return Bukkit.createBlockData(vanilla.material());
|
return Bukkit.createBlockData(material);
|
||||||
}
|
}
|
||||||
// Fallback for non-vanilla keys in vanilla setter (shouldn't happen)
|
// Fallback for non-vanilla keys in vanilla setter (shouldn't happen)
|
||||||
return Bukkit.createBlockData(Material.STONE);
|
return Bukkit.createBlockData(Material.STONE);
|
||||||
|
|
@ -116,8 +116,8 @@ public final class BukkitBlockSetter extends BlockSetter {
|
||||||
|
|
||||||
// Fallback to first entry
|
// Fallback to first entry
|
||||||
BlockKey firstKey = weights.keySet().iterator().next();
|
BlockKey firstKey = weights.keySet().iterator().next();
|
||||||
if (firstKey instanceof BlockKey.Vanilla vanilla) {
|
if (firstKey instanceof BlockKey.Vanilla(Material material)) {
|
||||||
return Bukkit.createBlockData(vanilla.material());
|
return Bukkit.createBlockData(material);
|
||||||
}
|
}
|
||||||
return Bukkit.createBlockData(Material.STONE);
|
return Bukkit.createBlockData(Material.STONE);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
85
src/main/java/dev/loki/lomines/command/LmCommand.java
Normal file
85
src/main/java/dev/loki/lomines/command/LmCommand.java
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
package dev.loki.lomines.command;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import dev.loki.lomines.command.admin.info.InfoCommand;
|
||||||
|
import dev.loki.lomines.command.admin.info.RegionCommands;
|
||||||
|
import dev.loki.lomines.command.admin.manage.AdminCommands;
|
||||||
|
import dev.loki.lomines.command.admin.manage.CopyCommand;
|
||||||
|
import dev.loki.lomines.command.admin.misc.MaskCommands;
|
||||||
|
import dev.loki.lomines.command.admin.misc.TeleportCommands;
|
||||||
|
import dev.loki.lomines.command.admin.stats.HologramCommands;
|
||||||
|
import dev.loki.lomines.command.admin.stats.StatsCommands;
|
||||||
|
import dev.loki.lomines.command.player.PlayerCommands;
|
||||||
|
import dev.loki.lomines.command.player.TeleportCommand;
|
||||||
|
import dev.loki.lomines.command.common.LoMinesTabCompleter;
|
||||||
|
import dev.loki.lomines.util.ErrorHandler;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.command.TabCompleter;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class LmCommand implements CommandExecutor, TabCompleter {
|
||||||
|
|
||||||
|
private final LoMinesPlugin plugin;
|
||||||
|
private final AdminCommands adminCommands;
|
||||||
|
private final InfoCommand infoCommand;
|
||||||
|
private final RegionCommands regionCommands;
|
||||||
|
private final CopyCommand copyCommand;
|
||||||
|
private final MaskCommands maskCommands;
|
||||||
|
private final TeleportCommands teleportCommands;
|
||||||
|
private final HologramCommands hologramCommands;
|
||||||
|
private final StatsCommands statsCommands;
|
||||||
|
private final PlayerCommands playerCommands;
|
||||||
|
private final TeleportCommand tpCommand;
|
||||||
|
private final LoMinesTabCompleter tabCompleter;
|
||||||
|
|
||||||
|
public LmCommand(LoMinesPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.adminCommands = new AdminCommands(plugin);
|
||||||
|
this.infoCommand = new InfoCommand(plugin);
|
||||||
|
this.regionCommands = new RegionCommands(plugin);
|
||||||
|
this.copyCommand = new CopyCommand(plugin);
|
||||||
|
this.maskCommands = new MaskCommands(plugin);
|
||||||
|
this.teleportCommands = new TeleportCommands(plugin, plugin.getMines(), new ErrorHandler(plugin.getLogger(), "[LoMines] "));
|
||||||
|
this.hologramCommands = new HologramCommands(plugin);
|
||||||
|
this.statsCommands = new StatsCommands(plugin);
|
||||||
|
this.playerCommands = new PlayerCommands(plugin);
|
||||||
|
this.tpCommand = new TeleportCommand(plugin);
|
||||||
|
this.tabCompleter = new LoMinesTabCompleter(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||||
|
if (args.length == 0) {
|
||||||
|
sender.sendMessage("§6/lm help §f- Show help");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String sub = args[0].toLowerCase();
|
||||||
|
String[] rest = new String[args.length - 1];
|
||||||
|
System.arraycopy(args, 1, rest, 0, rest.length);
|
||||||
|
|
||||||
|
switch (sub) {
|
||||||
|
case "create", "delete", "edit", "reset", "reload", "list" -> adminCommands.handle(sender, sub, rest);
|
||||||
|
case "info" -> infoCommand.handle(sender, rest);
|
||||||
|
case "regions", "addregion", "removeregion" -> regionCommands.handle(sender, sub, rest);
|
||||||
|
case "copy" -> copyCommand.handle(sender, rest);
|
||||||
|
case "maskscan" -> maskCommands.handle(sender, rest);
|
||||||
|
case "setteleport", "setspawn", "clearspawn" -> teleportCommands.handle(sender, sub, rest);
|
||||||
|
case "hologram" -> hologramCommands.handle(sender, rest);
|
||||||
|
case "stats", "top" -> statsCommands.handle(sender, sub, rest);
|
||||||
|
case "tp" -> tpCommand.handle(sender, rest);
|
||||||
|
case "wand", "group", "help" -> playerCommands.handle(sender, sub, rest);
|
||||||
|
default -> sender.sendMessage("§cUnknown command: " + sub);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||||
|
return tabCompleter.onTabComplete(sender, command, alias, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
package dev.loki.lomines.command.admin;
|
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
|
||||||
import dev.loki.lomines.core.mine.Mine;
|
|
||||||
import dev.loki.lomines.core.mine.Mines;
|
|
||||||
import dev.loki.lomines.data.stats.Leaderboard;
|
|
||||||
import dev.loki.lomines.data.stats.LeaderboardEntry;
|
|
||||||
import dev.loki.lomines.data.stats.PlayerStats;
|
|
||||||
import dev.loki.lomines.data.stats.StatsManager;
|
|
||||||
import dev.loki.lomines.util.ErrorHandler;
|
|
||||||
import dev.lolib.commands.annotation.Arg;
|
|
||||||
import dev.lolib.commands.annotation.Command;
|
|
||||||
import dev.lolib.commands.annotation.Subcommand;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.OfflinePlayer;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Statistics commands for player mining stats and leaderboards.
|
|
||||||
* Provides commands for viewing stats and top players.
|
|
||||||
*/
|
|
||||||
@Command(value = "lm", permission = "lomines.use")
|
|
||||||
public class StatsCommands {
|
|
||||||
|
|
||||||
private final LoMinesPlugin plugin;
|
|
||||||
private final Mines mines;
|
|
||||||
private final ErrorHandler errorHandler;
|
|
||||||
|
|
||||||
public StatsCommands(LoMinesPlugin plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.mines = plugin.getMines();
|
|
||||||
this.errorHandler = new ErrorHandler(plugin.getLogger(), "[LoMines] ");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void sendTopRows(CommandSender sender, List<LeaderboardEntry> rows) {
|
|
||||||
if (rows.isEmpty()) {
|
|
||||||
sender.sendMessage(Component.text("Пока нет данных.", NamedTextColor.GRAY));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < rows.size(); i++) {
|
|
||||||
LeaderboardEntry e = rows.get(i);
|
|
||||||
String name = formatPlayerName(e.playerId());
|
|
||||||
sender.sendMessage(Component.text(
|
|
||||||
(i + 1) + ". " + name + " — " + e.count(),
|
|
||||||
NamedTextColor.WHITE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String formatPlayerName(UUID id) {
|
|
||||||
String name = Bukkit.getOfflinePlayer(id).getName();
|
|
||||||
return name != null ? name : id.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int clampLimit(int limit) {
|
|
||||||
return Math.max(1, Math.min(50, limit));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Integer tryParsePositiveInt(String s) {
|
|
||||||
try {
|
|
||||||
int v = Integer.parseInt(s);
|
|
||||||
return v > 0 ? v : null;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows mining stats for self or another player.
|
|
||||||
* Usage: /lm stats [player]
|
|
||||||
*/
|
|
||||||
@Subcommand(value = "stats", permission = "lomines.stats")
|
|
||||||
public void stats(CommandSender sender, @Arg(value = "player", optional = true, defaultValue = "") String targetName) {
|
|
||||||
if (isStatsDisabled(sender)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String name = targetName == null ? "" : targetName.trim();
|
|
||||||
UUID uuid;
|
|
||||||
if (name.isEmpty()) {
|
|
||||||
if (!(sender instanceof Player self)) {
|
|
||||||
errorHandler.sendError(sender, "Укажите игрока: /lm stats <ник>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uuid = self.getUniqueId();
|
|
||||||
} else {
|
|
||||||
if (!sender.hasPermission("lomines.stats.others")) {
|
|
||||||
errorHandler.handlePermissionDenied(sender, "смотреть чужую статистику");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Player online = Bukkit.getPlayerExact(name);
|
|
||||||
if (online != null) {
|
|
||||||
uuid = online.getUniqueId();
|
|
||||||
} else {
|
|
||||||
OfflinePlayer off = Bukkit.getOfflinePlayer(name);
|
|
||||||
uuid = off.getUniqueId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StatsManager sm = plugin.getStatsManager();
|
|
||||||
PlayerStats ps = sm.getOrCreate(uuid);
|
|
||||||
String displayName = formatPlayerName(uuid);
|
|
||||||
sender.sendMessage(Component.text("=== Статистика: " + displayName + " ===", NamedTextColor.GOLD));
|
|
||||||
sender.sendMessage(Component.text("Всего блоков: " + ps.getTotalBlocks(), NamedTextColor.WHITE));
|
|
||||||
int pos = sm.getLeaderboard().getPosition(uuid);
|
|
||||||
if (pos > 0) {
|
|
||||||
sender.sendMessage(Component.text("Место в общем топе: #" + pos, NamedTextColor.GRAY));
|
|
||||||
}
|
|
||||||
Map<String, Long> byMine = ps.getMineStatsSnapshot();
|
|
||||||
if (byMine.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sender.sendMessage(Component.text("По шахтам:", NamedTextColor.GRAY));
|
|
||||||
byMine.entrySet().stream()
|
|
||||||
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
|
|
||||||
.limit(12)
|
|
||||||
.forEach(e -> sender.sendMessage(Component.text(
|
|
||||||
" " + e.getKey() + ": " + e.getValue(), NamedTextColor.DARK_AQUA)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Leaderboard: global or per-mine.
|
|
||||||
* Usage: /lm top — глобально 10; /lm top 25 — глобально 25; /lm top <шахта> — по шахте;
|
|
||||||
* /lm top <шахта> <лимит>
|
|
||||||
*/
|
|
||||||
@Subcommand(value = "top", permission = "lomines.stats")
|
|
||||||
public void top(
|
|
||||||
CommandSender sender,
|
|
||||||
@Arg(value = "arg1", optional = true, defaultValue = "") String arg1,
|
|
||||||
@Arg(value = "arg2", optional = true, defaultValue = "") String arg2
|
|
||||||
) {
|
|
||||||
if (isStatsDisabled(sender)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String a = arg1 == null ? "" : arg1.trim();
|
|
||||||
String b = arg2 == null ? "" : arg2.trim();
|
|
||||||
int limit;
|
|
||||||
String mineName;
|
|
||||||
if (a.isEmpty()) {
|
|
||||||
limit = clampLimit(10);
|
|
||||||
mineName = null;
|
|
||||||
} else if (b.isEmpty()) {
|
|
||||||
Integer asLimit = tryParsePositiveInt(a);
|
|
||||||
if (asLimit != null) {
|
|
||||||
limit = clampLimit(asLimit);
|
|
||||||
mineName = null;
|
|
||||||
} else {
|
|
||||||
limit = clampLimit(10);
|
|
||||||
mineName = a;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mineName = a;
|
|
||||||
Integer limParsed = tryParsePositiveInt(b);
|
|
||||||
limit = clampLimit(limParsed != null ? limParsed : 10);
|
|
||||||
}
|
|
||||||
Leaderboard lb = plugin.getStatsManager().getLeaderboard();
|
|
||||||
if (mineName != null) {
|
|
||||||
var found = mines.find(mineName);
|
|
||||||
if (found.isEmpty()) {
|
|
||||||
errorHandler.handleNotFound(sender, "Шахта", mineName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Mine m = found.get();
|
|
||||||
String key = m.getName();
|
|
||||||
List<LeaderboardEntry> rows = lb.getTopByMine(key, limit);
|
|
||||||
sender.sendMessage(Component.text("=== Топ по шахте «" + key + "» ===", NamedTextColor.GOLD));
|
|
||||||
sendTopRows(sender, rows);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
List<LeaderboardEntry> rows = lb.getTopTotal(limit);
|
|
||||||
sender.sendMessage(Component.text("=== Общий топ ===", NamedTextColor.GOLD));
|
|
||||||
sendTopRows(sender, rows);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isStatsDisabled(CommandSender sender) {
|
|
||||||
if (!plugin.getConfig().getBoolean("statistics-enabled", true)) {
|
|
||||||
sender.sendMessage(Component.text("Статистика отключена в config.yml.", NamedTextColor.RED));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
package dev.loki.lomines.command.admin.info;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
|
import dev.loki.lomines.data.config.block.BlockKey;
|
||||||
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
|
import dev.loki.lomines.util.location.geo.Cuboid;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class InfoCommand {
|
||||||
|
|
||||||
|
private final LoMinesPlugin plugin;
|
||||||
|
|
||||||
|
public InfoCommand(LoMinesPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(CommandSender sender, String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
sender.sendMessage(Component.text("Usage: /lm info <mine>", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String mineName = args[0];
|
||||||
|
|
||||||
|
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
||||||
|
if (mine == null) {
|
||||||
|
sender.sendMessage(Component.text("Шахта не найдена: " + mineName, NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = mine.getConfig();
|
||||||
|
sender.sendMessage(Component.text("§8§m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
||||||
|
sender.sendMessage(Component.text("§6§lИнформация о шахте: §f" + mine.getName()));
|
||||||
|
sender.sendMessage(Component.text(""));
|
||||||
|
sender.sendMessage(Component.text("§7Мир: §f" + config.worldName()));
|
||||||
|
sender.sendMessage(Component.text("§7Блоков: §f" + mine.getBlocks() + "§7/§f" + mine.getTotalVolume() +
|
||||||
|
" §7(§f" + String.format("%.1f", mine.getPercentFilled()) + "%§7)"));
|
||||||
|
|
||||||
|
printRegions(sender, mine.getRegions());
|
||||||
|
printBlocks(sender, config.blocks().weights());
|
||||||
|
printResetAndTeleport(sender, config);
|
||||||
|
|
||||||
|
sender.sendMessage(Component.text("§6§lНаграды: §f" + config.rewards().entries().size() + " записей"));
|
||||||
|
sender.sendMessage(Component.text("§8§m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printRegions(CommandSender sender, List<Cuboid> regions) {
|
||||||
|
sender.sendMessage(Component.text("§6§lРегионы:"));
|
||||||
|
for (int i = 0; i < regions.size(); i++) {
|
||||||
|
Cuboid r = regions.get(i);
|
||||||
|
sender.sendMessage(Component.text("§7 #" + (i + 1) + ": §f" +
|
||||||
|
r.getMinX() + "," + r.getMinY() + "," + r.getMinZ() + " §7-> §f" +
|
||||||
|
r.getMaxX() + "," + r.getMaxY() + "," + r.getMaxZ() +
|
||||||
|
" §7(§f" + r.getVolume() + " §7блоков)"));
|
||||||
|
}
|
||||||
|
sender.sendMessage(Component.text(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printBlocks(CommandSender sender, Map<BlockKey, Double> weights) {
|
||||||
|
sender.sendMessage(Component.text("§6§lБлоки:"));
|
||||||
|
weights.entrySet().stream()
|
||||||
|
.sorted((a, b) -> Double.compare(b.getValue(), a.getValue()))
|
||||||
|
.limit(10)
|
||||||
|
.forEach(e -> sender.sendMessage(Component.text(
|
||||||
|
"§7 " + formatBlockKey(e.getKey()) + ": §f" + String.format("%.1f", e.getValue() * 100) + "%")));
|
||||||
|
if (weights.size() > 10) {
|
||||||
|
sender.sendMessage(Component.text("§7 ... и ещё " + (weights.size() - 10) + " блоков"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printResetAndTeleport(CommandSender sender, MineConfig config) {
|
||||||
|
sender.sendMessage(Component.text("§6§lНастройки сброса:"));
|
||||||
|
sender.sendMessage(Component.text("§7 Интервал: §f" + config.reset().intervalDisplay()));
|
||||||
|
sender.sendMessage(Component.text("§7 Триггер по %: §f" +
|
||||||
|
(config.reset().isPercentTriggerEnabled() ? config.reset().percentTrigger() + "%" : "выкл")));
|
||||||
|
|
||||||
|
sender.sendMessage(Component.text("§6§lТелепортация:"));
|
||||||
|
sender.sendMessage(Component.text("§7 Точка телепорта: §f" +
|
||||||
|
(config.teleport().enabled() ? "§aустановлена" : "§7не задана")));
|
||||||
|
sender.sendMessage(Component.text("§7 Точка спавна: §f" +
|
||||||
|
(config.playerSpawn().enabled() ? "§aустановлена" : "§7используется точка телепорта")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatBlockKey(BlockKey key) {
|
||||||
|
return switch (key) {
|
||||||
|
case BlockKey.Vanilla v -> v.material().name().toLowerCase();
|
||||||
|
case BlockKey.Oraxen o -> "oraxen:" + o.id();
|
||||||
|
case BlockKey.ItemsAdder i -> "ia:" + i.id();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
package dev.loki.lomines.command.admin.info;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
|
import dev.loki.lomines.data.config.region.RegionConfig;
|
||||||
|
import dev.loki.lomines.util.location.geo.Cuboid;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class RegionActionHandler {
|
||||||
|
|
||||||
|
private final LoMinesPlugin plugin;
|
||||||
|
|
||||||
|
public RegionActionHandler(LoMinesPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addRegion(CommandSender sender, String mineName) {
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
sender.sendMessage(Component.text("Эта команда только для игроков!", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
||||||
|
if (mine == null) {
|
||||||
|
player.sendMessage(Component.text("Шахта не найдена: " + mineName, NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cuboid newRegion = resolveNewRegion(player, mine);
|
||||||
|
if (newRegion == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Cuboid> currentRegions = new ArrayList<>(mine.getRegions());
|
||||||
|
currentRegions.add(newRegion);
|
||||||
|
|
||||||
|
updateMineRegions(mine, new RegionConfig(currentRegions));
|
||||||
|
|
||||||
|
sendRegionAdded(player, newRegion, currentRegions.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Cuboid resolveNewRegion(Player player, Mine mine) {
|
||||||
|
var wandManager = plugin.getGroupWandManager();
|
||||||
|
var session = wandManager.getSession(player.getUniqueId());
|
||||||
|
int slot = session.getActiveSlot();
|
||||||
|
var pos1 = session.getPos1(slot);
|
||||||
|
var pos2 = session.getPos2(slot);
|
||||||
|
if (pos1 == null || pos2 == null) {
|
||||||
|
player.sendMessage(Component.text("§cВыделите регион палочкой! ЛКМ - 1-я точка, ПКМ - 2-я точка"));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Cuboid newRegion = new Cuboid(pos1, pos2);
|
||||||
|
String mineWorld = mine.getConfig().region().worldName();
|
||||||
|
String regionWorld = newRegion.getWorld().getName();
|
||||||
|
|
||||||
|
if (!mineWorld.equals(regionWorld)) {
|
||||||
|
player.sendMessage(Component.text("§cРегион должен быть в мире §f" + mineWorld + "§c, а не §f" + regionWorld));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
session.clearCorners(slot);
|
||||||
|
return newRegion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendRegionAdded(Player player, Cuboid newRegion, int count) {
|
||||||
|
player.sendMessage(Component.text("§aДобавлен регион #" + count));
|
||||||
|
player.sendMessage(Component.text("§7От: §f" + newRegion.getMinX() + ", " + newRegion.getMinY() + ", " + newRegion.getMinZ()));
|
||||||
|
player.sendMessage(Component.text("§7До: §f" + newRegion.getMaxX() + ", " + newRegion.getMaxY() + ", " + newRegion.getMaxZ()));
|
||||||
|
player.sendMessage(Component.text("§7Объём: §f" + newRegion.getVolume() + " §7блоков"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeRegion(CommandSender sender, String mineName, int index) {
|
||||||
|
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
||||||
|
if (mine == null) {
|
||||||
|
sender.sendMessage(Component.text("Шахта не найдена: " + mineName, NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Cuboid> currentRegions = new ArrayList<>(mine.getRegions());
|
||||||
|
|
||||||
|
if (currentRegions.size() <= 1) {
|
||||||
|
sender.sendMessage(Component.text("§cНельзя удалить последний регион! Удалите всю шахту."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < 1 || index > currentRegions.size()) {
|
||||||
|
sender.sendMessage(Component.text("§cНеверный номер региона. Доступны: 1-" + currentRegions.size()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cuboid removed = currentRegions.remove(index - 1);
|
||||||
|
var newRegionConfig = new RegionConfig(currentRegions);
|
||||||
|
updateMineRegions(mine, newRegionConfig);
|
||||||
|
|
||||||
|
sender.sendMessage(Component.text("§aУдалён регион #" + index));
|
||||||
|
sender.sendMessage(Component.text("§7Был: §f" + removed.getMinX() + ", " + removed.getMinY() + ", " + removed.getMinZ() +
|
||||||
|
" §7-> §f" + removed.getMaxX() + ", " + removed.getMaxY() + ", " + removed.getMaxZ()));
|
||||||
|
sender.sendMessage(Component.text("§7Осталось регионов: §f" + currentRegions.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMineRegions(Mine mine, RegionConfig newConfig) {
|
||||||
|
var oldConfig = mine.getConfig();
|
||||||
|
var newMineConfig = new MineConfig(
|
||||||
|
oldConfig.name(),
|
||||||
|
newConfig,
|
||||||
|
oldConfig.blocks(),
|
||||||
|
oldConfig.reset(),
|
||||||
|
oldConfig.rewards(),
|
||||||
|
oldConfig.teleport(),
|
||||||
|
oldConfig.ui(),
|
||||||
|
oldConfig.worldGuard(),
|
||||||
|
oldConfig.playerSpawn()
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
plugin.getMines().updateMineConfig(mine.getName(), newMineConfig);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("Failed to update mine regions", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
package dev.loki.lomines.command.admin.info;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
|
import dev.loki.lomines.util.location.geo.Cuboid;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RegionCommands {
|
||||||
|
|
||||||
|
private final LoMinesPlugin plugin;
|
||||||
|
private final RegionActionHandler actionHandler;
|
||||||
|
|
||||||
|
public RegionCommands(LoMinesPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.actionHandler = new RegionActionHandler(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(CommandSender sender, String subcommand, String[] args) {
|
||||||
|
switch (subcommand.toLowerCase()) {
|
||||||
|
case "regions" -> handleRegions(sender, args);
|
||||||
|
case "addregion" -> handleAddRegion(sender, args);
|
||||||
|
case "removeregion" -> handleRemoveRegion(sender, args);
|
||||||
|
default -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRegions(CommandSender sender, String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
sender.sendMessage(Component.text("Usage: /lm regions <mine>", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String mineName = args[0];
|
||||||
|
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
||||||
|
if (mine == null) {
|
||||||
|
sender.sendMessage(Component.text("Шахта не найдена: " + mineName, NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Cuboid> regions = mine.getRegions();
|
||||||
|
sender.sendMessage(Component.text("§8§m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
||||||
|
sender.sendMessage(Component.text("§6§lРегионы шахты §f" + mine.getName()));
|
||||||
|
sender.sendMessage(Component.text("§7Всего регионов: §f" + regions.size()));
|
||||||
|
sender.sendMessage(Component.text(""));
|
||||||
|
|
||||||
|
for (int i = 0; i < regions.size(); i++) {
|
||||||
|
Cuboid r = regions.get(i);
|
||||||
|
sender.sendMessage(Component.text("§e#" + (i + 1) + " §7(§f" + r.getVolume() + " §7блоков)"));
|
||||||
|
sender.sendMessage(Component.text(" §7Мир: §f" + r.getWorld().getName()));
|
||||||
|
sender.sendMessage(Component.text(" §7От: §f" + r.getMinX() + ", " + r.getMinY() + ", " + r.getMinZ()));
|
||||||
|
sender.sendMessage(Component.text(" §7До: §f" + r.getMaxX() + ", " + r.getMaxY() + ", " + r.getMaxZ()));
|
||||||
|
}
|
||||||
|
sender.sendMessage(Component.text("§8§m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleAddRegion(CommandSender sender, String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
sender.sendMessage(Component.text("Usage: /lm addregion <mine>", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
actionHandler.addRegion(sender, args[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRemoveRegion(CommandSender sender, String[] args) {
|
||||||
|
if (args.length < 2) {
|
||||||
|
sender.sendMessage(Component.text("Usage: /lm removeregion <mine> <index>", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int index = Integer.parseInt(args[1]);
|
||||||
|
actionHandler.removeRegion(sender, args[0], index);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
sender.sendMessage(Component.text("Индекс должен быть числом: " + args[1], NamedTextColor.RED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
package dev.loki.lomines.command.admin.manage;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
|
import dev.loki.lomines.core.mine.registry.Mines;
|
||||||
|
import dev.loki.lomines.util.ErrorHandler;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class AdminCommands {
|
||||||
|
|
||||||
|
private final LoMinesPlugin plugin;
|
||||||
|
private final Mines mines;
|
||||||
|
private final ErrorHandler errorHandler;
|
||||||
|
private final MineActionHandler actionHandler;
|
||||||
|
|
||||||
|
public AdminCommands(LoMinesPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.mines = plugin.getMines();
|
||||||
|
this.errorHandler = new ErrorHandler(plugin.getLogger(), "[LoMines] ");
|
||||||
|
this.actionHandler = new MineActionHandler(plugin, mines, errorHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(CommandSender sender, String subcommand, String[] args) {
|
||||||
|
switch (subcommand.toLowerCase()) {
|
||||||
|
case "create" -> handleCreate(sender, args);
|
||||||
|
case "delete" -> handleDelete(sender, args);
|
||||||
|
case "edit" -> handleEdit(sender, args);
|
||||||
|
case "reset" -> handleReset(sender, args);
|
||||||
|
case "reload" -> actionHandler.reload(sender);
|
||||||
|
case "list" -> handleList(sender);
|
||||||
|
default -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCreate(CommandSender sender, String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
sender.sendMessage(Component.text("Usage: /lm create <name>"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
actionHandler.create(sender, args[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleDelete(CommandSender sender, String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
sender.sendMessage(Component.text("Usage: /lm delete <name>"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
actionHandler.delete(sender, args[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEdit(CommandSender sender, String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
sender.sendMessage(Component.text("Usage: /lm edit <mine>"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
actionHandler.edit(sender, args[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleReset(CommandSender sender, String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
sender.sendMessage(Component.text("Usage: /lm reset <mine> [silent]"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean silent = args.length > 1 && (args[1].equalsIgnoreCase("true") || args[1].equalsIgnoreCase("silent"));
|
||||||
|
actionHandler.reset(sender, args[0], silent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleList(CommandSender sender) {
|
||||||
|
Collection<Mine> allMines = mines.getAll();
|
||||||
|
if (allMines.isEmpty()) {
|
||||||
|
sender.sendMessage(Component.text("No mines found"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sender.sendMessage(Component.text("=== Mines (" + allMines.size() + ") ==="));
|
||||||
|
for (Mine mine : allMines) {
|
||||||
|
String status = formatMineStatus(mine);
|
||||||
|
sender.sendMessage(Component.text("- " + mine.getName() + " " + status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatMineStatus(Mine mine) {
|
||||||
|
int blocks = mine.getBlocks();
|
||||||
|
int total = mine.getTotalVolume();
|
||||||
|
double percent = mine.getPercentFilled();
|
||||||
|
return String.format("(%d/%d blocks, %.1f%%)", blocks, total, percent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
package dev.loki.lomines.command.admin.manage;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
public class CopyCommand {
|
||||||
|
|
||||||
|
private final LoMinesPlugin plugin;
|
||||||
|
|
||||||
|
public CopyCommand(LoMinesPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(CommandSender sender, String[] args) {
|
||||||
|
if (args.length < 2) {
|
||||||
|
sender.sendMessage(Component.text("Usage: /lm copy <from> <to>", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String fromMineName = args[0];
|
||||||
|
String toMineName = args[1];
|
||||||
|
|
||||||
|
MinePair pair = resolveMines(sender, fromMineName, toMineName);
|
||||||
|
if (pair == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sourceConfig = pair.fromMine().getConfig();
|
||||||
|
|
||||||
|
MineConfig newTargetConfig = buildCopiedConfig(pair.toMine(), sourceConfig);
|
||||||
|
|
||||||
|
try {
|
||||||
|
plugin.getMines().updateMineConfig(pair.toMine().getName(), newTargetConfig);
|
||||||
|
} catch (Exception e) {
|
||||||
|
sender.sendMessage(Component.text("Ошибка сохранения: " + e.getMessage(), NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendCopySummary(sender, pair.fromMine(), pair.toMine(), sourceConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record MinePair(Mine fromMine, Mine toMine) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private MinePair resolveMines(CommandSender sender, String fromMineName, String toMineName) {
|
||||||
|
Mine fromMine = plugin.getMines().find(fromMineName).orElse(null);
|
||||||
|
if (fromMine == null) {
|
||||||
|
sender.sendMessage(Component.text("Исходная шахта не найдена: " + fromMineName, NamedTextColor.RED));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mine toMine = plugin.getMines().find(toMineName).orElse(null);
|
||||||
|
if (toMine == null) {
|
||||||
|
sender.sendMessage(Component.text("Целевая шахта не найдена: " + toMineName, NamedTextColor.RED));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromMine.getName().equalsIgnoreCase(toMine.getName())) {
|
||||||
|
sender.sendMessage(Component.text("Нельзя копировать шахту саму в себя!", NamedTextColor.RED));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new MinePair(fromMine, toMine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MineConfig buildCopiedConfig(Mine toMine, MineConfig sourceConfig) {
|
||||||
|
var targetConfig = toMine.getConfig();
|
||||||
|
return MineConfig.builder(toMine.getName())
|
||||||
|
.region(targetConfig.region())
|
||||||
|
.blocks(sourceConfig.blocks())
|
||||||
|
.reset(sourceConfig.reset())
|
||||||
|
.rewards(sourceConfig.rewards())
|
||||||
|
.teleport(sourceConfig.teleport())
|
||||||
|
.ui(sourceConfig.ui())
|
||||||
|
.worldGuard(sourceConfig.worldGuard())
|
||||||
|
.playerSpawn(sourceConfig.playerSpawn())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendCopySummary(CommandSender sender, Mine fromMine, Mine toMine, MineConfig sourceConfig) {
|
||||||
|
sender.sendMessage(Component.text("§aКонфигурация скопирована из §f" + fromMine.getName() +
|
||||||
|
" §aв §f" + toMine.getName()));
|
||||||
|
sender.sendMessage(Component.text("§7Скопировано:"));
|
||||||
|
sender.sendMessage(Component.text(" §7• Блоки: §f" + sourceConfig.blocks().weights().size() + " типов"));
|
||||||
|
sender.sendMessage(Component.text(" §7• Настройки сброса"));
|
||||||
|
sender.sendMessage(Component.text(" §7• Точки телепорта/спавна"));
|
||||||
|
sender.sendMessage(Component.text(" §7• Награды: §f" + sourceConfig.rewards().entries().size() + " записей"));
|
||||||
|
sender.sendMessage(Component.text("§7§oПримечание: регионы и позиции не скопированы"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,45 +1,31 @@
|
||||||
package dev.loki.lomines.command.admin;
|
package dev.loki.lomines.command.admin.manage;
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.core.mine.Mine;
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
import dev.loki.lomines.core.mine.Mines;
|
import dev.loki.lomines.core.mine.registry.Mines;
|
||||||
import dev.loki.lomines.gui.mine.main.MineEditGui;
|
import dev.loki.lomines.gui.mine.main.MineEditGui;
|
||||||
import dev.loki.lomines.util.ErrorHandler;
|
import dev.loki.lomines.util.ErrorHandler;
|
||||||
import dev.loki.lomines.util.MessageFormatter;
|
import dev.loki.lomines.util.MessageFormatter;
|
||||||
import dev.lolib.commands.annotation.Arg;
|
|
||||||
import dev.lolib.commands.annotation.Command;
|
|
||||||
import dev.lolib.commands.annotation.Subcommand;
|
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
/**
|
public final class MineActionHandler {
|
||||||
* Admin commands for mine management.
|
|
||||||
* Provides commands for creating, deleting, resetting, reloading, listing, and editing mines.
|
|
||||||
*/
|
|
||||||
@Command(value = "lm", permission = "lomines.use")
|
|
||||||
public class AdminCommands {
|
|
||||||
|
|
||||||
private final LoMinesPlugin plugin;
|
private final LoMinesPlugin plugin;
|
||||||
private final Mines mines;
|
private final Mines mines;
|
||||||
private final ErrorHandler errorHandler;
|
private final ErrorHandler errorHandler;
|
||||||
|
|
||||||
public AdminCommands(LoMinesPlugin plugin) {
|
public MineActionHandler(LoMinesPlugin plugin, Mines mines, ErrorHandler errorHandler) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.mines = plugin.getMines();
|
this.mines = mines;
|
||||||
this.errorHandler = new ErrorHandler(plugin.getLogger(), "[LoMines] ");
|
this.errorHandler = errorHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void create(CommandSender sender, String name) {
|
||||||
* Creates a new mine with the given name.
|
|
||||||
* Usage: /lm create <name>
|
|
||||||
*/
|
|
||||||
@Subcommand(value = "create", permission = "lomines.admin.create")
|
|
||||||
public void create(CommandSender sender, @Arg("name") String name) {
|
|
||||||
try {
|
try {
|
||||||
mines.create(name);
|
mines.create(name);
|
||||||
sender.sendMessage(MessageFormatter.success("Successfully created mine: " + name));
|
sender.sendMessage(MessageFormatter.success("Successfully created mine: " + name));
|
||||||
|
|
@ -58,12 +44,7 @@ public class AdminCommands {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void delete(CommandSender sender, String name) {
|
||||||
* Deletes an existing mine.
|
|
||||||
* Usage: /lm delete <name>
|
|
||||||
*/
|
|
||||||
@Subcommand(value = "delete", permission = "lomines.admin.delete")
|
|
||||||
public void delete(CommandSender sender, @Arg("name") String name) {
|
|
||||||
try {
|
try {
|
||||||
mines.delete(name);
|
mines.delete(name);
|
||||||
sender.sendMessage(MessageFormatter.success("Successfully deleted mine: " + name));
|
sender.sendMessage(MessageFormatter.success("Successfully deleted mine: " + name));
|
||||||
|
|
@ -75,12 +56,7 @@ public class AdminCommands {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void edit(CommandSender sender, String mineName) {
|
||||||
* Opens the GUI editor for a mine.
|
|
||||||
* Usage: /lm edit <mine>
|
|
||||||
*/
|
|
||||||
@Subcommand(value = "edit", permission = "lomines.admin.edit")
|
|
||||||
public void edit(CommandSender sender, @Arg("mine") String mineName) {
|
|
||||||
if (!(sender instanceof Player player)) {
|
if (!(sender instanceof Player player)) {
|
||||||
sender.sendMessage(Component.text("This command can only be used by players!", NamedTextColor.RED));
|
sender.sendMessage(Component.text("This command can only be used by players!", NamedTextColor.RED));
|
||||||
return;
|
return;
|
||||||
|
|
@ -95,16 +71,7 @@ public class AdminCommands {
|
||||||
MineEditGui.open(plugin, player, mineName);
|
MineEditGui.open(plugin, player, mineName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void reset(CommandSender sender, String mineName, boolean silent) {
|
||||||
* Resets a mine by refilling all its regions.
|
|
||||||
* Usage: /lm reset <mine> [silent]
|
|
||||||
*/
|
|
||||||
@Subcommand(value = "reset", permission = "lomines.admin.reset")
|
|
||||||
public void reset(
|
|
||||||
CommandSender sender,
|
|
||||||
@Arg("mine") String mineName,
|
|
||||||
@Arg(value = "silent", optional = true, defaultValue = "false") boolean silent
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
Mine mine = mines.get(mineName);
|
Mine mine = mines.get(mineName);
|
||||||
mine.reset(silent);
|
mine.reset(silent);
|
||||||
|
|
@ -114,11 +81,6 @@ public class AdminCommands {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reloads all mine configurations from disk.
|
|
||||||
* Usage: /lm reload
|
|
||||||
*/
|
|
||||||
@Subcommand(value = "reload", permission = "lomines.admin.reload")
|
|
||||||
public void reload(CommandSender sender) {
|
public void reload(CommandSender sender) {
|
||||||
try {
|
try {
|
||||||
mines.getAll().forEach(Mine::stop);
|
mines.getAll().forEach(Mine::stop);
|
||||||
|
|
@ -130,32 +92,4 @@ public class AdminCommands {
|
||||||
"Failed to reload mines", e);
|
"Failed to reload mines", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Lists all active mines.
|
|
||||||
* Usage: /lm list
|
|
||||||
*/
|
|
||||||
@Subcommand(value = "list", permission = "lomines.admin.list")
|
|
||||||
public void list(CommandSender sender) {
|
|
||||||
Collection<Mine> allMines = mines.getAll();
|
|
||||||
|
|
||||||
if (allMines.isEmpty()) {
|
|
||||||
sender.sendMessage(Component.text("No mines found"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sender.sendMessage(Component.text("=== Mines (" + allMines.size() + ") ==="));
|
|
||||||
|
|
||||||
for (Mine mine : allMines) {
|
|
||||||
String status = formatMineStatus(mine);
|
|
||||||
sender.sendMessage(Component.text("- " + mine.getName() + " " + status));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String formatMineStatus(Mine mine) {
|
|
||||||
int blocks = mine.getBlocks();
|
|
||||||
int total = mine.getTotalVolume();
|
|
||||||
double percent = mine.getPercentFilled();
|
|
||||||
return String.format("(%d/%d blocks, %.1f%%)", blocks, total, percent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,23 +1,16 @@
|
||||||
package dev.loki.lomines.command.admin;
|
package dev.loki.lomines.command.admin.misc;
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.core.mine.Mines;
|
import dev.loki.lomines.core.mine.registry.Mines;
|
||||||
import dev.loki.lomines.data.config.loader.common.ConfigLoader;
|
import dev.loki.lomines.data.config.ConfigLoader;
|
||||||
import dev.loki.lomines.util.ErrorHandler;
|
import dev.loki.lomines.util.ErrorHandler;
|
||||||
import dev.loki.lomines.util.MessageFormatter;
|
import dev.loki.lomines.util.MessageFormatter;
|
||||||
import dev.lolib.commands.annotation.Arg;
|
|
||||||
import dev.lolib.commands.annotation.Command;
|
import org.bukkit.Bukkit;
|
||||||
import dev.lolib.commands.annotation.Subcommand;
|
|
||||||
import dev.lolib.scheduler.Scheduler;
|
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Mask commands for mine shape management.
|
|
||||||
* Provides commands for scanning and saving mask positions.
|
|
||||||
*/
|
|
||||||
@Command(value = "lm", permission = "lomines.use")
|
|
||||||
public class MaskCommands {
|
public class MaskCommands {
|
||||||
|
|
||||||
private final LoMinesPlugin plugin;
|
private final LoMinesPlugin plugin;
|
||||||
|
|
@ -30,9 +23,13 @@ public class MaskCommands {
|
||||||
this.errorHandler = new ErrorHandler(plugin.getLogger(), "[LoMines] ");
|
this.errorHandler = new ErrorHandler(plugin.getLogger(), "[LoMines] ");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subcommand(value = "maskscan", permission = "lomines.admin.maskscan")
|
public void handle(CommandSender sender, String[] args) {
|
||||||
public void maskScan(CommandSender sender, @Arg("mine") String name) {
|
if (args.length < 1) {
|
||||||
Scheduler.get(plugin).run(() -> {
|
sender.sendMessage(MessageFormatter.error("Usage: /lm maskscan <mine>"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String name = args[0];
|
||||||
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||||
try {
|
try {
|
||||||
int n = mines.scanAndSaveMask(name);
|
int n = mines.scanAndSaveMask(name);
|
||||||
sender.sendMessage(MessageFormatter.success(
|
sender.sendMessage(MessageFormatter.success(
|
||||||
|
|
@ -1,42 +1,32 @@
|
||||||
package dev.loki.lomines.command.admin;
|
package dev.loki.lomines.command.admin.misc;
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.core.mine.Mine;
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
import dev.loki.lomines.core.mine.Mines;
|
import dev.loki.lomines.core.mine.registry.Mines;
|
||||||
import dev.loki.lomines.data.config.MineConfig;
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
import dev.loki.lomines.data.config.spawn.PlayerSpawnConfig;
|
import dev.loki.lomines.data.config.spawn.PlayerSpawnConfig;
|
||||||
import dev.loki.lomines.data.config.teleport.TeleportConfig;
|
import dev.loki.lomines.data.config.teleport.TeleportConfig;
|
||||||
import dev.loki.lomines.util.ErrorHandler;
|
import dev.loki.lomines.util.ErrorHandler;
|
||||||
import dev.loki.lomines.util.MessageFormatter;
|
import dev.loki.lomines.util.MessageFormatter;
|
||||||
import dev.lolib.commands.annotation.Arg;
|
|
||||||
import dev.lolib.commands.annotation.Subcommand;
|
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
/**
|
public final class TeleportActionHandler {
|
||||||
* Commands for managing teleport and spawn locations for mines.
|
|
||||||
*/
|
|
||||||
class TeleportCommands {
|
|
||||||
|
|
||||||
private final LoMinesPlugin plugin;
|
private final LoMinesPlugin plugin;
|
||||||
private final Mines mines;
|
private final Mines mines;
|
||||||
private final ErrorHandler errorHandler;
|
private final ErrorHandler errorHandler;
|
||||||
|
|
||||||
TeleportCommands(LoMinesPlugin plugin, Mines mines, ErrorHandler errorHandler) {
|
public TeleportActionHandler(LoMinesPlugin plugin, Mines mines, ErrorHandler errorHandler) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.mines = mines;
|
this.mines = mines;
|
||||||
this.errorHandler = errorHandler;
|
this.errorHandler = errorHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void setTeleport(CommandSender sender, String mineName) {
|
||||||
* Sets the teleport location for players when mine resets.
|
|
||||||
* Usage: /lm setteleport <mine>
|
|
||||||
*/
|
|
||||||
@Subcommand(value = "setteleport", permission = "lomines.admin.setteleport")
|
|
||||||
void setTeleport(CommandSender sender, @Arg("mine") String mineName) {
|
|
||||||
if (!(sender instanceof Player player)) {
|
if (!(sender instanceof Player player)) {
|
||||||
sender.sendMessage(Component.text("This command can only be used by players!", NamedTextColor.RED));
|
sender.sendMessage(Component.text("This command can only be used by players!", NamedTextColor.RED));
|
||||||
return;
|
return;
|
||||||
|
|
@ -50,18 +40,7 @@ class TeleportCommands {
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = mine.getConfig();
|
var config = mine.getConfig();
|
||||||
var newTeleport = TeleportConfig.at(player.getLocation());
|
var updatedConfig = withTeleport(config, TeleportConfig.at(player.getLocation()));
|
||||||
|
|
||||||
var updatedConfig = MineConfig.builder(mineName)
|
|
||||||
.region(config.region())
|
|
||||||
.blocks(config.blocks())
|
|
||||||
.reset(config.reset())
|
|
||||||
.rewards(config.rewards())
|
|
||||||
.teleport(newTeleport)
|
|
||||||
.ui(config.ui())
|
|
||||||
.worldGuard(config.worldGuard())
|
|
||||||
.playerSpawn(config.playerSpawn())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
plugin.getMines().getFileManager().saveConfig(updatedConfig);
|
plugin.getMines().getFileManager().saveConfig(updatedConfig);
|
||||||
sender.sendMessage(MessageFormatter.success("Teleport location for mine '" + mineName + "' set to your current position"));
|
sender.sendMessage(MessageFormatter.success("Teleport location for mine '" + mineName + "' set to your current position"));
|
||||||
|
|
@ -72,12 +51,7 @@ class TeleportCommands {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void setSpawn(CommandSender sender, String mineName) {
|
||||||
* Sets the safe spawn location for stuck players after mine reset.
|
|
||||||
* Usage: /lm setspawn <mine>
|
|
||||||
*/
|
|
||||||
@Subcommand(value = "setspawn", permission = "lomines.admin.setspawn")
|
|
||||||
void setSpawn(CommandSender sender, @Arg("mine") String mineName) {
|
|
||||||
if (!(sender instanceof Player player)) {
|
if (!(sender instanceof Player player)) {
|
||||||
sender.sendMessage(Component.text("This command can only be used by players!", NamedTextColor.RED));
|
sender.sendMessage(Component.text("This command can only be used by players!", NamedTextColor.RED));
|
||||||
return;
|
return;
|
||||||
|
|
@ -91,18 +65,7 @@ class TeleportCommands {
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = mine.getConfig();
|
var config = mine.getConfig();
|
||||||
var newSpawn = PlayerSpawnConfig.at(player.getLocation());
|
var updatedConfig = withPlayerSpawn(config, PlayerSpawnConfig.at(player.getLocation()));
|
||||||
|
|
||||||
var updatedConfig = MineConfig.builder(mineName)
|
|
||||||
.region(config.region())
|
|
||||||
.blocks(config.blocks())
|
|
||||||
.reset(config.reset())
|
|
||||||
.rewards(config.rewards())
|
|
||||||
.teleport(config.teleport())
|
|
||||||
.ui(config.ui())
|
|
||||||
.worldGuard(config.worldGuard())
|
|
||||||
.playerSpawn(newSpawn)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
plugin.getMines().getFileManager().saveConfig(updatedConfig);
|
plugin.getMines().getFileManager().saveConfig(updatedConfig);
|
||||||
sender.sendMessage(MessageFormatter.success("Safe spawn location for mine '" + mineName + "' set to your current position"));
|
sender.sendMessage(MessageFormatter.success("Safe spawn location for mine '" + mineName + "' set to your current position"));
|
||||||
|
|
@ -114,12 +77,7 @@ class TeleportCommands {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void clearSpawn(CommandSender sender, String mineName) {
|
||||||
* Clears the spawn location for stuck players.
|
|
||||||
* Usage: /lm clearspawn <mine>
|
|
||||||
*/
|
|
||||||
@Subcommand(value = "clearspawn", permission = "lomines.admin.setspawn")
|
|
||||||
void clearSpawn(CommandSender sender, @Arg("mine") String mineName) {
|
|
||||||
try {
|
try {
|
||||||
Mine mine = mines.find(mineName).orElse(null);
|
Mine mine = mines.find(mineName).orElse(null);
|
||||||
if (mine == null) {
|
if (mine == null) {
|
||||||
|
|
@ -128,16 +86,7 @@ class TeleportCommands {
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = mine.getConfig();
|
var config = mine.getConfig();
|
||||||
var updatedConfig = MineConfig.builder(mineName)
|
var updatedConfig = withPlayerSpawn(config, PlayerSpawnConfig.disabled());
|
||||||
.region(config.region())
|
|
||||||
.blocks(config.blocks())
|
|
||||||
.reset(config.reset())
|
|
||||||
.rewards(config.rewards())
|
|
||||||
.teleport(config.teleport())
|
|
||||||
.ui(config.ui())
|
|
||||||
.worldGuard(config.worldGuard())
|
|
||||||
.playerSpawn(PlayerSpawnConfig.disabled())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
plugin.getMines().getFileManager().saveConfig(updatedConfig);
|
plugin.getMines().getFileManager().saveConfig(updatedConfig);
|
||||||
sender.sendMessage(MessageFormatter.success("Spawn location for mine '" + mineName + "' cleared"));
|
sender.sendMessage(MessageFormatter.success("Spawn location for mine '" + mineName + "' cleared"));
|
||||||
|
|
@ -148,7 +97,33 @@ class TeleportCommands {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String formatLocation(Location loc) {
|
private MineConfig withTeleport(MineConfig config, TeleportConfig teleport) {
|
||||||
|
return MineConfig.builder(config.name())
|
||||||
|
.region(config.region())
|
||||||
|
.blocks(config.blocks())
|
||||||
|
.reset(config.reset())
|
||||||
|
.rewards(config.rewards())
|
||||||
|
.teleport(teleport)
|
||||||
|
.ui(config.ui())
|
||||||
|
.worldGuard(config.worldGuard())
|
||||||
|
.playerSpawn(config.playerSpawn())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MineConfig withPlayerSpawn(MineConfig config, PlayerSpawnConfig spawn) {
|
||||||
|
return MineConfig.builder(config.name())
|
||||||
|
.region(config.region())
|
||||||
|
.blocks(config.blocks())
|
||||||
|
.reset(config.reset())
|
||||||
|
.rewards(config.rewards())
|
||||||
|
.teleport(config.teleport())
|
||||||
|
.ui(config.ui())
|
||||||
|
.worldGuard(config.worldGuard())
|
||||||
|
.playerSpawn(spawn)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String formatLocation(Location loc) {
|
||||||
return String.format("%.1f, %.1f, %.1f in %s",
|
return String.format("%.1f, %.1f, %.1f in %s",
|
||||||
loc.getX(), loc.getY(), loc.getZ(), loc.getWorld().getName());
|
loc.getX(), loc.getY(), loc.getZ(), loc.getWorld().getName());
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package dev.loki.lomines.command.admin.misc;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import dev.loki.lomines.core.mine.registry.Mines;
|
||||||
|
import dev.loki.lomines.util.ErrorHandler;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
public class TeleportCommands {
|
||||||
|
|
||||||
|
private final TeleportActionHandler actionHandler;
|
||||||
|
|
||||||
|
public TeleportCommands(LoMinesPlugin plugin, Mines mines, ErrorHandler errorHandler) {
|
||||||
|
this.actionHandler = new TeleportActionHandler(plugin, mines, errorHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(CommandSender sender, String subcommand, String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
sender.sendMessage(Component.text("Usage: /lm " + subcommand + " <mine>", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (subcommand.toLowerCase()) {
|
||||||
|
case "setteleport" -> actionHandler.setTeleport(sender, args[0]);
|
||||||
|
case "setspawn" -> actionHandler.setSpawn(sender, args[0]);
|
||||||
|
case "clearspawn" -> actionHandler.clearSpawn(sender, args[0]);
|
||||||
|
default -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,35 +1,30 @@
|
||||||
package dev.loki.lomines.command.admin;
|
package dev.loki.lomines.command.admin.stats;
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.core.mine.Mine;
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
import dev.loki.lomines.integration.hologram.HologramManager;
|
import dev.loki.lomines.integration.hologram.HologramManager;
|
||||||
import dev.lolilb.commands.annotation.Arg;
|
|
||||||
import dev.lolilb.commands.annotation.Subcommand;
|
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
/**
|
public class HologramCommands {
|
||||||
* Commands for managing holograms above mines.
|
|
||||||
*/
|
|
||||||
class HologramCommands {
|
|
||||||
|
|
||||||
private final LoMinesPlugin plugin;
|
private final LoMinesPlugin plugin;
|
||||||
private final HologramManager hologramManager;
|
private final HologramManager hologramManager;
|
||||||
|
|
||||||
HologramCommands(LoMinesPlugin plugin) {
|
public HologramCommands(LoMinesPlugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.hologramManager = plugin.getHologramManager();
|
this.hologramManager = plugin.getHologramManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void handle(CommandSender sender, String[] args) {
|
||||||
* Toggles hologram for a mine.
|
if (args.length < 1) {
|
||||||
* Usage: /lm hologram <mine> [on|off]
|
sender.sendMessage(Component.text("Usage: /lm hologram <mine> [on|off]", NamedTextColor.RED));
|
||||||
*/
|
return;
|
||||||
@Subcommand(value = "hologram", permission = "lomines.admin.hologram")
|
}
|
||||||
void hologram(CommandSender sender, @Arg("mine") String mineName,
|
String mineName = args[0];
|
||||||
@Arg(value = "state", optional = true) String state) {
|
String state = args.length > 1 ? args[1] : null;
|
||||||
|
|
||||||
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
||||||
if (mine == null) {
|
if (mine == null) {
|
||||||
sender.sendMessage(Component.text("Шахта не найдена: " + mineName, NamedTextColor.RED));
|
sender.sendMessage(Component.text("Шахта не найдена: " + mineName, NamedTextColor.RED));
|
||||||
|
|
@ -43,10 +38,9 @@ class HologramCommands {
|
||||||
|
|
||||||
boolean enable;
|
boolean enable;
|
||||||
if (state == null) {
|
if (state == null) {
|
||||||
// Toggle
|
|
||||||
enable = hologramManager.toggleHologram(mine);
|
enable = hologramManager.toggleHologram(mine);
|
||||||
} else {
|
} else {
|
||||||
enable = state.equalsIgnoreCase("on") || state.equalsIgnoreCase("true") || state.equalsIgnoreCase("enable");
|
enable = "on".equalsIgnoreCase(state) || "true".equalsIgnoreCase(state) || "enable".equalsIgnoreCase(state);
|
||||||
if (enable) {
|
if (enable) {
|
||||||
hologramManager.createMineHologram(mine);
|
hologramManager.createMineHologram(mine);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
package dev.loki.lomines.command.admin.stats;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
|
import dev.loki.lomines.core.mine.registry.Mines;
|
||||||
|
import dev.loki.lomines.data.stats.model.LeaderboardEntry;
|
||||||
|
import dev.loki.lomines.util.ErrorHandler;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public final class LeaderboardRenderer {
|
||||||
|
|
||||||
|
private LeaderboardRenderer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendTopRows(CommandSender sender, List<LeaderboardEntry> rows) {
|
||||||
|
if (rows.isEmpty()) {
|
||||||
|
sender.sendMessage(Component.text("Пока нет данных.", NamedTextColor.GRAY));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < rows.size(); i++) {
|
||||||
|
LeaderboardEntry e = rows.get(i);
|
||||||
|
String name = formatPlayerName(e.playerId());
|
||||||
|
sender.sendMessage(Component.text(
|
||||||
|
(i + 1) + ". " + name + " \u2014 " + e.count(),
|
||||||
|
NamedTextColor.WHITE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatPlayerName(UUID id) {
|
||||||
|
String name = Bukkit.getOfflinePlayer(id).getName();
|
||||||
|
return name != null ? name : id.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int clampLimit(int limit) {
|
||||||
|
return Math.max(1, Math.min(50, limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Integer tryParsePositiveInt(String s) {
|
||||||
|
try {
|
||||||
|
int v = Integer.parseInt(s);
|
||||||
|
return v > 0 ? v : null;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UUID resolvePlayerUuid(CommandSender sender, String targetName, ErrorHandler errorHandler) {
|
||||||
|
String name = targetName == null ? "" : targetName.trim();
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
if (!(sender instanceof Player self)) {
|
||||||
|
errorHandler.sendError(sender, "Укажите игрока: /lm stats <ник>");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return self.getUniqueId();
|
||||||
|
} else {
|
||||||
|
if (!sender.hasPermission("lomines.stats.others")) {
|
||||||
|
errorHandler.handlePermissionDenied(sender, "смотреть чужую статистику");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Player online = Bukkit.getPlayerExact(name);
|
||||||
|
if (online != null) {
|
||||||
|
return online.getUniqueId();
|
||||||
|
} else {
|
||||||
|
OfflinePlayer off = Bukkit.getOfflinePlayer(name);
|
||||||
|
return off.getUniqueId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void renderTopCommand(CommandSender sender, String arg1, String arg2, Mines mines, LoMinesPlugin plugin) {
|
||||||
|
TopRequest req = parseTopRequest(arg1, arg2);
|
||||||
|
if (req.mineName() != null) {
|
||||||
|
renderMineTop(sender, req.mineName(), req.limit(), mines, plugin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<LeaderboardEntry> rows = plugin.getStatsManager().getLeaderboard().getTopTotal(req.limit());
|
||||||
|
sender.sendMessage(Component.text("=== Общий топ ===", NamedTextColor.GOLD));
|
||||||
|
sendTopRows(sender, rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record TopRequest(int limit, String mineName) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TopRequest parseTopRequest(String arg1, String arg2) {
|
||||||
|
String a = arg1 == null ? "" : arg1.trim();
|
||||||
|
String b = arg2 == null ? "" : arg2.trim();
|
||||||
|
if (a.isEmpty()) {
|
||||||
|
return new TopRequest(clampLimit(10), null);
|
||||||
|
}
|
||||||
|
if (b.isEmpty()) {
|
||||||
|
Integer asLimit = tryParsePositiveInt(a);
|
||||||
|
if (asLimit != null) {
|
||||||
|
return new TopRequest(clampLimit(asLimit), null);
|
||||||
|
}
|
||||||
|
return new TopRequest(clampLimit(10), a);
|
||||||
|
}
|
||||||
|
Integer limParsed = tryParsePositiveInt(b);
|
||||||
|
return new TopRequest(clampLimit(limParsed != null ? limParsed : 10), a);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void renderMineTop(CommandSender sender, String mineName, int limit, Mines mines, LoMinesPlugin plugin) {
|
||||||
|
var found = mines.find(mineName);
|
||||||
|
if (found.isEmpty()) {
|
||||||
|
sender.sendMessage(Component.text("Шахта не найдена: " + mineName, NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Mine m = found.get();
|
||||||
|
String key = m.getName();
|
||||||
|
List<LeaderboardEntry> rows = plugin.getStatsManager().getLeaderboard().getTopByMine(key, limit);
|
||||||
|
sender.sendMessage(Component.text("=== Топ по шахте \u00ab" + key + "\u00bb ===", NamedTextColor.GOLD));
|
||||||
|
sendTopRows(sender, rows);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package dev.loki.lomines.command.admin.stats;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import dev.loki.lomines.core.mine.registry.Mines;
|
||||||
|
import dev.loki.lomines.data.stats.model.PlayerStats;
|
||||||
|
import dev.loki.lomines.data.stats.service.StatsManager;
|
||||||
|
import dev.loki.lomines.util.ErrorHandler;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class StatsCommands {
|
||||||
|
|
||||||
|
private final LoMinesPlugin plugin;
|
||||||
|
private final Mines mines;
|
||||||
|
private final ErrorHandler errorHandler;
|
||||||
|
|
||||||
|
public StatsCommands(LoMinesPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.mines = plugin.getMines();
|
||||||
|
this.errorHandler = new ErrorHandler(plugin.getLogger(), "[LoMines] ");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(CommandSender sender, String subcommand, String[] args) {
|
||||||
|
switch (subcommand.toLowerCase()) {
|
||||||
|
case "stats" -> handleStats(sender, args);
|
||||||
|
case "top" -> {
|
||||||
|
if (isStatsDisabled(sender)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String arg1 = args.length > 0 ? args[0] : "";
|
||||||
|
String arg2 = args.length > 1 ? args[1] : "";
|
||||||
|
LeaderboardRenderer.renderTopCommand(sender, arg1, arg2, mines, plugin);
|
||||||
|
}
|
||||||
|
default -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleStats(CommandSender sender, String[] args) {
|
||||||
|
if (isStatsDisabled(sender)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String targetName = args.length > 0 ? args[0] : "";
|
||||||
|
UUID uuid = LeaderboardRenderer.resolvePlayerUuid(sender, targetName, errorHandler);
|
||||||
|
if (uuid == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StatsManager sm = plugin.getStatsManager();
|
||||||
|
PlayerStats ps = sm.getOrCreate(uuid);
|
||||||
|
String displayName = LeaderboardRenderer.formatPlayerName(uuid);
|
||||||
|
sender.sendMessage(Component.text("=== Статистика: " + displayName + " ===", NamedTextColor.GOLD));
|
||||||
|
sender.sendMessage(Component.text("Всего блоков: " + ps.getTotalBlocks(), NamedTextColor.WHITE));
|
||||||
|
int pos = sm.getLeaderboard().getPosition(uuid);
|
||||||
|
if (pos > 0) {
|
||||||
|
sender.sendMessage(Component.text("Место в общем топе: #" + pos, NamedTextColor.GRAY));
|
||||||
|
}
|
||||||
|
Map<String, Long> byMine = ps.getMineStatsSnapshot();
|
||||||
|
if (byMine.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sender.sendMessage(Component.text("По шахтам:", NamedTextColor.GRAY));
|
||||||
|
byMine.entrySet().stream()
|
||||||
|
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
|
||||||
|
.limit(12)
|
||||||
|
.forEach(e -> sender.sendMessage(Component.text(
|
||||||
|
" " + e.getKey() + ": " + e.getValue(), NamedTextColor.DARK_AQUA)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isStatsDisabled(CommandSender sender) {
|
||||||
|
if (!plugin.getConfig().getBoolean("statistics-enabled", true)) {
|
||||||
|
sender.sendMessage(Component.text("Статистика отключена в config.yml.", NamedTextColor.RED));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,31 +4,26 @@ import dev.loki.lomines.LoMinesPlugin;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.command.TabCompleter;
|
import org.bukkit.command.TabCompleter;
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tab completer for LoMines commands.
|
|
||||||
* Provides auto-completion suggestions for all subcommands and arguments.
|
|
||||||
*/
|
|
||||||
public class LoMinesTabCompleter implements TabCompleter {
|
public class LoMinesTabCompleter implements TabCompleter {
|
||||||
|
|
||||||
private final LoMinesPlugin plugin;
|
|
||||||
|
|
||||||
private static final List<String> ALL_COMMANDS = List.of(
|
private static final List<String> ALL_COMMANDS = List.of(
|
||||||
"create", "delete", "reset", "reload", "list", "wand", "group",
|
"create", "delete", "reset", "reload", "list", "wand", "group",
|
||||||
"stats", "top", "maskscan", "edit", "setteleport", "setspawn",
|
"stats", "top", "maskscan", "edit", "setteleport", "setspawn",
|
||||||
"clearspawn", "help"
|
"clearspawn", "info", "tp", "copy", "regions", "addregion", "removeregion", "help"
|
||||||
);
|
);
|
||||||
|
|
||||||
private static final List<String> BOOLEAN_VALUES = List.of("true", "false");
|
private final LoMinesPlugin plugin;
|
||||||
private static final List<String> TOP_LIMITS = List.of("5", "10", "15", "20", "25", "50");
|
private final SubcommandCompleter completer;
|
||||||
|
private final PermissionPredicate permission;
|
||||||
|
|
||||||
public LoMinesTabCompleter(LoMinesPlugin plugin) {
|
public LoMinesTabCompleter(LoMinesPlugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
this.completer = new SubcommandCompleter(plugin);
|
||||||
|
this.permission = new PermissionPredicate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -41,130 +36,27 @@ public class LoMinesTabCompleter implements TabCompleter {
|
||||||
|
|
||||||
return switch (args.length) {
|
return switch (args.length) {
|
||||||
case 1 -> completeSubcommands(sender, partial);
|
case 1 -> completeSubcommands(sender, partial);
|
||||||
case 2 -> completeSecondArg(sender, args[0].toLowerCase(), partial);
|
case 2 -> completer.completeSecondArg(sender, args[0].toLowerCase(), partial);
|
||||||
case 3 -> completeThirdArg(args[0].toLowerCase(), args[1], partial);
|
case 3 -> completer.completeThirdArg(args[0].toLowerCase(), args[1], partial);
|
||||||
default -> new ArrayList<>();
|
default -> new ArrayList<>();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isLoMinesCommand(String name) {
|
private boolean isLoMinesCommand(String name) {
|
||||||
return name.equalsIgnoreCase("lm") ||
|
return "lm".equalsIgnoreCase(name) ||
|
||||||
name.equalsIgnoreCase("lomines") ||
|
"lomines".equalsIgnoreCase(name) ||
|
||||||
name.equalsIgnoreCase("mine") ||
|
"mine".equalsIgnoreCase(name) ||
|
||||||
name.equalsIgnoreCase("mines");
|
"mines".equalsIgnoreCase(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> completeSubcommands(CommandSender sender, String partial) {
|
private List<String> completeSubcommands(CommandSender sender, String partial) {
|
||||||
List<String> completions = new ArrayList<>();
|
List<String> completions = new ArrayList<>();
|
||||||
for (String cmd : ALL_COMMANDS) {
|
for (String cmd : ALL_COMMANDS) {
|
||||||
if (cmd.startsWith(partial) && hasPermission(sender, cmd)) {
|
if (cmd.startsWith(partial) && permission.hasPermission(sender, cmd)) {
|
||||||
completions.add(cmd);
|
completions.add(cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return completions;
|
return completions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> completeSecondArg(CommandSender sender, String subcommand, String partial) {
|
|
||||||
return switch (subcommand) {
|
|
||||||
case "create" -> completeCreate(partial);
|
|
||||||
case "delete", "maskscan", "edit", "setteleport", "setspawn", "clearspawn" ->
|
|
||||||
filterStartsWith(getMineNames(), partial);
|
|
||||||
case "reset" -> completeReset(partial);
|
|
||||||
case "stats" -> completeStats(sender, partial);
|
|
||||||
case "top" -> completeTopFirstArg(partial);
|
|
||||||
default -> new ArrayList<>();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> completeThirdArg(String subcommand, String arg2, String partial) {
|
|
||||||
return switch (subcommand) {
|
|
||||||
case "reset" -> filterStartsWith(BOOLEAN_VALUES, partial);
|
|
||||||
case "top" -> completeTopSecondArg(arg2, partial);
|
|
||||||
default -> new ArrayList<>();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> completeCreate(String partial) {
|
|
||||||
if (partial.isEmpty()) {
|
|
||||||
return new ArrayList<>(List.of("<name>"));
|
|
||||||
}
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> completeReset(String partial) {
|
|
||||||
List<String> mines = filterStartsWith(getMineNames(), partial);
|
|
||||||
if (partial.isEmpty() || "true".startsWith(partial) || "false".startsWith(partial)) {
|
|
||||||
mines.addAll(filterStartsWith(BOOLEAN_VALUES, partial));
|
|
||||||
}
|
|
||||||
return mines;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> completeStats(CommandSender sender, String partial) {
|
|
||||||
if (!sender.hasPermission("lomines.stats.others")) {
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
return filterStartsWith(getOnlinePlayerNames(), partial);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> completeTopFirstArg(String partial) {
|
|
||||||
List<String> completions = new ArrayList<>();
|
|
||||||
|
|
||||||
completions.addAll(filterStartsWith(getMineNames(), partial));
|
|
||||||
|
|
||||||
if (isNumericPartial(partial)) {
|
|
||||||
completions.addAll(filterStartsWith(TOP_LIMITS, partial));
|
|
||||||
}
|
|
||||||
|
|
||||||
return completions;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> completeTopSecondArg(String firstArg, String partial) {
|
|
||||||
boolean firstArgWasMine = plugin.getMines().find(firstArg).isPresent();
|
|
||||||
|
|
||||||
if (firstArgWasMine && isNumericPartial(partial)) {
|
|
||||||
return filterStartsWith(TOP_LIMITS, partial);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!firstArgWasMine && isNumericPartial(firstArg)) {
|
|
||||||
return filterStartsWith(getMineNames(), partial);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isNumericPartial(String partial) {
|
|
||||||
return partial.isEmpty() || partial.matches("\\d*");
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> filterStartsWith(List<String> options, String partial) {
|
|
||||||
return options.stream()
|
|
||||||
.filter(s -> s.toLowerCase().startsWith(partial))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getMineNames() {
|
|
||||||
return plugin.getMines().getAll().stream()
|
|
||||||
.map(mine -> mine.getName())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getOnlinePlayerNames() {
|
|
||||||
return plugin.getServer().getOnlinePlayers().stream()
|
|
||||||
.map(Player::getName)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasPermission(CommandSender sender, String cmd) {
|
|
||||||
return switch (cmd) {
|
|
||||||
case "create", "delete", "reload", "list", "maskscan", "edit" ->
|
|
||||||
sender.hasPermission("lomines.admin");
|
|
||||||
case "reset" -> sender.hasPermission("lomines.admin.reset");
|
|
||||||
case "setteleport" -> sender.hasPermission("lomines.admin.setteleport");
|
|
||||||
case "setspawn", "clearspawn" -> sender.hasPermission("lomines.admin.setspawn");
|
|
||||||
case "wand", "group" -> sender.hasPermission("lomines.admin.wand");
|
|
||||||
case "stats", "top" -> sender.hasPermission("lomines.stats");
|
|
||||||
case "help" -> sender.hasPermission("lomines.use");
|
|
||||||
default -> true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package dev.loki.lomines.command.common;
|
||||||
|
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
public final class PermissionPredicate {
|
||||||
|
|
||||||
|
public boolean hasPermission(CommandSender sender, String cmd) {
|
||||||
|
return switch (cmd) {
|
||||||
|
case "create", "delete", "reload", "list", "maskscan", "edit", "info" ->
|
||||||
|
sender.hasPermission("lomines.admin");
|
||||||
|
case "reset" -> sender.hasPermission("lomines.admin.reset");
|
||||||
|
case "setteleport" -> sender.hasPermission("lomines.admin.setteleport");
|
||||||
|
case "setspawn", "clearspawn" -> sender.hasPermission("lomines.admin.setspawn");
|
||||||
|
case "wand", "group" -> sender.hasPermission("lomines.admin.wand");
|
||||||
|
case "stats", "top" -> sender.hasPermission("lomines.stats");
|
||||||
|
case "tp" -> sender.hasPermission("lomines.teleport");
|
||||||
|
case "copy" -> sender.hasPermission("lomines.admin.copy");
|
||||||
|
case "regions", "addregion", "removeregion" -> sender.hasPermission("lomines.admin.regions");
|
||||||
|
case "help" -> sender.hasPermission("lomines.use");
|
||||||
|
default -> true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
package dev.loki.lomines.command.common;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public final class SubcommandCompleter {
|
||||||
|
|
||||||
|
private static final List<String> BOOLEAN_VALUES = List.of("true", "false");
|
||||||
|
private static final List<String> TOP_LIMITS = List.of("5", "10", "15", "20", "25", "50");
|
||||||
|
|
||||||
|
private final LoMinesPlugin plugin;
|
||||||
|
|
||||||
|
public SubcommandCompleter(LoMinesPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> completeSecondArg(CommandSender sender, String subcommand, String partial) {
|
||||||
|
return switch (subcommand) {
|
||||||
|
case "create" -> completeCreate(partial);
|
||||||
|
case "delete", "maskscan", "edit", "setteleport", "setspawn", "clearspawn", "info",
|
||||||
|
"regions", "addregion" -> filterStartsWith(getMineNames(), partial);
|
||||||
|
case "reset" -> completeReset(partial);
|
||||||
|
case "stats" -> completeStats(sender, partial);
|
||||||
|
case "top" -> completeTopFirstArg(partial);
|
||||||
|
case "tp" -> completeTeleport(sender, partial);
|
||||||
|
case "copy" -> filterStartsWith(getMineNames(), partial);
|
||||||
|
case "removeregion" -> filterStartsWith(getMineNames(), partial);
|
||||||
|
default -> new ArrayList<>();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> completeThirdArg(String subcommand, String arg2, String partial) {
|
||||||
|
return switch (subcommand) {
|
||||||
|
case "reset" -> filterStartsWith(BOOLEAN_VALUES, partial);
|
||||||
|
case "top" -> completeTopSecondArg(arg2, partial);
|
||||||
|
case "copy" -> filterStartsWith(getMineNames(), partial);
|
||||||
|
case "removeregion" -> completeRegionIndex(arg2, partial);
|
||||||
|
default -> new ArrayList<>();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> completeRegionIndex(String mineName, String partial) {
|
||||||
|
var mine = plugin.getMines().find(mineName);
|
||||||
|
if (mine.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
int regionCount = mine.get().getRegions().size();
|
||||||
|
List<String> indices = new ArrayList<>();
|
||||||
|
for (int i = 1; i <= regionCount; i++) {
|
||||||
|
indices.add(String.valueOf(i));
|
||||||
|
}
|
||||||
|
return filterStartsWith(indices, partial);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> completeCreate(String partial) {
|
||||||
|
if (partial.isEmpty()) {
|
||||||
|
return new ArrayList<>(List.of("<name>"));
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> completeReset(String partial) {
|
||||||
|
List<String> mines = filterStartsWith(getMineNames(), partial);
|
||||||
|
if (partial.isEmpty() || "true".startsWith(partial) || "false".startsWith(partial)) {
|
||||||
|
mines.addAll(filterStartsWith(BOOLEAN_VALUES, partial));
|
||||||
|
}
|
||||||
|
return mines;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> completeStats(CommandSender sender, String partial) {
|
||||||
|
if (!sender.hasPermission("lomines.stats.others")) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return filterStartsWith(getOnlinePlayerNames(), partial);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> completeTopFirstArg(String partial) {
|
||||||
|
List<String> completions = new ArrayList<>();
|
||||||
|
completions.addAll(filterStartsWith(getMineNames(), partial));
|
||||||
|
if (isNumericPartial(partial)) {
|
||||||
|
completions.addAll(filterStartsWith(TOP_LIMITS, partial));
|
||||||
|
}
|
||||||
|
return completions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> completeTopSecondArg(String firstArg, String partial) {
|
||||||
|
boolean firstArgWasMine = plugin.getMines().find(firstArg).isPresent();
|
||||||
|
if (firstArgWasMine && isNumericPartial(partial)) {
|
||||||
|
return filterStartsWith(TOP_LIMITS, partial);
|
||||||
|
}
|
||||||
|
if (!firstArgWasMine && isNumericPartial(firstArg)) {
|
||||||
|
return filterStartsWith(getMineNames(), partial);
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> completeTeleport(CommandSender sender, String partial) {
|
||||||
|
if (!sender.hasPermission("lomines.teleport")) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return filterStartsWith(getMineNames(), partial);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isNumericPartial(String partial) {
|
||||||
|
return partial.isEmpty() || partial.matches("\\d*");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> filterStartsWith(List<String> options, String partial) {
|
||||||
|
return options.stream()
|
||||||
|
.filter(s -> s.toLowerCase().startsWith(partial))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getMineNames() {
|
||||||
|
return plugin.getMines().getAll().stream()
|
||||||
|
.map(mine -> mine.getName())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getOnlinePlayerNames() {
|
||||||
|
return plugin.getServer().getOnlinePlayers().stream()
|
||||||
|
.map(Player::getName)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,19 +3,11 @@ package dev.loki.lomines.command.player;
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.util.ErrorHandler;
|
import dev.loki.lomines.util.ErrorHandler;
|
||||||
import dev.loki.lomines.wand.group.GroupWandItem;
|
import dev.loki.lomines.wand.group.GroupWandItem;
|
||||||
import dev.lolib.commands.annotation.Arg;
|
|
||||||
import dev.lolib.commands.annotation.Command;
|
|
||||||
import dev.lolib.commands.annotation.Subcommand;
|
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
/**
|
|
||||||
* Player commands for mine interaction.
|
|
||||||
* Provides commands for wand and group management.
|
|
||||||
*/
|
|
||||||
@Command(value = "lm", permission = "lomines.use")
|
|
||||||
public class PlayerCommands {
|
public class PlayerCommands {
|
||||||
|
|
||||||
private final LoMinesPlugin plugin;
|
private final LoMinesPlugin plugin;
|
||||||
|
|
@ -26,8 +18,9 @@ public class PlayerCommands {
|
||||||
this.errorHandler = new ErrorHandler(plugin.getLogger(), "[LoMines] ");
|
this.errorHandler = new ErrorHandler(plugin.getLogger(), "[LoMines] ");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subcommand(value = "wand", permission = "lomines.admin.wand")
|
public void handle(CommandSender sender, String subcommand, String[] args) {
|
||||||
public void wand(CommandSender sender) {
|
switch (subcommand.toLowerCase()) {
|
||||||
|
case "wand" -> {
|
||||||
if (!(sender instanceof Player player)) {
|
if (!(sender instanceof Player player)) {
|
||||||
errorHandler.sendError(sender, "Только для игрока.");
|
errorHandler.sendError(sender, "Только для игрока.");
|
||||||
return;
|
return;
|
||||||
|
|
@ -37,15 +30,20 @@ public class PlayerCommands {
|
||||||
"Палочка группы: ЛКМ/ПКМ по блоку — углы, Shift+ПКМ — меню (9 шахт). Задайте /lm group <префикс>",
|
"Палочка группы: ЛКМ/ПКМ по блоку — углы, Shift+ПКМ — меню (9 шахт). Задайте /lm group <префикс>",
|
||||||
NamedTextColor.GREEN));
|
NamedTextColor.GREEN));
|
||||||
}
|
}
|
||||||
|
case "group" -> {
|
||||||
@Subcommand(value = "group", permission = "lomines.admin.wand")
|
if (args.length < 1) {
|
||||||
public void group(CommandSender sender, @Arg("prefix") String prefix) {
|
sender.sendMessage(Component.text("Usage: /lm group <prefix>", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!(sender instanceof Player player)) {
|
if (!(sender instanceof Player player)) {
|
||||||
errorHandler.sendError(sender, "Префикс задаётся в игре.");
|
errorHandler.sendError(sender, "Префикс задаётся в игре.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
plugin.getGroupWandManager().getSession(player.getUniqueId()).setBaseName(prefix);
|
plugin.getGroupWandManager().getSession(player.getUniqueId()).setBaseName(args[0]);
|
||||||
String p = prefix.trim();
|
String p = args[0].trim();
|
||||||
player.sendMessage(Component.text("Префикс: " + p + " → файлы " + p + "_1.yml … " + p + "_9.yml", NamedTextColor.AQUA));
|
player.sendMessage(Component.text("Префикс: " + p + " → файлы " + p + "_1.yml … " + p + "_9.yml", NamedTextColor.AQUA));
|
||||||
}
|
}
|
||||||
|
default -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
package dev.loki.lomines.command.player;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
|
import dev.loki.lomines.util.location.geo.Cuboid;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public class TeleportCommand {
|
||||||
|
|
||||||
|
private final LoMinesPlugin plugin;
|
||||||
|
|
||||||
|
public TeleportCommand(LoMinesPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(CommandSender sender, String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
sender.sendMessage(Component.text("Usage: /lm tp <mine>", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String mineName = args[0];
|
||||||
|
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
sender.sendMessage(Component.text("Эта команда только для игроков!", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
||||||
|
if (mine == null) {
|
||||||
|
player.sendMessage(Component.text("Шахта не найдена: " + mineName, NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Location target = resolveTarget(player, mine);
|
||||||
|
if (target == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.teleport(target);
|
||||||
|
player.sendMessage(Component.text("§aТелепортировано к шахте §f" + mine.getName() +
|
||||||
|
" §a(§f" + String.format("%.0f, %.0f, %.0f", target.getX(), target.getY(), target.getZ()) + "§a)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Location resolveTarget(Player player, Mine mine) {
|
||||||
|
var config = mine.getConfig();
|
||||||
|
if (config.teleport().enabled() && config.teleport().getLocation().isPresent()) {
|
||||||
|
return config.teleport().getLocation().get();
|
||||||
|
}
|
||||||
|
if (!mine.getRegions().isEmpty()) {
|
||||||
|
Cuboid region = mine.getRegions().get(0);
|
||||||
|
double centerX = (region.getMinX() + region.getMaxX()) / 2.0 + 0.5;
|
||||||
|
double centerZ = (region.getMinZ() + region.getMaxZ()) / 2.0 + 0.5;
|
||||||
|
double centerY = region.getMaxY() + 1;
|
||||||
|
return new Location(region.getWorld(), centerX, centerY, centerZ);
|
||||||
|
}
|
||||||
|
player.sendMessage(Component.text("У шахты нет настроенной точки телепорта!", NamedTextColor.RED));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
/**
|
|
||||||
* Core components of the LoMines plugin.
|
|
||||||
* <p>
|
|
||||||
* This package contains the main plugin class and core system classes:
|
|
||||||
* <ul>
|
|
||||||
* <li>{@code LoMinesPlugin} - Main plugin class extending LoPlugin</li>
|
|
||||||
* <li>{@code Mines} - Registry for managing all active mines</li>
|
|
||||||
* <li>{@code Mine} - Representation of a single mine with its configuration and state</li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
package dev.loki.lomines.core.common;
|
|
||||||
|
|
@ -1,31 +1,28 @@
|
||||||
package dev.loki.lomines.core.mine;
|
package dev.loki.lomines.core.mine.model;
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.core.service.MineFileManager;
|
|
||||||
import dev.loki.lomines.block.BlockSetter;
|
import dev.loki.lomines.block.BlockSetter;
|
||||||
import dev.loki.lomines.data.config.MineConfig;
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
import dev.loki.lomines.data.config.block.BlockKey;
|
import dev.loki.lomines.data.config.block.BlockKey;
|
||||||
import dev.loki.lomines.data.config.block.FillMode;
|
import dev.loki.lomines.data.config.block.FillMode;
|
||||||
import dev.loki.lomines.handler.ActionBarHandler;
|
import dev.loki.lomines.handler.ui.ActionBarHandler;
|
||||||
import dev.loki.lomines.handler.MineBlockHandler;
|
import dev.loki.lomines.handler.block.MineBlockHandler;
|
||||||
import dev.loki.lomines.handler.MineResetHandler;
|
import dev.loki.lomines.handler.reset.MineResetHandler;
|
||||||
import dev.loki.lomines.util.location.BlockKeys;
|
import dev.loki.lomines.util.location.BlockKeys;
|
||||||
import dev.loki.lomines.util.location.Cuboid;
|
import dev.loki.lomines.util.location.geo.Cuboid;
|
||||||
import dev.loki.lomines.util.location.LocationParser;
|
import dev.loki.lomines.util.location.LocationParser;
|
||||||
import dev.lolib.scheduler.ScheduledTask;
|
import org.bukkit.Bukkit;
|
||||||
import dev.lolib.scheduler.Scheduler;
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a single mine with its configuration and state.
|
|
||||||
* Thread-safe implementation using AtomicInteger for mutable state.
|
|
||||||
* Updated for new section-based configuration (v2).
|
|
||||||
*/
|
|
||||||
public final class Mine {
|
public final class Mine {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
@ -33,14 +30,12 @@ public final class Mine {
|
||||||
private final MineConfig config;
|
private final MineConfig config;
|
||||||
private final BlockSetter blockSetter;
|
private final BlockSetter blockSetter;
|
||||||
private final LoMinesPlugin plugin;
|
private final LoMinesPlugin plugin;
|
||||||
private final int totalVolume;
|
private final MineState state;
|
||||||
private final Set<String> maskBlockKeys;
|
|
||||||
private final AtomicInteger blocks;
|
|
||||||
private final AtomicInteger ticks;
|
|
||||||
private final MineResetHandler resetHandler;
|
private final MineResetHandler resetHandler;
|
||||||
private final MineBlockHandler blockHandler;
|
private final MineBlockHandler blockHandler;
|
||||||
private final ActionBarHandler actionBarHandler;
|
private final ActionBarHandler actionBarHandler;
|
||||||
private ScheduledTask actionBarTask;
|
private BukkitTask actionBarTask;
|
||||||
|
private final Set<String> maskBlockKeys;
|
||||||
|
|
||||||
public Mine(String name, MineConfig config, LoMinesPlugin plugin) {
|
public Mine(String name, MineConfig config, LoMinesPlugin plugin) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
@ -49,30 +44,21 @@ public final class Mine {
|
||||||
this.regions = config.region().regions();
|
this.regions = config.region().regions();
|
||||||
this.blockSetter = createBlockSetter(config, plugin);
|
this.blockSetter = createBlockSetter(config, plugin);
|
||||||
this.maskBlockKeys = initMaskBlockKeys(config);
|
this.maskBlockKeys = initMaskBlockKeys(config);
|
||||||
this.totalVolume = config.blocks().fillMode() == FillMode.MASK
|
this.state = new MineState(config);
|
||||||
? maskBlockKeys.size()
|
|
||||||
: config.region().totalVolume();
|
|
||||||
this.blocks = new AtomicInteger(Math.max(0, totalVolume));
|
|
||||||
this.ticks = new AtomicInteger(0);
|
|
||||||
this.resetHandler = new MineResetHandler(this, plugin);
|
this.resetHandler = new MineResetHandler(this, plugin);
|
||||||
this.blockHandler = new MineBlockHandler(this, plugin);
|
this.blockHandler = new MineBlockHandler(this, plugin);
|
||||||
this.actionBarHandler = new ActionBarHandler(this);
|
this.actionBarHandler = new ActionBarHandler(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> initMaskBlockKeys(MineConfig config) {
|
private Set<String> initMaskBlockKeys(MineConfig config) {
|
||||||
if (config.blocks().fillMode() != FillMode.MASK) {
|
if (config.blocks().fillMode() != FillMode.MASK || config.blocks().mask() == null) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
Set<String> keys = new HashSet<>();
|
return config.blocks().mask().positions().keySet().stream()
|
||||||
if (config.blocks().mask() != null) {
|
.map(LocationParser::parseSimple)
|
||||||
for (String pos : config.blocks().mask().positions().keySet()) {
|
.filter(Objects::nonNull)
|
||||||
Location loc = LocationParser.parseSimple(pos);
|
.map(BlockKeys::key)
|
||||||
if (loc != null) {
|
.collect(Collectors.toSet());
|
||||||
keys.add(BlockKeys.key(loc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return keys;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
|
|
@ -99,18 +85,15 @@ public final class Mine {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean appliesToBlock(Location location) {
|
public boolean appliesToBlock(Location location) {
|
||||||
if (!contains(location)) return false;
|
return contains(location) && (config.blocks().fillMode() != FillMode.MASK || maskBlockKeys.contains(BlockKeys.key(location)));
|
||||||
if (config.blocks().fillMode() != FillMode.MASK) return true;
|
|
||||||
return maskBlockKeys.contains(BlockKeys.key(location));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTotalVolume() {
|
public int getTotalVolume() {
|
||||||
return totalVolume;
|
return state.getTotalVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getPercentFilled() {
|
public double getPercentFilled() {
|
||||||
if (totalVolume == 0) return 0.0;
|
return state.getPercentFilled();
|
||||||
return (double) blocks.get() / totalVolume * 100.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlockSetter createBlockSetter(MineConfig config, LoMinesPlugin plugin) {
|
private BlockSetter createBlockSetter(MineConfig config, LoMinesPlugin plugin) {
|
||||||
|
|
@ -120,17 +103,16 @@ public final class Mine {
|
||||||
}
|
}
|
||||||
BlockKey firstKey = weights.keySet().iterator().next();
|
BlockKey firstKey = weights.keySet().iterator().next();
|
||||||
return switch (firstKey) {
|
return switch (firstKey) {
|
||||||
case BlockKey.Oraxen oraxen -> throw new IllegalArgumentException("Oraxen integration is currently disabled");
|
case BlockKey.Oraxen oraxen -> throw new IllegalArgumentException("External plugins disabled");
|
||||||
case BlockKey.ItemsAdder itemsAdder -> throw new IllegalArgumentException("ItemsAdder integration is currently disabled");
|
case BlockKey.ItemsAdder itemsAdder -> throw new IllegalArgumentException("External plugins disabled");
|
||||||
case BlockKey.Vanilla vanilla -> new dev.loki.lomines.block.BukkitBlockSetter(weights, plugin);
|
case BlockKey.Vanilla vanilla -> new dev.loki.lomines.block.BukkitBlockSetter(weights, plugin);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startActionBarTask() {
|
private void startActionBarTask() {
|
||||||
if (!config.ui().actionBarEnabled()) return;
|
if (config.ui().actionBarEnabled()) {
|
||||||
actionBarTask = Scheduler.get(plugin).runTimer(() -> {
|
actionBarTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> actionBarHandler.sendToNearbyPlayers(), 10L, 10L);
|
||||||
actionBarHandler.sendToNearbyPlayers();
|
}
|
||||||
}, 10L, 10L);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
|
@ -149,19 +131,19 @@ public final class Mine {
|
||||||
return regions;
|
return regions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AtomicInteger getBlocksAtomic() {
|
|
||||||
return blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBlocks() {
|
public int getBlocks() {
|
||||||
return blocks.get();
|
return state.getBlocks();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTicks() {
|
public int getTicks() {
|
||||||
return ticks.get();
|
return state.getTicks();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AtomicInteger getBlocksAtomic() {
|
||||||
|
return state.getBlocksAtomic();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AtomicInteger getTicksAtomic() {
|
public AtomicInteger getTicksAtomic() {
|
||||||
return ticks;
|
return state.getTicksAtomic();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
package dev.loki.lomines.core.mine.model;
|
||||||
|
|
||||||
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
|
import dev.loki.lomines.data.config.block.FillMode;
|
||||||
|
import dev.loki.lomines.util.location.BlockKeys;
|
||||||
|
import dev.loki.lomines.util.location.LocationParser;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public final class MineState {
|
||||||
|
|
||||||
|
private final AtomicInteger blocks;
|
||||||
|
private final AtomicInteger ticks;
|
||||||
|
private final int totalVolume;
|
||||||
|
private final Set<String> maskBlockKeys;
|
||||||
|
|
||||||
|
public MineState(MineConfig config) {
|
||||||
|
this.maskBlockKeys = initMaskBlockKeys(config);
|
||||||
|
this.totalVolume = config.blocks().fillMode() == FillMode.MASK
|
||||||
|
? maskBlockKeys.size()
|
||||||
|
: config.region().totalVolume();
|
||||||
|
this.blocks = new AtomicInteger(Math.max(0, totalVolume));
|
||||||
|
this.ticks = new AtomicInteger(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> initMaskBlockKeys(MineConfig config) {
|
||||||
|
if (config.blocks().fillMode() != FillMode.MASK) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
Set<String> keys = new HashSet<>();
|
||||||
|
if (config.blocks().mask() != null) {
|
||||||
|
for (String pos : config.blocks().mask().positions().keySet()) {
|
||||||
|
Location loc = LocationParser.parseSimple(pos);
|
||||||
|
if (loc != null) {
|
||||||
|
keys.add(BlockKeys.key(loc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalVolume() {
|
||||||
|
return totalVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getPercentFilled() {
|
||||||
|
if (totalVolume == 0) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
return (double) blocks.get() / totalVolume * 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AtomicInteger getBlocksAtomic() {
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBlocks() {
|
||||||
|
return blocks.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTicks() {
|
||||||
|
return ticks.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AtomicInteger getTicksAtomic() {
|
||||||
|
return ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getMaskBlockKeys() {
|
||||||
|
return maskBlockKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean appliesToBlock(Location location) {
|
||||||
|
if (maskBlockKeys.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return maskBlockKeys.contains(BlockKeys.key(location));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package dev.loki.lomines.core.mine.registry;
|
||||||
|
|
||||||
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
|
import dev.loki.lomines.core.service.MineRepository;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public final class MineFinder {
|
||||||
|
|
||||||
|
private final MineRepository repository;
|
||||||
|
|
||||||
|
public MineFinder(MineRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mine get(String name) {
|
||||||
|
return repository.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Mine> find(String name) {
|
||||||
|
return repository.find(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Mine> getAll() {
|
||||||
|
return repository.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Mine> findByLocation(Location location) {
|
||||||
|
return repository.findByLocation(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,23 +1,20 @@
|
||||||
package dev.loki.lomines.core.mine;
|
package dev.loki.lomines.core.mine.registry;
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.core.common.MineLoader;
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
import dev.loki.lomines.core.service.MaskScanService;
|
import dev.loki.lomines.core.mine.service.MineLoader;
|
||||||
|
import dev.loki.lomines.core.service.mask.MaskScanService;
|
||||||
import dev.loki.lomines.core.service.MineFileManager;
|
import dev.loki.lomines.core.service.MineFileManager;
|
||||||
import dev.loki.lomines.core.service.MineRepository;
|
import dev.loki.lomines.core.service.MineRepository;
|
||||||
import dev.loki.lomines.data.config.loader.common.ConfigLoader;
|
import dev.loki.lomines.data.config.ConfigLoader;
|
||||||
import dev.loki.lomines.data.config.MineConfig;
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
import dev.loki.lomines.integration.worldguard.WorldGuardRegionService;
|
import dev.loki.lomines.integration.worldguard.region.WorldGuardRegionService;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
|
||||||
* Registry for all active mines.
|
|
||||||
* Manages the lifecycle of mines including loading, creating, and deleting.
|
|
||||||
*/
|
|
||||||
public final class Mines {
|
public final class Mines {
|
||||||
|
|
||||||
private final LoMinesPlugin plugin;
|
private final LoMinesPlugin plugin;
|
||||||
|
|
@ -26,6 +23,7 @@ public final class Mines {
|
||||||
private final MineLoader loader;
|
private final MineLoader loader;
|
||||||
private final MaskScanService maskScanService;
|
private final MaskScanService maskScanService;
|
||||||
private final WorldGuardRegionService worldGuardService;
|
private final WorldGuardRegionService worldGuardService;
|
||||||
|
private final MineFinder finder;
|
||||||
|
|
||||||
public Mines(LoMinesPlugin plugin) {
|
public Mines(LoMinesPlugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
@ -34,6 +32,7 @@ public final class Mines {
|
||||||
this.loader = new MineLoader(plugin, fileManager, repository);
|
this.loader = new MineLoader(plugin, fileManager, repository);
|
||||||
this.maskScanService = new MaskScanService(repository, fileManager);
|
this.maskScanService = new MaskScanService(repository, fileManager);
|
||||||
this.worldGuardService = new WorldGuardRegionService(plugin);
|
this.worldGuardService = new WorldGuardRegionService(plugin);
|
||||||
|
this.finder = new MineFinder(repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadAll() throws IOException {
|
public void loadAll() throws IOException {
|
||||||
|
|
@ -45,6 +44,7 @@ public final class Mines {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void create(String name, Location corner1, Location corner2) throws IOException {
|
public void create(String name, Location corner1, Location corner2) throws IOException {
|
||||||
|
name = name.toLowerCase().trim();
|
||||||
if (repository.exists(name)) {
|
if (repository.exists(name)) {
|
||||||
throw new IllegalArgumentException("Mine already exists: " + name);
|
throw new IllegalArgumentException("Mine already exists: " + name);
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +64,7 @@ public final class Mines {
|
||||||
|
|
||||||
worldGuardService.createRegion(name, config);
|
worldGuardService.createRegion(name, config);
|
||||||
repository.createAndStart(name, config);
|
repository.createAndStart(name, config);
|
||||||
plugin.loLogger().info("Created mine: " + name);
|
plugin.getLogger().info("Created mine: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete(String name) throws IOException {
|
public void delete(String name) throws IOException {
|
||||||
|
|
@ -79,39 +79,65 @@ public final class Mines {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mine get(String name) {
|
public Mine get(String name) {
|
||||||
return repository.get(name);
|
return finder.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Mine> find(String name) {
|
public Optional<Mine> find(String name) {
|
||||||
return repository.find(name);
|
return finder.find(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Mine> getAll() {
|
public Collection<Mine> getAll() {
|
||||||
return repository.getAll();
|
return finder.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Mine> findByLocation(Location location) {
|
public Optional<Mine> findByLocation(Location location) {
|
||||||
return repository.findByLocation(location);
|
return finder.findByLocation(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int scanAndSaveMask(String mineName) throws IOException, ConfigLoader.ConfigLoadException {
|
public int scanAndSaveMask(String mineName) throws IOException, ConfigLoader.ConfigLoadException {
|
||||||
int count = maskScanService.scanAndSave(mineName);
|
int count = maskScanService.scanAndSave(mineName);
|
||||||
plugin.loLogger().info("Mask scan for '" + mineName + "': " + count + " marker block(s)");
|
plugin.getLogger().info("Mask scan for '" + mineName + "': " + count + " marker block(s)");
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reloadMine(String name) throws IOException, ConfigLoader.ConfigLoadException {
|
public void reloadMine(String name) throws IOException, ConfigLoader.ConfigLoadException {
|
||||||
Mine oldMine = repository.find(name).orElse(null);
|
Mine oldMine = finder.find(name).orElse(null);
|
||||||
repository.reload(name);
|
repository.reload(name);
|
||||||
|
|
||||||
if (oldMine != null) {
|
if (oldMine != null) {
|
||||||
Mine newMine = repository.find(name).orElse(null);
|
Mine newMine = finder.find(name).orElse(null);
|
||||||
if (newMine != null) {
|
if (newMine != null) {
|
||||||
worldGuardService.updateRegion(name, newMine.getConfig());
|
worldGuardService.updateRegion(name, newMine.getConfig());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateMineConfig(String name, MineConfig newConfig) throws IOException {
|
||||||
|
if (!repository.exists(name)) {
|
||||||
|
throw new IllegalArgumentException("Mine not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
fileManager.saveConfig(name, newConfig);
|
||||||
|
} catch (ConfigLoader.ConfigLoadException e) {
|
||||||
|
throw new IOException("Failed to save mine config: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Mine oldMine = finder.find(name).orElse(null);
|
||||||
|
repository.reload(name);
|
||||||
|
|
||||||
|
if (oldMine != null) {
|
||||||
|
Mine newMine = finder.find(name).orElse(null);
|
||||||
|
if (newMine != null) {
|
||||||
|
worldGuardService.updateRegion(name, newMine.getConfig());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ConfigLoader.ConfigLoadException e) {
|
||||||
|
throw new IOException("Failed to reload mine after config update: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public WorldGuardRegionService getWorldGuardService() {
|
public WorldGuardRegionService getWorldGuardService() {
|
||||||
return worldGuardService;
|
return worldGuardService;
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package dev.loki.lomines.core.common;
|
package dev.loki.lomines.core.mine.service;
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.core.mine.Mine;
|
|
||||||
import dev.loki.lomines.core.service.MineFileManager;
|
import dev.loki.lomines.core.service.MineFileManager;
|
||||||
import dev.loki.lomines.data.config.loader.common.ConfigLoader;
|
import dev.loki.lomines.core.service.MineRepository;
|
||||||
import dev.loki.lomines.data.config.MineConfig;
|
import dev.loki.lomines.data.config.ConfigLoader;
|
||||||
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
|
@ -14,19 +14,19 @@ import java.util.stream.Stream;
|
||||||
/**
|
/**
|
||||||
* Loads mine configurations from disk.
|
* Loads mine configurations from disk.
|
||||||
*/
|
*/
|
||||||
final class MineLoader {
|
public final class MineLoader {
|
||||||
|
|
||||||
private final LoMinesPlugin plugin;
|
private final LoMinesPlugin plugin;
|
||||||
private final MineFileManager fileManager;
|
private final MineFileManager fileManager;
|
||||||
private final MineRepository repository;
|
private final MineRepository repository;
|
||||||
|
|
||||||
MineLoader(LoMinesPlugin plugin, MineFileManager fileManager, MineRepository repository) {
|
public MineLoader(LoMinesPlugin plugin, MineFileManager fileManager, MineRepository repository) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.fileManager = fileManager;
|
this.fileManager = fileManager;
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadAll() throws IOException {
|
public void loadAll() throws IOException {
|
||||||
repository.clear();
|
repository.clear();
|
||||||
|
|
||||||
Path minesFolder = fileManager.getMinesFolder();
|
Path minesFolder = fileManager.getMinesFolder();
|
||||||
|
|
@ -49,9 +49,9 @@ final class MineLoader {
|
||||||
MineConfig config = fileManager.loadConfig(mineName);
|
MineConfig config = fileManager.loadConfig(mineName);
|
||||||
repository.createAndStart(mineName, config);
|
repository.createAndStart(mineName, config);
|
||||||
|
|
||||||
plugin.loLogger().info("Loaded mine: " + mineName);
|
plugin.getLogger().info("Loaded mine: " + mineName);
|
||||||
} catch (IOException | ConfigLoader.ConfigLoadException e) {
|
} catch (IOException | ConfigLoader.ConfigLoadException e) {
|
||||||
plugin.loLogger().error("Failed to load mine from " + configFile.getFileName() + ": " + e.getMessage());
|
plugin.getLogger().severe("Failed to load mine from " + configFile.getFileName() + ": " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package dev.loki.lomines.core.mine;
|
package dev.loki.lomines.core.mine.service;
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.lolib.scheduler.ScheduledTask;
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
import dev.lolib.scheduler.Scheduler;
|
import dev.loki.lomines.core.mine.registry.Mines;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Centralized ticker for all mines.
|
* Centralized ticker for all mines.
|
||||||
|
|
@ -13,7 +15,7 @@ public final class MineTicker {
|
||||||
|
|
||||||
private final Mines mines;
|
private final Mines mines;
|
||||||
private final LoMinesPlugin plugin;
|
private final LoMinesPlugin plugin;
|
||||||
private ScheduledTask tickerTask;
|
private BukkitTask tickerTask;
|
||||||
|
|
||||||
public MineTicker(Mines mines, LoMinesPlugin plugin) {
|
public MineTicker(Mines mines, LoMinesPlugin plugin) {
|
||||||
this.mines = mines;
|
this.mines = mines;
|
||||||
|
|
@ -29,7 +31,7 @@ public final class MineTicker {
|
||||||
return; // Already running
|
return; // Already running
|
||||||
}
|
}
|
||||||
|
|
||||||
tickerTask = Scheduler.get(plugin).runTimer(() -> {
|
tickerTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
|
||||||
for (Mine mine : mines.getAll()) {
|
for (Mine mine : mines.getAll()) {
|
||||||
tickMine(mine);
|
tickMine(mine);
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +54,7 @@ public final class MineTicker {
|
||||||
private void tickMine(Mine mine) {
|
private void tickMine(Mine mine) {
|
||||||
int currentTicks = mine.getTicksAtomic().incrementAndGet();
|
int currentTicks = mine.getTicksAtomic().incrementAndGet();
|
||||||
|
|
||||||
if (currentTicks >= mine.getConfig().getResetTicks()) {
|
if (currentTicks >= mine.getConfig().reset().intervalTicks()) {
|
||||||
mine.getTicksAtomic().set(0);
|
mine.getTicksAtomic().set(0);
|
||||||
mine.reset(false);
|
mine.reset(false);
|
||||||
}
|
}
|
||||||
|
|
@ -1,38 +1,25 @@
|
||||||
package dev.loki.lomines.core.service;
|
package dev.loki.lomines.core.service;
|
||||||
|
|
||||||
import dev.loki.lomines.data.config.loader.common.ConfigLoader;
|
import dev.loki.lomines.core.service.write.MineConfigWriter;
|
||||||
import dev.loki.lomines.data.config.MineConfig;
|
import dev.loki.lomines.data.config.ConfigLoader;
|
||||||
import dev.loki.lomines.data.config.block.BlockConfig;
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
import dev.loki.lomines.data.config.block.BlockKey;
|
import dev.loki.lomines.data.config.block.BlockKey;
|
||||||
import dev.loki.lomines.data.config.block.FillMode;
|
|
||||||
import dev.loki.lomines.data.config.region.RegionConfig;
|
|
||||||
import dev.loki.lomines.data.config.reset.ResetConfig;
|
|
||||||
import dev.loki.lomines.data.config.reward.RewardConfig;
|
|
||||||
import dev.loki.lomines.data.config.spawn.PlayerSpawnConfig;
|
|
||||||
import dev.loki.lomines.data.config.teleport.TeleportConfig;
|
|
||||||
import dev.loki.lomines.data.config.ui.UIConfig;
|
|
||||||
import dev.loki.lomines.integration.worldguard.WorldGuardConfig;
|
|
||||||
import dev.loki.lomines.util.location.Cuboid;
|
|
||||||
import dev.loki.lomines.util.location.LocationParser;
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
public final class MineFileManager {
|
||||||
* Handles file operations for mine configurations.
|
|
||||||
* Updated for new section-based configuration (v2).
|
private final Path minesFolder;
|
||||||
*/
|
private final ConfigLoader configLoader;
|
||||||
public record MineFileManager(Path minesFolder, ConfigLoader configLoader) {
|
private final MineConfigWriter configWriter;
|
||||||
|
|
||||||
public MineFileManager(Path minesFolder) {
|
public MineFileManager(Path minesFolder) {
|
||||||
this(minesFolder, new ConfigLoader(minesFolder.getParent()));
|
this.minesFolder = minesFolder;
|
||||||
|
this.configLoader = new ConfigLoader(minesFolder.getParent());
|
||||||
|
this.configWriter = new MineConfigWriter(minesFolder, configLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getMinesFolder() {
|
public Path getMinesFolder() {
|
||||||
|
|
@ -45,82 +32,9 @@ public record MineFileManager(Path minesFolder, ConfigLoader configLoader) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a default mine configuration with new format.
|
|
||||||
* WorldGuard region settings are loaded from defaults.yml.
|
|
||||||
*/
|
|
||||||
public void createDefaultConfig(String name, Location corner1, Location corner2) throws IOException {
|
public void createDefaultConfig(String name, Location corner1, Location corner2) throws IOException {
|
||||||
ensureFolderExists();
|
ensureFolderExists();
|
||||||
|
configWriter.createDefaultConfig(name, corner1, corner2);
|
||||||
// Create default region
|
|
||||||
Location loc1 = corner1 != null ? corner1 : new Location(
|
|
||||||
org.bukkit.Bukkit.getWorlds().get(0), 0, 64, 0);
|
|
||||||
Location loc2 = corner2 != null ? corner2 : new Location(
|
|
||||||
org.bukkit.Bukkit.getWorlds().get(0), 10, 74, 10);
|
|
||||||
|
|
||||||
Cuboid cuboid = new Cuboid(loc1, loc2);
|
|
||||||
RegionConfig region = RegionConfig.single(cuboid);
|
|
||||||
|
|
||||||
// Create default blocks (stone only)
|
|
||||||
Map<BlockKey, Double> weights = new HashMap<>();
|
|
||||||
weights.put(new BlockKey.Vanilla(Material.STONE), 1.0);
|
|
||||||
BlockConfig blocks = new BlockConfig(weights, FillMode.CUBOID, null);
|
|
||||||
|
|
||||||
// Load WorldGuard defaults from _defaults.yml
|
|
||||||
WorldGuardConfig wgConfig = loadWorldGuardDefaults();
|
|
||||||
|
|
||||||
// Build mine config with defaults
|
|
||||||
MineConfig config = MineConfig.builder(name)
|
|
||||||
.region(region)
|
|
||||||
.blocks(blocks)
|
|
||||||
.reset(ResetConfig.defaults())
|
|
||||||
.rewards(RewardConfig.empty())
|
|
||||||
.teleport(TeleportConfig.disabled())
|
|
||||||
.ui(UIConfig.defaults())
|
|
||||||
.worldGuard(wgConfig)
|
|
||||||
.playerSpawn(PlayerSpawnConfig.disabled())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// Save using new loader
|
|
||||||
configLoader.save(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads WorldGuard configuration from defaults.yml.
|
|
||||||
*/
|
|
||||||
private WorldGuardConfig loadWorldGuardDefaults() {
|
|
||||||
Path defaultsPath = minesFolder.resolve("_defaults.yml");
|
|
||||||
if (!Files.exists(defaultsPath)) {
|
|
||||||
return WorldGuardConfig.disabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(defaultsPath.toFile());
|
|
||||||
ConfigurationSection wgSection = yaml.getConfigurationSection("worldguard");
|
|
||||||
|
|
||||||
if (wgSection == null) {
|
|
||||||
return WorldGuardConfig.disabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean enabled = wgSection.getBoolean("enabled", false);
|
|
||||||
if (!enabled) {
|
|
||||||
return WorldGuardConfig.disabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
String template = wgSection.getString("region-template", "{mine_name}_{random_4}");
|
|
||||||
|
|
||||||
return WorldGuardConfig.builder()
|
|
||||||
.enabled(true)
|
|
||||||
.template(template)
|
|
||||||
.owners(wgSection.getStringList("owners"))
|
|
||||||
.members(wgSection.getStringList("members"))
|
|
||||||
.flags(wgSection.getStringList("flags"))
|
|
||||||
.protectOnCreate(wgSection.getBoolean("protect-on-create", true))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
return WorldGuardConfig.disabled();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MineConfig loadConfig(String name) throws IOException, ConfigLoader.ConfigLoadException {
|
public MineConfig loadConfig(String name) throws IOException, ConfigLoader.ConfigLoadException {
|
||||||
|
|
@ -134,48 +48,16 @@ public record MineFileManager(Path minesFolder, ConfigLoader configLoader) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves a mine configuration to file.
|
|
||||||
*
|
|
||||||
* @param config the configuration to save
|
|
||||||
* @throws ConfigLoader.ConfigLoadException if saving fails
|
|
||||||
*/
|
|
||||||
public void saveConfig(MineConfig config) throws ConfigLoader.ConfigLoadException {
|
public void saveConfig(MineConfig config) throws ConfigLoader.ConfigLoadException {
|
||||||
configLoader.save(config);
|
configLoader.save(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void saveConfig(String name, MineConfig config) throws ConfigLoader.ConfigLoadException {
|
||||||
* Saves mask positions for mask fill mode.
|
configLoader.save(config);
|
||||||
*/
|
|
||||||
public void saveMaskPositions(String name, BlockKey markerMaterial, java.util.List<Location> positions) throws IOException, ConfigLoader.ConfigLoadException {
|
|
||||||
MineConfig config = loadConfig(name);
|
|
||||||
|
|
||||||
// Build new mask config
|
|
||||||
Map<String, Boolean> posMap = new HashMap<>();
|
|
||||||
for (Location loc : positions) {
|
|
||||||
posMap.put(LocationParser.format(loc), true);
|
|
||||||
}
|
}
|
||||||
BlockConfig.MaskConfig mask = new BlockConfig.MaskConfig(markerMaterial, posMap);
|
|
||||||
|
|
||||||
// Create new block config with mask
|
public void saveMaskPositions(String name, BlockKey markerMaterial,
|
||||||
BlockConfig newBlocks = new BlockConfig(
|
java.util.List<Location> positions) throws IOException, ConfigLoader.ConfigLoadException {
|
||||||
config.blocks().weights(),
|
configWriter.saveMaskPositions(name, markerMaterial, positions);
|
||||||
FillMode.MASK,
|
|
||||||
mask
|
|
||||||
);
|
|
||||||
|
|
||||||
// Build updated config
|
|
||||||
MineConfig updated = MineConfig.builder(name)
|
|
||||||
.region(config.region())
|
|
||||||
.blocks(newBlocks)
|
|
||||||
.reset(config.reset())
|
|
||||||
.rewards(config.rewards())
|
|
||||||
.teleport(config.teleport())
|
|
||||||
.ui(config.ui())
|
|
||||||
.worldGuard(config.worldGuard())
|
|
||||||
.playerSpawn(config.playerSpawn())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
configLoader.save(updated);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
package dev.loki.lomines.core.service;
|
package dev.loki.lomines.core.service;
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.core.mine.Mine;
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
import dev.loki.lomines.data.config.loader.common.ConfigLoader;
|
import dev.loki.lomines.data.config.ConfigLoader;
|
||||||
import dev.loki.lomines.data.config.MineConfig;
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -84,6 +88,6 @@ public final class MineRepository {
|
||||||
stopAndRemove(name);
|
stopAndRemove(name);
|
||||||
MineConfig config = fileManager.loadConfig(name);
|
MineConfig config = fileManager.loadConfig(name);
|
||||||
Mine mine = createAndStart(name, config);
|
Mine mine = createAndStart(name, config);
|
||||||
plugin.loLogger().info("Reloaded mine: " + name);
|
plugin.getLogger().info("Reloaded mine: " + name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
package dev.loki.lomines.core.service;
|
package dev.loki.lomines.core.service.mask;
|
||||||
|
|
||||||
import dev.loki.lomines.core.mine.Mine;
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
import dev.loki.lomines.data.config.loader.common.ConfigLoader;
|
import dev.loki.lomines.data.config.ConfigLoader;
|
||||||
|
import dev.loki.lomines.core.service.MineFileManager;
|
||||||
|
import dev.loki.lomines.core.service.MineRepository;
|
||||||
import dev.loki.lomines.data.config.block.BlockKey;
|
import dev.loki.lomines.data.config.block.BlockKey;
|
||||||
import dev.loki.lomines.util.ValidationUtils;
|
import dev.loki.lomines.util.ValidationUtils;
|
||||||
import dev.loki.lomines.util.location.Cuboid;
|
import dev.loki.lomines.util.location.geo.Cuboid;
|
||||||
import dev.loki.lomines.util.selection.MaskScanner;
|
import dev.loki.lomines.util.selection.MaskScanner;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
|
@ -57,8 +59,8 @@ public final class MaskScanService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Material parseMarkerMaterial(BlockKey key) {
|
private Material parseMarkerMaterial(BlockKey key) {
|
||||||
if (key instanceof BlockKey.Vanilla vanilla) {
|
if (key instanceof BlockKey.Vanilla(Material material)) {
|
||||||
return vanilla.material();
|
return material;
|
||||||
}
|
}
|
||||||
// For custom blocks (Oraxen/ItemsAdder), we need to get the underlying material
|
// For custom blocks (Oraxen/ItemsAdder), we need to get the underlying material
|
||||||
// This would need integration with those plugins to get the actual material
|
// This would need integration with those plugins to get the actual material
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
package dev.loki.lomines.core.service.write;
|
||||||
|
|
||||||
|
import dev.loki.lomines.data.config.ConfigLoader;
|
||||||
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
|
import dev.loki.lomines.data.config.block.BlockConfig;
|
||||||
|
import dev.loki.lomines.data.config.block.BlockKey;
|
||||||
|
import dev.loki.lomines.data.config.block.FillMode;
|
||||||
|
import dev.loki.lomines.data.config.region.RegionConfig;
|
||||||
|
import dev.loki.lomines.data.config.reset.ResetConfig;
|
||||||
|
import dev.loki.lomines.data.config.reward.RewardConfig;
|
||||||
|
import dev.loki.lomines.data.config.spawn.PlayerSpawnConfig;
|
||||||
|
import dev.loki.lomines.data.config.teleport.TeleportConfig;
|
||||||
|
import dev.loki.lomines.data.config.ui.UIConfig;
|
||||||
|
import dev.loki.lomines.integration.worldguard.config.WorldGuardConfig;
|
||||||
|
import dev.loki.lomines.util.location.geo.Cuboid;
|
||||||
|
import dev.loki.lomines.util.location.LocationParser;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class MineConfigWriter {
|
||||||
|
|
||||||
|
private final Path minesFolder;
|
||||||
|
private final ConfigLoader configLoader;
|
||||||
|
|
||||||
|
public MineConfigWriter(Path minesFolder, ConfigLoader configLoader) {
|
||||||
|
this.minesFolder = minesFolder;
|
||||||
|
this.configLoader = configLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createDefaultConfig(String name, Location corner1, Location corner2) throws IOException {
|
||||||
|
Location loc1 = corner1 != null ? corner1 : new Location(
|
||||||
|
org.bukkit.Bukkit.getWorlds().get(0), 0, 64, 0);
|
||||||
|
Location loc2 = corner2 != null ? corner2 : new Location(
|
||||||
|
org.bukkit.Bukkit.getWorlds().get(0), 10, 74, 10);
|
||||||
|
|
||||||
|
Cuboid cuboid = new Cuboid(loc1, loc2);
|
||||||
|
RegionConfig region = RegionConfig.single(cuboid);
|
||||||
|
|
||||||
|
Map<BlockKey, Double> weights = new HashMap<>();
|
||||||
|
weights.put(new BlockKey.Vanilla(Material.STONE), 1.0);
|
||||||
|
BlockConfig blocks = new BlockConfig(weights, FillMode.CUBOID, null);
|
||||||
|
|
||||||
|
WorldGuardConfig wgConfig = loadWorldGuardDefaults();
|
||||||
|
|
||||||
|
MineConfig config = MineConfig.builder(name)
|
||||||
|
.region(region)
|
||||||
|
.blocks(blocks)
|
||||||
|
.reset(ResetConfig.defaults())
|
||||||
|
.rewards(RewardConfig.empty())
|
||||||
|
.teleport(TeleportConfig.disabled())
|
||||||
|
.ui(UIConfig.defaults())
|
||||||
|
.worldGuard(wgConfig)
|
||||||
|
.playerSpawn(PlayerSpawnConfig.disabled())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
configLoader.save(config);
|
||||||
|
} catch (ConfigLoader.ConfigLoadException e) {
|
||||||
|
throw new IOException("Failed to save default mine config: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveMaskPositions(String name, BlockKey markerMaterial,
|
||||||
|
java.util.List<Location> positions) throws IOException, ConfigLoader.ConfigLoadException {
|
||||||
|
MineConfig config = configLoader.load(name);
|
||||||
|
|
||||||
|
Map<String, Boolean> posMap = new HashMap<>();
|
||||||
|
for (Location loc : positions) {
|
||||||
|
posMap.put(LocationParser.format(loc), true);
|
||||||
|
}
|
||||||
|
BlockConfig.MaskConfig mask = new BlockConfig.MaskConfig(markerMaterial, posMap);
|
||||||
|
|
||||||
|
BlockConfig newBlocks = new BlockConfig(
|
||||||
|
config.blocks().weights(),
|
||||||
|
FillMode.MASK,
|
||||||
|
mask
|
||||||
|
);
|
||||||
|
|
||||||
|
MineConfig updated = MineConfig.builder(name)
|
||||||
|
.region(config.region())
|
||||||
|
.blocks(newBlocks)
|
||||||
|
.reset(config.reset())
|
||||||
|
.rewards(config.rewards())
|
||||||
|
.teleport(config.teleport())
|
||||||
|
.ui(config.ui())
|
||||||
|
.worldGuard(config.worldGuard())
|
||||||
|
.playerSpawn(config.playerSpawn())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
configLoader.save(updated);
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldGuardConfig loadWorldGuardDefaults() {
|
||||||
|
Path defaultsPath = minesFolder.resolve("_defaults.yml");
|
||||||
|
if (!Files.exists(defaultsPath)) {
|
||||||
|
return WorldGuardConfig.disabled();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(defaultsPath.toFile());
|
||||||
|
ConfigurationSection wgSection = yaml.getConfigurationSection("worldguard");
|
||||||
|
if (wgSection == null) {
|
||||||
|
return WorldGuardConfig.disabled();
|
||||||
|
}
|
||||||
|
boolean enabled = wgSection.getBoolean("enabled", false);
|
||||||
|
if (!enabled) {
|
||||||
|
return WorldGuardConfig.disabled();
|
||||||
|
}
|
||||||
|
String template = wgSection.getString("region-template", "{mine_name}_{random_4}");
|
||||||
|
return WorldGuardConfig.builder()
|
||||||
|
.enabled(true)
|
||||||
|
.template(template)
|
||||||
|
.owners(wgSection.getStringList("owners"))
|
||||||
|
.members(wgSection.getStringList("members"))
|
||||||
|
.flags(wgSection.getStringList("flags"))
|
||||||
|
.protectOnCreate(wgSection.getBoolean("protect-on-create", true))
|
||||||
|
.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return WorldGuardConfig.disabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,27 +5,21 @@ import dev.loki.lomines.data.config.loader.entity.PlayerSpawnConfigLoader;
|
||||||
import dev.loki.lomines.data.config.loader.entity.TeleportConfigLoader;
|
import dev.loki.lomines.data.config.loader.entity.TeleportConfigLoader;
|
||||||
import dev.loki.lomines.data.config.loader.region.RegionConfigLoader;
|
import dev.loki.lomines.data.config.loader.region.RegionConfigLoader;
|
||||||
import dev.loki.lomines.data.config.loader.region.WorldGuardConfigLoader;
|
import dev.loki.lomines.data.config.loader.region.WorldGuardConfigLoader;
|
||||||
import dev.loki.lomines.data.config.loader.reset.ResetConfigLoader;
|
|
||||||
import dev.loki.lomines.data.config.loader.reward.RewardConfigLoader;
|
import dev.loki.lomines.data.config.loader.reward.RewardConfigLoader;
|
||||||
|
import dev.loki.lomines.data.config.loader.system.ResetConfigLoader;
|
||||||
import dev.loki.lomines.data.config.loader.system.UIConfigLoader;
|
import dev.loki.lomines.data.config.loader.system.UIConfigLoader;
|
||||||
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clean config loader for section-based configuration.
|
|
||||||
* Supports YAML inheritance from defaults.
|
|
||||||
* Delegates to specialized loaders for each section.
|
|
||||||
*/
|
|
||||||
public final class ConfigLoader {
|
public final class ConfigLoader {
|
||||||
|
|
||||||
private final Path dataFolder;
|
private final Path dataFolder;
|
||||||
private YamlConfiguration defaults;
|
private final DefaultsMerger defaultsMerger;
|
||||||
|
|
||||||
// Section loaders
|
|
||||||
private RegionConfigLoader regionLoader;
|
private RegionConfigLoader regionLoader;
|
||||||
private BlockConfigLoader blockLoader;
|
private BlockConfigLoader blockLoader;
|
||||||
private ResetConfigLoader resetLoader;
|
private ResetConfigLoader resetLoader;
|
||||||
|
|
@ -37,69 +31,29 @@ public final class ConfigLoader {
|
||||||
|
|
||||||
public ConfigLoader(Path dataFolder) {
|
public ConfigLoader(Path dataFolder) {
|
||||||
this.dataFolder = dataFolder;
|
this.dataFolder = dataFolder;
|
||||||
loadDefaults();
|
this.defaultsMerger = new DefaultsMerger(dataFolder);
|
||||||
initLoaders();
|
initLoaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadDefaults() {
|
|
||||||
Path defaultsPath = dataFolder.resolve("mines").resolve("_defaults.yml");
|
|
||||||
defaults = new YamlConfiguration();
|
|
||||||
|
|
||||||
if (Files.exists(defaultsPath)) {
|
|
||||||
defaults = YamlConfiguration.loadConfiguration(defaultsPath.toFile());
|
|
||||||
} else {
|
|
||||||
setDefaults(defaults);
|
|
||||||
try {
|
|
||||||
Files.createDirectories(defaultsPath.getParent());
|
|
||||||
defaults.save(defaultsPath.toFile());
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Ignore, use in-memory defaults
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setDefaults(YamlConfiguration yaml) {
|
|
||||||
yaml.set("reset.interval", "5m");
|
|
||||||
yaml.set("reset.percent-trigger", 10.0);
|
|
||||||
yaml.set("reset.percent-enabled", false);
|
|
||||||
yaml.set("reset.commands", List.of());
|
|
||||||
yaml.set("reset.broadcast", "");
|
|
||||||
|
|
||||||
yaml.set("ui.actionbar.enabled", true);
|
|
||||||
yaml.set("ui.actionbar.format", "<green>{mine}</green> <gray>{percent}%</gray> <dark_gray>({time})");
|
|
||||||
yaml.set("ui.actionbar.range", 50.0);
|
|
||||||
yaml.set("ui.timer-format", "mm:ss");
|
|
||||||
|
|
||||||
yaml.set("teleport.enabled", false);
|
|
||||||
yaml.set("rewards", List.of());
|
|
||||||
|
|
||||||
worldGuardLoader.setDefaults(yaml);
|
|
||||||
playerSpawnLoader.setDefaults(yaml);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initLoaders() {
|
private void initLoaders() {
|
||||||
regionLoader = new RegionConfigLoader();
|
regionLoader = new RegionConfigLoader();
|
||||||
blockLoader = new BlockConfigLoader();
|
blockLoader = new BlockConfigLoader();
|
||||||
resetLoader = new ResetConfigLoader(defaults);
|
resetLoader = new ResetConfigLoader(defaultsMerger.getDefaults());
|
||||||
rewardLoader = new RewardConfigLoader();
|
rewardLoader = new RewardConfigLoader();
|
||||||
teleportLoader = new TeleportConfigLoader();
|
teleportLoader = new TeleportConfigLoader();
|
||||||
uiLoader = new UIConfigLoader(defaults);
|
uiLoader = new UIConfigLoader(defaultsMerger.getDefaults());
|
||||||
worldGuardLoader = new WorldGuardConfigLoader(defaults);
|
worldGuardLoader = new WorldGuardConfigLoader(defaultsMerger.getDefaults());
|
||||||
playerSpawnLoader = new PlayerSpawnConfigLoader();
|
playerSpawnLoader = new PlayerSpawnConfigLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a mine configuration from file.
|
|
||||||
*/
|
|
||||||
public MineConfig load(String mineName) throws ConfigLoadException {
|
public MineConfig load(String mineName) throws ConfigLoadException {
|
||||||
Path configPath = dataFolder.resolve("mines").resolve(mineName + ".yml");
|
if (!Files.exists(dataFolder.resolve("mines").resolve(mineName + ".yml"))) {
|
||||||
|
|
||||||
if (!Files.exists(configPath)) {
|
|
||||||
throw new ConfigLoadException("Mine not found: " + mineName);
|
throw new ConfigLoadException("Mine not found: " + mineName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Path configPath = dataFolder.resolve("mines").resolve(mineName + ".yml");
|
||||||
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(configPath.toFile());
|
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(configPath.toFile());
|
||||||
mergeDefaults(yaml);
|
defaultsMerger.mergeDefaults(yaml);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return MineConfig.builder(mineName)
|
return MineConfig.builder(mineName)
|
||||||
|
|
@ -117,11 +71,7 @@ public final class ConfigLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Save a mine configuration to file.
|
|
||||||
*/
|
|
||||||
public void save(MineConfig config) throws ConfigLoadException {
|
public void save(MineConfig config) throws ConfigLoadException {
|
||||||
Path configPath = dataFolder.resolve("mines").resolve(config.name() + ".yml");
|
|
||||||
YamlConfiguration yaml = new YamlConfiguration();
|
YamlConfiguration yaml = new YamlConfiguration();
|
||||||
|
|
||||||
regionLoader.save(yaml, config.region());
|
regionLoader.save(yaml, config.region());
|
||||||
|
|
@ -134,6 +84,7 @@ public final class ConfigLoader {
|
||||||
playerSpawnLoader.save(yaml, config.playerSpawn());
|
playerSpawnLoader.save(yaml, config.playerSpawn());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
Path configPath = dataFolder.resolve("mines").resolve(config.name() + ".yml");
|
||||||
Files.createDirectories(configPath.getParent());
|
Files.createDirectories(configPath.getParent());
|
||||||
yaml.save(configPath.toFile());
|
yaml.save(configPath.toFile());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
@ -141,21 +92,11 @@ public final class ConfigLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mergeDefaults(YamlConfiguration yaml) {
|
|
||||||
for (String key : defaults.getKeys(true)) {
|
|
||||||
if (!yaml.contains(key)) {
|
|
||||||
yaml.set(key, defaults.get(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception for config loading errors.
|
|
||||||
*/
|
|
||||||
public static class ConfigLoadException extends Exception {
|
public static class ConfigLoadException extends Exception {
|
||||||
public ConfigLoadException(String message) {
|
public ConfigLoadException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigLoadException(String message, Throwable cause) {
|
public ConfigLoadException(String message, Throwable cause) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
package dev.loki.lomines.data.config;
|
||||||
|
|
||||||
|
import dev.loki.lomines.data.config.loader.region.WorldGuardConfigLoader;
|
||||||
|
import dev.loki.lomines.data.config.loader.entity.PlayerSpawnConfigLoader;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class DefaultsMerger {
|
||||||
|
|
||||||
|
private final Path dataFolder;
|
||||||
|
private YamlConfiguration defaults;
|
||||||
|
|
||||||
|
public DefaultsMerger(Path dataFolder) {
|
||||||
|
this.dataFolder = dataFolder;
|
||||||
|
loadDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
public YamlConfiguration getDefaults() {
|
||||||
|
return defaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadDefaults() {
|
||||||
|
Path defaultsPath = dataFolder.resolve("mines").resolve("_defaults.yml");
|
||||||
|
defaults = new YamlConfiguration();
|
||||||
|
|
||||||
|
if (Files.exists(defaultsPath)) {
|
||||||
|
defaults = YamlConfiguration.loadConfiguration(defaultsPath.toFile());
|
||||||
|
} else {
|
||||||
|
setDefaults(defaults);
|
||||||
|
try {
|
||||||
|
Files.createDirectories(defaultsPath.getParent());
|
||||||
|
defaults.save(defaultsPath.toFile());
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Ignore, use in-memory defaults
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDefaults(YamlConfiguration yaml) {
|
||||||
|
yaml.set("reset.interval", "5m");
|
||||||
|
yaml.set("reset.percent-trigger", 10.0);
|
||||||
|
yaml.set("reset.percent-enabled", false);
|
||||||
|
yaml.set("reset.commands", List.of());
|
||||||
|
yaml.set("reset.broadcast", "");
|
||||||
|
|
||||||
|
yaml.set("ui.actionbar.enabled", true);
|
||||||
|
yaml.set("ui.actionbar.format", "<green>{mine}</green> <gray>{percent}%</gray> <dark_gray>({time})");
|
||||||
|
yaml.set("ui.actionbar.range", 50.0);
|
||||||
|
yaml.set("ui.timer-format", "mm:ss");
|
||||||
|
|
||||||
|
yaml.set("teleport.enabled", false);
|
||||||
|
yaml.set("rewards", List.of());
|
||||||
|
|
||||||
|
WorldGuardConfigLoader wgLoader = new WorldGuardConfigLoader(new YamlConfiguration());
|
||||||
|
wgLoader.setDefaults(yaml);
|
||||||
|
PlayerSpawnConfigLoader psLoader = new PlayerSpawnConfigLoader();
|
||||||
|
psLoader.setDefaults(yaml);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mergeDefaults(YamlConfiguration yaml) {
|
||||||
|
for (String key : defaults.getKeys(true)) {
|
||||||
|
if (!yaml.contains(key)) {
|
||||||
|
yaml.set(key, defaults.get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package dev.loki.lomines.data.config.block;
|
package dev.loki.lomines.data.config.block;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,6 @@ import java.util.Objects;
|
||||||
*/
|
*/
|
||||||
public sealed interface BlockKey permits BlockKey.Vanilla, BlockKey.Oraxen, BlockKey.ItemsAdder {
|
public sealed interface BlockKey permits BlockKey.Vanilla, BlockKey.Oraxen, BlockKey.ItemsAdder {
|
||||||
|
|
||||||
/**
|
|
||||||
* Serializes this key to string format for YAML storage.
|
|
||||||
* Format: "stone" (vanilla), "oraxen:my_block", "itemsadder:custom_block"
|
|
||||||
*/
|
|
||||||
String serialize();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserializes a string to appropriate BlockKey type.
|
* Deserializes a string to appropriate BlockKey type.
|
||||||
*
|
*
|
||||||
|
|
@ -43,6 +37,12 @@ public sealed interface BlockKey permits BlockKey.Vanilla, BlockKey.Oraxen, Bloc
|
||||||
return new Vanilla(material);
|
return new Vanilla(material);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes this key to string format for YAML storage.
|
||||||
|
* Format: "stone" (vanilla), "oraxen:my_block", "itemsadder:custom_block"
|
||||||
|
*/
|
||||||
|
String serialize();
|
||||||
|
|
||||||
// --- Implementations ---
|
// --- Implementations ---
|
||||||
|
|
||||||
record Vanilla(Material material) implements BlockKey {
|
record Vanilla(Material material) implements BlockKey {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,9 @@ public final class BlockConfigLoader {
|
||||||
Map<BlockKey, Double> weights = new HashMap<>();
|
Map<BlockKey, Double> weights = new HashMap<>();
|
||||||
for (String key : contents.getKeys(false)) {
|
for (String key : contents.getKeys(false)) {
|
||||||
double weight = contents.getDouble(key);
|
double weight = contents.getDouble(key);
|
||||||
if (weight <= 0) continue;
|
if (weight <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
weights.put(BlockKey.deserialize(key), weight);
|
weights.put(BlockKey.deserialize(key), weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,9 +42,13 @@ public final class BlockConfigLoader {
|
||||||
yaml.getString("blocks.fill-mode", "CUBOID").toUpperCase()
|
yaml.getString("blocks.fill-mode", "CUBOID").toUpperCase()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Parse mask config if applicable
|
return new BlockConfig(weights, fillMode, parseMaskConfig(yaml, fillMode));
|
||||||
BlockConfig.MaskConfig mask = null;
|
}
|
||||||
if (fillMode == FillMode.MASK) {
|
|
||||||
|
private BlockConfig.MaskConfig parseMaskConfig(YamlConfiguration yaml, FillMode fillMode) {
|
||||||
|
if (fillMode != FillMode.MASK) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
String markerStr = yaml.getString("blocks.mask.marker", "pink_concrete");
|
String markerStr = yaml.getString("blocks.mask.marker", "pink_concrete");
|
||||||
BlockKey marker = BlockKey.deserialize(markerStr);
|
BlockKey marker = BlockKey.deserialize(markerStr);
|
||||||
|
|
||||||
|
|
@ -52,10 +58,7 @@ public final class BlockConfigLoader {
|
||||||
positions.put(pos, true);
|
positions.put(pos, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
mask = new BlockConfig.MaskConfig(marker, positions);
|
return new BlockConfig.MaskConfig(marker, positions);
|
||||||
}
|
|
||||||
|
|
||||||
return new BlockConfig(weights, fillMode, mask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -17,32 +17,17 @@ public final class RegionConfigLoader {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses region configuration from YAML.
|
* Parses region configuration from YAML.
|
||||||
|
* Supports up to 100 selection points (50 regions).
|
||||||
*/
|
*/
|
||||||
public RegionConfig parse(YamlConfiguration yaml) {
|
public RegionConfig parse(YamlConfiguration yaml) {
|
||||||
List<Location> selections = new ArrayList<>();
|
List<Location> selections = new ArrayList<>();
|
||||||
|
|
||||||
// Try new format first
|
// Try new format first (region.selection.X)
|
||||||
for (int i = 1; i <= 10; i++) {
|
selections.addAll(parseSelections(yaml, "region.selection.", 100));
|
||||||
String key = "region.selection." + i;
|
|
||||||
if (yaml.contains(key)) {
|
|
||||||
String locStr = yaml.getString(key);
|
|
||||||
if (locStr != null && !locStr.isBlank()) {
|
|
||||||
selections.add(parseLocation(locStr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Legacy fallback
|
// Legacy fallback (selection.X)
|
||||||
if (selections.isEmpty()) {
|
if (selections.isEmpty()) {
|
||||||
for (int i = 1; i <= 10; i++) {
|
selections.addAll(parseSelections(yaml, "selection.", 100));
|
||||||
String key = "selection." + i;
|
|
||||||
if (yaml.contains(key)) {
|
|
||||||
String locStr = yaml.getString(key);
|
|
||||||
if (locStr != null && !locStr.isBlank()) {
|
|
||||||
selections.add(parseLocation(locStr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selections.size() < 2) {
|
if (selections.size() < 2) {
|
||||||
|
|
@ -52,15 +37,56 @@ public final class RegionConfigLoader {
|
||||||
return RegionConfig.fromSelections(selections);
|
return RegionConfig.fromSelections(selections);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses selection points from YAML with given prefix.
|
||||||
|
* Supports indexed format: prefix.1, prefix.2, etc.
|
||||||
|
*/
|
||||||
|
private List<Location> parseSelections(YamlConfiguration yaml, String prefix, int maxPoints) {
|
||||||
|
List<Location> selections = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 1; i <= maxPoints; i++) {
|
||||||
|
String key = prefix + i;
|
||||||
|
if (yaml.contains(key)) {
|
||||||
|
String locStr = yaml.getString(key);
|
||||||
|
if (locStr != null && !locStr.isBlank()) {
|
||||||
|
try {
|
||||||
|
selections.add(parseLocation(locStr));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// Skip invalid locations but continue parsing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Stop at first missing index (assuming sequential)
|
||||||
|
// But check at least a few more for gaps
|
||||||
|
if (i > 20) {
|
||||||
|
boolean hasMore = false;
|
||||||
|
for (int j = i + 1; j <= Math.min(i + 5, maxPoints); j++) {
|
||||||
|
if (yaml.contains(prefix + j)) {
|
||||||
|
hasMore = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasMore) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return selections;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves region configuration to YAML.
|
* Saves region configuration to YAML.
|
||||||
*/
|
*/
|
||||||
public void save(YamlConfiguration yaml, RegionConfig region) {
|
public void save(YamlConfiguration yaml, RegionConfig region) {
|
||||||
int i = 1;
|
int i = 1;
|
||||||
for (var cuboid : region.regions()) {
|
for (var cuboid : region.regions()) {
|
||||||
yaml.set("region.selection." + i, LocationParser.format(cuboid.getMin()));
|
Location min = new Location(cuboid.getWorld(), cuboid.getMinX(), cuboid.getMinY(), cuboid.getMinZ());
|
||||||
|
yaml.set("region.selection." + i, LocationParser.format(min));
|
||||||
i++;
|
i++;
|
||||||
yaml.set("region.selection." + i, LocationParser.format(cuboid.getMax()));
|
Location max = new Location(cuboid.getWorld(), cuboid.getMaxX(), cuboid.getMaxY(), cuboid.getMaxZ());
|
||||||
|
yaml.set("region.selection." + i, LocationParser.format(max));
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
package dev.loki.lomines.data.config.loader.region;
|
package dev.loki.lomines.data.config.loader.region;
|
||||||
|
|
||||||
import dev.loki.lomines.data.config.ConfigLoader;
|
import dev.loki.lomines.integration.worldguard.config.WorldGuardConfig;
|
||||||
import dev.loki.lomines.integration.worldguard.WorldGuardConfig;
|
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loader for WorldGuard configuration section.
|
* Loader for WorldGuard configuration section.
|
||||||
|
|
@ -23,51 +19,23 @@ public final class WorldGuardConfigLoader {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses WorldGuard configuration from YAML.
|
* Parses WorldGuard configuration from YAML.
|
||||||
|
* Supports any WorldGuard flags. Default: block-break=allow.
|
||||||
*/
|
*/
|
||||||
public WorldGuardConfig parse(YamlConfiguration yaml) {
|
public WorldGuardConfig parse(YamlConfiguration yaml) {
|
||||||
ConfigurationSection wgSection = yaml.getConfigurationSection("worldguard");
|
if (!isEnabled(yaml)) {
|
||||||
if (wgSection == null) {
|
|
||||||
// Check defaults
|
|
||||||
boolean defaultEnabled = defaults.getBoolean("worldguard.enabled", false);
|
|
||||||
if (!defaultEnabled) {
|
|
||||||
return WorldGuardConfig.disabled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean enabled = yaml.getBoolean("worldguard.enabled",
|
|
||||||
defaults.getBoolean("worldguard.enabled", false));
|
|
||||||
|
|
||||||
if (!enabled) {
|
|
||||||
return WorldGuardConfig.disabled();
|
return WorldGuardConfig.disabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
String template = yaml.getString("worldguard.region-template",
|
List<String> owners = resolveOwners(yaml);
|
||||||
|
List<String> members = resolveMembers(yaml);
|
||||||
|
List<String> flags = resolveFlags(yaml);
|
||||||
|
boolean protect = resolveProtect(yaml);
|
||||||
|
String tmpl = yaml.getString("worldguard.region-template",
|
||||||
defaults.getString("worldguard.region-template", "{mine_name}_{random_4}"));
|
defaults.getString("worldguard.region-template", "{mine_name}_{random_4}"));
|
||||||
|
|
||||||
List<String> owners = yaml.getStringList("worldguard.owners");
|
|
||||||
if (owners.isEmpty()) {
|
|
||||||
owners = defaults.getStringList("worldguard.owners");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> members = yaml.getStringList("worldguard.members");
|
|
||||||
if (members.isEmpty()) {
|
|
||||||
members = defaults.getStringList("worldguard.members");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> flags = yaml.getStringList("worldguard.flags");
|
|
||||||
if (flags.isEmpty()) {
|
|
||||||
flags = defaults.getStringList("worldguard.flags");
|
|
||||||
}
|
|
||||||
if (flags.isEmpty()) {
|
|
||||||
flags = List.of("passthrough=deny", "build=allow");
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean protect = yaml.getBoolean("worldguard.protect-on-create",
|
|
||||||
defaults.getBoolean("worldguard.protect-on-create", true));
|
|
||||||
|
|
||||||
return WorldGuardConfig.builder()
|
return WorldGuardConfig.builder()
|
||||||
.enabled(true)
|
.enabled(true)
|
||||||
.template(template)
|
.template(tmpl)
|
||||||
.owners(owners)
|
.owners(owners)
|
||||||
.members(members)
|
.members(members)
|
||||||
.flags(flags)
|
.flags(flags)
|
||||||
|
|
@ -75,6 +43,50 @@ public final class WorldGuardConfigLoader {
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isEnabled(YamlConfiguration yaml) {
|
||||||
|
ConfigurationSection wgSection = yaml.getConfigurationSection("worldguard");
|
||||||
|
if (wgSection == null) {
|
||||||
|
boolean defaultEnabled = defaults.getBoolean("worldguard.enabled", false);
|
||||||
|
if (!defaultEnabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return yaml.getBoolean("worldguard.enabled", defaults.getBoolean("worldguard.enabled", false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> resolveOwners(YamlConfiguration yaml) {
|
||||||
|
List<String> owners = yaml.getStringList("worldguard.owners");
|
||||||
|
if (owners.isEmpty()) {
|
||||||
|
owners = defaults.getStringList("worldguard.owners");
|
||||||
|
}
|
||||||
|
return owners;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> resolveMembers(YamlConfiguration yaml) {
|
||||||
|
List<String> members = yaml.getStringList("worldguard.members");
|
||||||
|
if (members.isEmpty()) {
|
||||||
|
members = defaults.getStringList("worldguard.members");
|
||||||
|
}
|
||||||
|
return members;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> resolveFlags(YamlConfiguration yaml) {
|
||||||
|
// Support any WorldGuard flags. Default: only block-break=allow
|
||||||
|
List<String> flags = yaml.getStringList("worldguard.flags");
|
||||||
|
if (flags.isEmpty()) {
|
||||||
|
flags = defaults.getStringList("worldguard.flags");
|
||||||
|
}
|
||||||
|
if (flags.isEmpty()) {
|
||||||
|
flags = List.of("block-break=allow");
|
||||||
|
}
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean resolveProtect(YamlConfiguration yaml) {
|
||||||
|
return yaml.getBoolean("worldguard.protect-on-create",
|
||||||
|
defaults.getBoolean("worldguard.protect-on-create", true));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves WorldGuard configuration to YAML.
|
* Saves WorldGuard configuration to YAML.
|
||||||
*/
|
*/
|
||||||
|
|
@ -92,13 +104,14 @@ public final class WorldGuardConfigLoader {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets default values for WorldGuard configuration.
|
* Sets default values for WorldGuard configuration.
|
||||||
|
* Default: only block-break=allow. User can add any flags.
|
||||||
*/
|
*/
|
||||||
public void setDefaults(YamlConfiguration yaml) {
|
public void setDefaults(YamlConfiguration yaml) {
|
||||||
yaml.set("worldguard.enabled", false);
|
yaml.set("worldguard.enabled", false);
|
||||||
yaml.set("worldguard.region-template", "{mine_name}_{random_4}");
|
yaml.set("worldguard.region-template", "{mine_name}_{random_4}");
|
||||||
yaml.set("worldguard.owners", List.of());
|
yaml.set("worldguard.owners", List.of());
|
||||||
yaml.set("worldguard.members", List.of());
|
yaml.set("worldguard.members", List.of());
|
||||||
yaml.set("worldguard.flags", List.of("passthrough=deny", "build=allow"));
|
yaml.set("worldguard.flags", List.of("block-break=allow"));
|
||||||
yaml.set("worldguard.protect-on-create", true);
|
yaml.set("worldguard.protect-on-create", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,18 +36,31 @@ public final class RewardConfigLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private RewardEntry parseRewardEntry(Map<?, ?> map) {
|
private RewardEntry parseRewardEntry(Map<?, ?> map) {
|
||||||
double chance = ((Number) map.getOrDefault("chance", 0)).doubleValue();
|
List<BlockKey> blocks = parseBlocks(map.get("blocks"));
|
||||||
|
List<ItemReward> items = parseItems(map.get("items"));
|
||||||
|
List<String> commands = parseCommands(map.get("commands"));
|
||||||
|
Object preventDropsObj = map.get("prevent-drops");
|
||||||
|
boolean preventDrops = preventDropsObj instanceof Boolean b && b;
|
||||||
|
|
||||||
|
Object chanceObj = map.get("chance");
|
||||||
|
double chance = chanceObj instanceof Number n ? n.doubleValue() : 0.0;
|
||||||
|
return new RewardEntry(blocks, chance, items, commands, preventDrops);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<BlockKey> parseBlocks(Object rawBlocks) {
|
||||||
List<BlockKey> blocks = new ArrayList<>();
|
List<BlockKey> blocks = new ArrayList<>();
|
||||||
List<?> blockList = (List<?>) map.get("blocks");
|
List<?> blockList = (List<?>) rawBlocks;
|
||||||
if (blockList != null) {
|
if (blockList != null) {
|
||||||
for (Object b : blockList) {
|
for (Object b : blockList) {
|
||||||
blocks.add(BlockKey.deserialize(b.toString()));
|
blocks.add(BlockKey.deserialize(b.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ItemReward> parseItems(Object rawItems) {
|
||||||
List<ItemReward> items = new ArrayList<>();
|
List<ItemReward> items = new ArrayList<>();
|
||||||
List<?> itemList = (List<?>) map.get("items");
|
List<?> itemList = (List<?>) rawItems;
|
||||||
if (itemList != null) {
|
if (itemList != null) {
|
||||||
for (Object i : itemList) {
|
for (Object i : itemList) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|
@ -55,12 +68,19 @@ public final class RewardConfigLoader {
|
||||||
items.add(parseItemReward(itemMap));
|
items.add(parseItemReward(itemMap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
private List<String> parseCommands(Object commandsObj) {
|
||||||
List<String> commands = (List<String>) map.getOrDefault("commands", List.of());
|
List<String> commands = new ArrayList<>();
|
||||||
boolean preventDrops = (Boolean) map.getOrDefault("prevent-drops", false);
|
if (commandsObj instanceof List<?> list) {
|
||||||
|
for (Object obj : list) {
|
||||||
return new RewardEntry(blocks, chance, items, commands, preventDrops);
|
if (obj != null) {
|
||||||
|
commands.add(obj.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ItemReward parseItemReward(Map<String, Object> map) {
|
private ItemReward parseItemReward(Map<String, Object> map) {
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ package dev.loki.lomines.data.config.loader.system;
|
||||||
import dev.loki.lomines.data.config.reset.ResetConfig;
|
import dev.loki.lomines.data.config.reset.ResetConfig;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loader for reset configuration section.
|
* Loader for reset configuration section.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package dev.loki.lomines.data.config.loader.system;
|
package dev.loki.lomines.data.config.loader.system;
|
||||||
|
|
||||||
import dev.loki.lomines.data.config.ui.UIConfig;
|
import dev.loki.lomines.data.config.ui.UIConfig;
|
||||||
|
import dev.loki.lomines.data.config.ui.HologramConfig;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loader for UI configuration section.
|
* Loader for UI configuration section.
|
||||||
|
|
@ -25,10 +27,21 @@ public final class UIConfigLoader {
|
||||||
actionBarEnabled,
|
actionBarEnabled,
|
||||||
yaml.getString("ui.actionbar.format", UIConfig.DEFAULT_ACTIONBAR_FORMAT),
|
yaml.getString("ui.actionbar.format", UIConfig.DEFAULT_ACTIONBAR_FORMAT),
|
||||||
yaml.getDouble("ui.actionbar.range", 50.0),
|
yaml.getDouble("ui.actionbar.range", 50.0),
|
||||||
yaml.getString("ui.timer-format", UIConfig.DEFAULT_TIMER_FORMAT)
|
yaml.getString("ui.timer-format", UIConfig.DEFAULT_TIMER_FORMAT),
|
||||||
|
parseHologram(yaml)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HologramConfig parseHologram(YamlConfiguration yaml) {
|
||||||
|
boolean enabled = yaml.getBoolean("ui.hologram.enabled", false);
|
||||||
|
List<String> format = yaml.getStringList("ui.hologram.format");
|
||||||
|
if (format.isEmpty()) {
|
||||||
|
format = HologramConfig.defaults().format();
|
||||||
|
}
|
||||||
|
double height = yaml.getDouble("ui.hologram.height", 2.5);
|
||||||
|
return new HologramConfig(enabled, format, height);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves UI configuration to YAML.
|
* Saves UI configuration to YAML.
|
||||||
*/
|
*/
|
||||||
|
|
@ -37,5 +50,8 @@ public final class UIConfigLoader {
|
||||||
yaml.set("ui.actionbar.format", ui.actionBarFormat());
|
yaml.set("ui.actionbar.format", ui.actionBarFormat());
|
||||||
yaml.set("ui.actionbar.range", ui.actionBarRange());
|
yaml.set("ui.actionbar.range", ui.actionBarRange());
|
||||||
yaml.set("ui.timer-format", ui.timerFormat());
|
yaml.set("ui.timer-format", ui.timerFormat());
|
||||||
|
yaml.set("ui.hologram.enabled", ui.hologram().enabled());
|
||||||
|
yaml.set("ui.hologram.format", ui.hologram().format());
|
||||||
|
yaml.set("ui.hologram.height", ui.hologram().height());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
package dev.loki.lomines.data.config.model;
|
||||||
|
|
||||||
|
import dev.loki.lomines.data.config.block.BlockConfig;
|
||||||
|
import dev.loki.lomines.data.config.region.RegionConfig;
|
||||||
|
import dev.loki.lomines.data.config.reset.ResetConfig;
|
||||||
|
import dev.loki.lomines.data.config.reward.RewardConfig;
|
||||||
|
import dev.loki.lomines.data.config.spawn.PlayerSpawnConfig;
|
||||||
|
import dev.loki.lomines.data.config.teleport.TeleportConfig;
|
||||||
|
import dev.loki.lomines.data.config.ui.UIConfig;
|
||||||
|
import dev.loki.lomines.integration.worldguard.config.WorldGuardConfig;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public record MineConfig(
|
||||||
|
String name,
|
||||||
|
RegionConfig region,
|
||||||
|
BlockConfig blocks,
|
||||||
|
ResetConfig reset,
|
||||||
|
RewardConfig rewards,
|
||||||
|
TeleportConfig teleport,
|
||||||
|
UIConfig ui,
|
||||||
|
WorldGuardConfig worldGuard,
|
||||||
|
PlayerSpawnConfig playerSpawn
|
||||||
|
) {
|
||||||
|
|
||||||
|
public MineConfig {
|
||||||
|
Objects.requireNonNull(name, "Mine name cannot be null");
|
||||||
|
if (name.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("Mine name cannot be blank");
|
||||||
|
}
|
||||||
|
name = name.toLowerCase().trim();
|
||||||
|
|
||||||
|
Objects.requireNonNull(region, "Region config cannot be null");
|
||||||
|
Objects.requireNonNull(blocks, "Block config cannot be null");
|
||||||
|
Objects.requireNonNull(reset, "Reset config cannot be null");
|
||||||
|
Objects.requireNonNull(rewards, "Reward config cannot be null");
|
||||||
|
Objects.requireNonNull(teleport, "Teleport config cannot be null");
|
||||||
|
Objects.requireNonNull(ui, "UI config cannot be null");
|
||||||
|
if (playerSpawn == null) {
|
||||||
|
playerSpawn = PlayerSpawnConfig.disabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MineConfigDefaults.Builder builder(String name) {
|
||||||
|
return new MineConfigDefaults.Builder(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int volume() {
|
||||||
|
return region.totalVolume();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String worldName() {
|
||||||
|
return region.worldName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Location> getSpawnForStuckPlayer() {
|
||||||
|
return playerSpawn.enabled()
|
||||||
|
? playerSpawn.getLocation()
|
||||||
|
: teleport.getLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.loki.lomines.data.config;
|
package dev.loki.lomines.data.config.model;
|
||||||
|
|
||||||
import dev.loki.lomines.data.config.block.BlockConfig;
|
import dev.loki.lomines.data.config.block.BlockConfig;
|
||||||
import dev.loki.lomines.data.config.region.RegionConfig;
|
import dev.loki.lomines.data.config.region.RegionConfig;
|
||||||
|
|
@ -7,72 +7,13 @@ import dev.loki.lomines.data.config.reward.RewardConfig;
|
||||||
import dev.loki.lomines.data.config.spawn.PlayerSpawnConfig;
|
import dev.loki.lomines.data.config.spawn.PlayerSpawnConfig;
|
||||||
import dev.loki.lomines.data.config.teleport.TeleportConfig;
|
import dev.loki.lomines.data.config.teleport.TeleportConfig;
|
||||||
import dev.loki.lomines.data.config.ui.UIConfig;
|
import dev.loki.lomines.data.config.ui.UIConfig;
|
||||||
import dev.loki.lomines.integration.worldguard.WorldGuardConfig;
|
import dev.loki.lomines.integration.worldguard.config.WorldGuardConfig;
|
||||||
|
|
||||||
import org.bukkit.Location;
|
public final class MineConfigDefaults {
|
||||||
|
|
||||||
import java.util.Objects;
|
private MineConfigDefaults() {
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clean, type-safe, section-based mine configuration.
|
|
||||||
* Proper separation of concerns: each section handles one aspect.
|
|
||||||
*/
|
|
||||||
public record MineConfig(
|
|
||||||
String name,
|
|
||||||
RegionConfig region,
|
|
||||||
BlockConfig blocks,
|
|
||||||
ResetConfig reset,
|
|
||||||
RewardConfig rewards,
|
|
||||||
TeleportConfig teleport,
|
|
||||||
UIConfig ui,
|
|
||||||
WorldGuardConfig worldGuard,
|
|
||||||
PlayerSpawnConfig playerSpawn
|
|
||||||
) {
|
|
||||||
|
|
||||||
public MineConfig {
|
|
||||||
Objects.requireNonNull(name, "Mine name cannot be null");
|
|
||||||
if (name.isBlank()) {
|
|
||||||
throw new IllegalArgumentException("Mine name cannot be blank");
|
|
||||||
}
|
|
||||||
name = name.toLowerCase().trim();
|
|
||||||
|
|
||||||
Objects.requireNonNull(region, "Region config cannot be null");
|
|
||||||
Objects.requireNonNull(blocks, "Block config cannot be null");
|
|
||||||
Objects.requireNonNull(reset, "Reset config cannot be null");
|
|
||||||
Objects.requireNonNull(rewards, "Reward config cannot be null");
|
|
||||||
Objects.requireNonNull(teleport, "Teleport config cannot be null");
|
|
||||||
Objects.requireNonNull(ui, "UI config cannot be null");
|
|
||||||
// worldGuard and playerSpawn can be null (disabled by default)
|
|
||||||
if (playerSpawn == null) {
|
|
||||||
playerSpawn = PlayerSpawnConfig.disabled();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Total volume of the mine (for progress calculation).
|
|
||||||
*/
|
|
||||||
public int volume() {
|
|
||||||
return region.totalVolume();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* World name where the mine is located.
|
|
||||||
*/
|
|
||||||
public String worldName() {
|
|
||||||
return region.worldName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates builder for fluent construction.
|
|
||||||
*/
|
|
||||||
public static Builder builder(String name) {
|
|
||||||
return new Builder(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates config with sensible defaults.
|
|
||||||
*/
|
|
||||||
public static MineConfig defaults(String name, RegionConfig region, BlockConfig blocks) {
|
public static MineConfig defaults(String name, RegionConfig region, BlockConfig blocks) {
|
||||||
return new MineConfig(
|
return new MineConfig(
|
||||||
name,
|
name,
|
||||||
|
|
@ -87,18 +28,6 @@ public record MineConfig(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the spawn location for stuck players.
|
|
||||||
* If playerSpawn is not set, falls back to teleport location.
|
|
||||||
*/
|
|
||||||
public Optional<Location> getSpawnForStuckPlayer() {
|
|
||||||
return playerSpawn.enabled()
|
|
||||||
? playerSpawn.getLocation()
|
|
||||||
: teleport.getLocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Builder ---
|
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private final String name;
|
private final String name;
|
||||||
private RegionConfig region;
|
private RegionConfig region;
|
||||||
|
|
@ -110,7 +39,7 @@ public record MineConfig(
|
||||||
private WorldGuardConfig worldGuard = WorldGuardConfig.disabled();
|
private WorldGuardConfig worldGuard = WorldGuardConfig.disabled();
|
||||||
private PlayerSpawnConfig playerSpawn = PlayerSpawnConfig.disabled();
|
private PlayerSpawnConfig playerSpawn = PlayerSpawnConfig.disabled();
|
||||||
|
|
||||||
private Builder(String name) {
|
public Builder(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package dev.loki.lomines.data.config.parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception for configuration parsing errors.
|
||||||
|
*/
|
||||||
|
public class ConfigParseException extends Exception {
|
||||||
|
|
||||||
|
public ConfigParseException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigParseException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package dev.loki.lomines.data.config.region;
|
package dev.loki.lomines.data.config.region;
|
||||||
|
|
||||||
import dev.loki.lomines.util.location.Cuboid;
|
import dev.loki.lomines.util.location.geo.Cuboid;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
package dev.loki.lomines.data.config.reset;
|
package dev.loki.lomines.data.config.reset;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration for mine reset behavior.
|
|
||||||
*/
|
|
||||||
public record ResetConfig(
|
public record ResetConfig(
|
||||||
Duration interval,
|
Duration interval,
|
||||||
double percentTrigger,
|
double percentTrigger,
|
||||||
|
|
@ -16,41 +11,34 @@ public record ResetConfig(
|
||||||
String broadcastMessage
|
String broadcastMessage
|
||||||
) {
|
) {
|
||||||
|
|
||||||
public static final Duration DEFAULT_INTERVAL = Duration.ofMinutes(5);
|
|
||||||
public static final double DEFAULT_PERCENT_TRIGGER = 10.0;
|
|
||||||
|
|
||||||
public ResetConfig {
|
public ResetConfig {
|
||||||
// Normalize and validate interval
|
interval = ResetConfigValidator.validateInterval(interval);
|
||||||
if (interval == null || interval.isZero() || interval.isNegative()) {
|
percentTrigger = ResetConfigValidator.validatePercentTrigger(percentTrigger);
|
||||||
interval = DEFAULT_INTERVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure interval is reasonable (1 second to 24 hours)
|
|
||||||
if (interval.getSeconds() < 1) {
|
|
||||||
interval = Duration.ofSeconds(1);
|
|
||||||
} else if (interval.getSeconds() > 86400) {
|
|
||||||
interval = Duration.ofHours(24);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate percent trigger
|
|
||||||
if (percentTrigger < 0 || percentTrigger > 100) {
|
|
||||||
percentTrigger = DEFAULT_PERCENT_TRIGGER;
|
|
||||||
}
|
|
||||||
|
|
||||||
commands = commands != null ? List.copyOf(commands) : List.of();
|
commands = commands != null ? List.copyOf(commands) : List.of();
|
||||||
broadcastMessage = broadcastMessage != null ? broadcastMessage : "";
|
broadcastMessage = broadcastMessage != null ? broadcastMessage : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static final Duration DEFAULT_INTERVAL = ResetConfigValidator.DEFAULT_INTERVAL;
|
||||||
* Interval in ticks (20 ticks = 1 second).
|
public static final double DEFAULT_PERCENT_TRIGGER = ResetConfigValidator.DEFAULT_PERCENT_TRIGGER;
|
||||||
*/
|
|
||||||
|
public static ResetConfig defaults() {
|
||||||
|
return new ResetConfig(
|
||||||
|
DEFAULT_INTERVAL,
|
||||||
|
DEFAULT_PERCENT_TRIGGER,
|
||||||
|
false,
|
||||||
|
List.of(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
public long intervalTicks() {
|
public long intervalTicks() {
|
||||||
return interval.getSeconds() * 20;
|
return interval.getSeconds() * 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Human-readable interval string.
|
|
||||||
*/
|
|
||||||
public String intervalDisplay() {
|
public String intervalDisplay() {
|
||||||
long seconds = interval.getSeconds();
|
long seconds = interval.getSeconds();
|
||||||
if (seconds < 60) {
|
if (seconds < 60) {
|
||||||
|
|
@ -62,33 +50,10 @@ public record ResetConfig(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether reset should trigger based on percent mined.
|
|
||||||
*/
|
|
||||||
public boolean isPercentTriggerEnabled() {
|
public boolean isPercentTriggerEnabled() {
|
||||||
return percentEnabled && percentTrigger > 0;
|
return percentEnabled && percentTrigger > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Default config: 5 minutes, 10% trigger disabled.
|
|
||||||
*/
|
|
||||||
public static ResetConfig defaults() {
|
|
||||||
return new ResetConfig(
|
|
||||||
DEFAULT_INTERVAL,
|
|
||||||
DEFAULT_PERCENT_TRIGGER,
|
|
||||||
false,
|
|
||||||
List.of(),
|
|
||||||
""
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builder for fluent construction.
|
|
||||||
*/
|
|
||||||
public static Builder builder() {
|
|
||||||
return new Builder();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private Duration interval = DEFAULT_INTERVAL;
|
private Duration interval = DEFAULT_INTERVAL;
|
||||||
private double percentTrigger = DEFAULT_PERCENT_TRIGGER;
|
private double percentTrigger = DEFAULT_PERCENT_TRIGGER;
|
||||||
|
|
@ -101,13 +66,13 @@ public record ResetConfig(
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder intervalTicks(long ticks) {
|
public Builder interval(String humanReadable) {
|
||||||
this.interval = Duration.ofSeconds(ticks / 20);
|
this.interval = ResetConfigValidator.parseDuration(humanReadable);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder interval(String humanReadable) {
|
public Builder intervalTicks(long ticks) {
|
||||||
this.interval = parseDuration(humanReadable);
|
this.interval = Duration.ofSeconds(ticks / 20);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,25 +99,5 @@ public record ResetConfig(
|
||||||
public ResetConfig build() {
|
public ResetConfig build() {
|
||||||
return new ResetConfig(interval, percentTrigger, percentEnabled, commands, broadcastMessage);
|
return new ResetConfig(interval, percentTrigger, percentEnabled, commands, broadcastMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Duration parseDuration(String s) {
|
|
||||||
s = s.trim().toLowerCase();
|
|
||||||
try {
|
|
||||||
if (s.endsWith("s")) {
|
|
||||||
return Duration.ofSeconds(Long.parseLong(s.substring(0, s.length() - 1)));
|
|
||||||
} else if (s.endsWith("m")) {
|
|
||||||
return Duration.ofMinutes(Long.parseLong(s.substring(0, s.length() - 1)));
|
|
||||||
} else if (s.endsWith("h")) {
|
|
||||||
return Duration.ofHours(Long.parseLong(s.substring(0, s.length() - 1)));
|
|
||||||
} else if (s.endsWith("d")) {
|
|
||||||
return Duration.ofDays(Long.parseLong(s.substring(0, s.length() - 1)));
|
|
||||||
} else {
|
|
||||||
// Assume seconds if no suffix
|
|
||||||
return Duration.ofSeconds(Long.parseLong(s));
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return DEFAULT_INTERVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package dev.loki.lomines.data.config.reset;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
public final class ResetConfigValidator {
|
||||||
|
|
||||||
|
public static final Duration DEFAULT_INTERVAL = Duration.ofMinutes(5);
|
||||||
|
public static final double DEFAULT_PERCENT_TRIGGER = 10.0;
|
||||||
|
|
||||||
|
private ResetConfigValidator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Duration validateInterval(Duration interval) {
|
||||||
|
if (interval == null || interval.isZero() || interval.isNegative()) {
|
||||||
|
interval = DEFAULT_INTERVAL;
|
||||||
|
}
|
||||||
|
if (interval.getSeconds() < 1) {
|
||||||
|
interval = Duration.ofSeconds(1);
|
||||||
|
} else if (interval.getSeconds() > 86400) {
|
||||||
|
interval = Duration.ofHours(24);
|
||||||
|
}
|
||||||
|
return interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double validatePercentTrigger(double percentTrigger) {
|
||||||
|
if (percentTrigger < 0 || percentTrigger > 100) {
|
||||||
|
return DEFAULT_PERCENT_TRIGGER;
|
||||||
|
}
|
||||||
|
return percentTrigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Duration parseDuration(String s) {
|
||||||
|
s = s.trim().toLowerCase();
|
||||||
|
try {
|
||||||
|
if (s.endsWith("s")) {
|
||||||
|
return Duration.ofSeconds(Long.parseLong(s.substring(0, s.length() - 1)));
|
||||||
|
} else if (s.endsWith("m")) {
|
||||||
|
return Duration.ofMinutes(Long.parseLong(s.substring(0, s.length() - 1)));
|
||||||
|
} else if (s.endsWith("h")) {
|
||||||
|
return Duration.ofHours(Long.parseLong(s.substring(0, s.length() - 1)));
|
||||||
|
} else if (s.endsWith("d")) {
|
||||||
|
return Duration.ofDays(Long.parseLong(s.substring(0, s.length() - 1)));
|
||||||
|
} else {
|
||||||
|
return Duration.ofSeconds(Long.parseLong(s));
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return DEFAULT_INTERVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,9 +4,7 @@ import dev.loki.lomines.data.config.block.BlockKey;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -18,6 +16,20 @@ public record RewardConfig(List<RewardEntry> entries) {
|
||||||
entries = entries != null ? List.copyOf(entries) : List.of();
|
entries = entries != null ? List.copyOf(entries) : List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty reward config.
|
||||||
|
*/
|
||||||
|
public static RewardConfig empty() {
|
||||||
|
return new RewardConfig(List.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single entry reward config builder.
|
||||||
|
*/
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns rewards applicable for the given block.
|
* Returns rewards applicable for the given block.
|
||||||
*/
|
*/
|
||||||
|
|
@ -34,20 +46,6 @@ public record RewardConfig(List<RewardEntry> entries) {
|
||||||
return !entries.isEmpty();
|
return !entries.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Empty reward config.
|
|
||||||
*/
|
|
||||||
public static RewardConfig empty() {
|
|
||||||
return new RewardConfig(List.of());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Single entry reward config builder.
|
|
||||||
*/
|
|
||||||
public static Builder builder() {
|
|
||||||
return new Builder();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Reward Entry ---
|
// --- Reward Entry ---
|
||||||
|
|
||||||
public record RewardEntry(
|
public record RewardEntry(
|
||||||
|
|
|
||||||
|
|
@ -2,33 +2,34 @@ package dev.loki.lomines.data.config.spawn;
|
||||||
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration for player spawn location when stuck in blocks.
|
* Configuration for player spawn locations when stuck in blocks.
|
||||||
|
* Supports multiple spawn points for a single mine.
|
||||||
* Used to teleport players out when they are suffocating after mine reset.
|
* Used to teleport players out when they are suffocating after mine reset.
|
||||||
*/
|
*/
|
||||||
public record PlayerSpawnConfig(
|
public record PlayerSpawnConfig(boolean enabled, List<Location> locations) {
|
||||||
boolean enabled,
|
|
||||||
Location location
|
|
||||||
) {
|
|
||||||
|
|
||||||
public PlayerSpawnConfig {
|
public PlayerSpawnConfig(boolean enabled, List<Location> locations) {
|
||||||
if (!enabled) {
|
this.enabled = enabled;
|
||||||
location = null;
|
if (!enabled || locations == null) {
|
||||||
} else if (location != null) {
|
this.locations = List.of();
|
||||||
// Defensive copy
|
} else {
|
||||||
location = location.clone();
|
// Defensive copy with validation
|
||||||
Objects.requireNonNull(location.getWorld(), "Spawn world cannot be null");
|
List<Location> copy = new ArrayList<>();
|
||||||
|
for (Location loc : locations) {
|
||||||
|
if (loc != null) {
|
||||||
|
Location cloned = loc.clone();
|
||||||
|
Objects.requireNonNull(cloned.getWorld(), "Spawn world cannot be null");
|
||||||
|
copy.add(cloned);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.locations = List.copyOf(copy);
|
||||||
/**
|
}
|
||||||
* Returns the spawn location if enabled.
|
|
||||||
*/
|
|
||||||
public Optional<Location> getLocation() {
|
|
||||||
return Optional.ofNullable(enabled ? location : null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -39,9 +40,74 @@ public record PlayerSpawnConfig(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates enabled config with location.
|
* Creates enabled config with single location.
|
||||||
*/
|
*/
|
||||||
public static PlayerSpawnConfig at(Location location) {
|
public static PlayerSpawnConfig at(Location location) {
|
||||||
return new PlayerSpawnConfig(true, location);
|
return new PlayerSpawnConfig(true, List.of(location));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates enabled config with multiple locations.
|
||||||
|
*/
|
||||||
|
public static PlayerSpawnConfig at(List<Location> locations) {
|
||||||
|
return new PlayerSpawnConfig(true, locations);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the primary spawn location (first in list) if enabled.
|
||||||
|
*/
|
||||||
|
public Optional<Location> getLocation() {
|
||||||
|
return locations.isEmpty() ? Optional.empty() : Optional.of(locations.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all spawn locations.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Location> locations() {
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if spawn is enabled and has at least one location.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean enabled() {
|
||||||
|
return enabled && !locations.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of configured spawn locations.
|
||||||
|
*/
|
||||||
|
public int count() {
|
||||||
|
return locations.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new config with an additional location.
|
||||||
|
*/
|
||||||
|
public PlayerSpawnConfig addLocation(Location location) {
|
||||||
|
List<Location> newList = new ArrayList<>(locations);
|
||||||
|
newList.add(location);
|
||||||
|
return new PlayerSpawnConfig(true, newList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new config without the location at given index.
|
||||||
|
*/
|
||||||
|
public PlayerSpawnConfig removeLocation(int index) {
|
||||||
|
if (index < 0 || index >= locations.size()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
List<Location> newList = new ArrayList<>(locations);
|
||||||
|
newList.remove(index);
|
||||||
|
return new PlayerSpawnConfig(!newList.isEmpty(), newList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all locations (disables spawn).
|
||||||
|
*/
|
||||||
|
public PlayerSpawnConfig clear() {
|
||||||
|
return disabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,32 +2,33 @@ package dev.loki.lomines.data.config.teleport;
|
||||||
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration for mine teleportation on reset.
|
* Configuration for mine teleportation on reset.
|
||||||
|
* Supports multiple teleport locations for a single mine.
|
||||||
*/
|
*/
|
||||||
public record TeleportConfig(
|
public record TeleportConfig(boolean enabled, List<Location> locations) {
|
||||||
boolean enabled,
|
|
||||||
Location location
|
|
||||||
) {
|
|
||||||
|
|
||||||
public TeleportConfig {
|
public TeleportConfig(boolean enabled, List<Location> locations) {
|
||||||
if (!enabled) {
|
this.enabled = enabled;
|
||||||
location = null;
|
if (!enabled || locations == null) {
|
||||||
} else if (location != null) {
|
this.locations = List.of();
|
||||||
// Defensive copy
|
} else {
|
||||||
location = location.clone();
|
// Defensive copy with validation
|
||||||
Objects.requireNonNull(location.getWorld(), "Teleport world cannot be null");
|
List<Location> copy = new ArrayList<>();
|
||||||
|
for (Location loc : locations) {
|
||||||
|
if (loc != null) {
|
||||||
|
Location cloned = loc.clone();
|
||||||
|
Objects.requireNonNull(cloned.getWorld(), "Teleport world cannot be null");
|
||||||
|
copy.add(cloned);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.locations = List.copyOf(copy);
|
||||||
/**
|
}
|
||||||
* Returns the teleport location if enabled.
|
|
||||||
*/
|
|
||||||
public Optional<Location> getLocation() {
|
|
||||||
return Optional.ofNullable(enabled ? location : null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -38,9 +39,74 @@ public record TeleportConfig(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates enabled config with location.
|
* Creates enabled config with single location.
|
||||||
*/
|
*/
|
||||||
public static TeleportConfig at(Location location) {
|
public static TeleportConfig at(Location location) {
|
||||||
return new TeleportConfig(true, location);
|
return new TeleportConfig(true, List.of(location));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates enabled config with multiple locations.
|
||||||
|
*/
|
||||||
|
public static TeleportConfig at(List<Location> locations) {
|
||||||
|
return new TeleportConfig(true, locations);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the primary teleport location (first in list) if enabled.
|
||||||
|
*/
|
||||||
|
public Optional<Location> getLocation() {
|
||||||
|
return locations.isEmpty() ? Optional.empty() : Optional.of(locations.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all teleport locations.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Location> locations() {
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if teleport is enabled and has at least one location.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean enabled() {
|
||||||
|
return enabled && !locations.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of configured teleport locations.
|
||||||
|
*/
|
||||||
|
public int count() {
|
||||||
|
return locations.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new config with an additional location.
|
||||||
|
*/
|
||||||
|
public TeleportConfig addLocation(Location location) {
|
||||||
|
List<Location> newList = new ArrayList<>(locations);
|
||||||
|
newList.add(location);
|
||||||
|
return new TeleportConfig(true, newList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new config without the location at given index.
|
||||||
|
*/
|
||||||
|
public TeleportConfig removeLocation(int index) {
|
||||||
|
if (index < 0 || index >= locations.size()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
List<Location> newList = new ArrayList<>(locations);
|
||||||
|
newList.remove(index);
|
||||||
|
return new TeleportConfig(!newList.isEmpty(), newList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all locations (disables teleport).
|
||||||
|
*/
|
||||||
|
public TeleportConfig clear() {
|
||||||
|
return disabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package dev.loki.lomines.data.config.ui;
|
package dev.loki.lomines.data.config.ui;
|
||||||
|
|
||||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
import dev.loki.lomines.util.format.color.ColorUtils;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration for UI elements (action bar, messages).
|
* Configuration for UI elements (action bar, messages).
|
||||||
|
|
@ -16,14 +16,11 @@ public record UIConfig(
|
||||||
HologramConfig hologram
|
HologramConfig hologram
|
||||||
) {
|
) {
|
||||||
|
|
||||||
public UIConfig {
|
|
||||||
hologram = hologram != null ? hologram : HologramConfig.disabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String DEFAULT_ACTIONBAR_FORMAT = "<green>{mine}</green> <gray>{percent}%</gray> <dark_gray>({time})";
|
public static final String DEFAULT_ACTIONBAR_FORMAT = "<green>{mine}</green> <gray>{percent}%</gray> <dark_gray>({time})";
|
||||||
public static final String DEFAULT_TIMER_FORMAT = "mm:ss";
|
public static final String DEFAULT_TIMER_FORMAT = "mm:ss";
|
||||||
|
|
||||||
public UIConfig {
|
public UIConfig {
|
||||||
|
hologram = hologram != null ? hologram : HologramConfig.disabled();
|
||||||
actionBarFormat = actionBarFormat != null && !actionBarFormat.isBlank()
|
actionBarFormat = actionBarFormat != null && !actionBarFormat.isBlank()
|
||||||
? actionBarFormat
|
? actionBarFormat
|
||||||
: DEFAULT_ACTIONBAR_FORMAT;
|
: DEFAULT_ACTIONBAR_FORMAT;
|
||||||
|
|
@ -33,19 +30,34 @@ public record UIConfig(
|
||||||
actionBarRange = Math.max(1, actionBarRange);
|
actionBarRange = Math.max(1, actionBarRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default configuration.
|
||||||
|
*/
|
||||||
|
public static UIConfig defaults() {
|
||||||
|
return new UIConfig(true, DEFAULT_ACTIONBAR_FORMAT, 50.0, DEFAULT_TIMER_FORMAT, HologramConfig.defaults());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disabled configuration (no UI).
|
||||||
|
*/
|
||||||
|
public static UIConfig disabled() {
|
||||||
|
return new UIConfig(false, "", 0, DEFAULT_TIMER_FORMAT, HologramConfig.disabled());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the action bar format with placeholders.
|
* Parses the action bar format with placeholders.
|
||||||
* Placeholders: {mine}, {percent}, {time}, {blocks}, {total}
|
* Placeholders: {mine}, {percent}, {time}, {blocks}, {total}
|
||||||
|
* Supports colors: &#RRGGBB, &a..&f, <color>
|
||||||
*/
|
*/
|
||||||
public Component formatActionBar(String mineName, double percent, String timeStr, int blocks, int total) {
|
public Component formatActionBar(String mineName, double percent, String timeStr, int blocks, int total) {
|
||||||
String parsed = actionBarFormat
|
String parsed = actionBarFormat
|
||||||
.replace("{mine}", mineName)
|
.replace("{mine}", mineName)
|
||||||
.replace("{percent}", String.format("%.1f", percent))
|
.replace("{percent}", String.format(Locale.US, "%.1f", percent))
|
||||||
.replace("{time}", timeStr)
|
.replace("{time}", timeStr)
|
||||||
.replace("{blocks}", String.valueOf(blocks))
|
.replace("{blocks}", String.valueOf(blocks))
|
||||||
.replace("{total}", String.valueOf(total));
|
.replace("{total}", String.valueOf(total));
|
||||||
|
|
||||||
return MiniMessage.miniMessage().deserialize(parsed);
|
return ColorUtils.format(parsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -70,18 +82,4 @@ public record UIConfig(
|
||||||
public double actionBarRangeSquared() {
|
public double actionBarRangeSquared() {
|
||||||
return actionBarRange * actionBarRange;
|
return actionBarRange * actionBarRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Default configuration.
|
|
||||||
*/
|
|
||||||
public static UIConfig defaults() {
|
|
||||||
return new UIConfig(true, DEFAULT_ACTIONBAR_FORMAT, 50.0, DEFAULT_TIMER_FORMAT, HologramConfig.defaults());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disabled configuration (no UI).
|
|
||||||
*/
|
|
||||||
public static UIConfig disabled() {
|
|
||||||
return new UIConfig(false, "", 0, DEFAULT_TIMER_FORMAT, HologramConfig.disabled());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.loki.lomines.data.reward;
|
package dev.loki.lomines.data.reward.entity;
|
||||||
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
@ -49,8 +49,12 @@ public record Reward(List<Material> materials, double chance, List<ItemStack> it
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Reward reward = (Reward) o;
|
Reward reward = (Reward) o;
|
||||||
return Double.compare(reward.chance, chance) == 0 &&
|
return Double.compare(reward.chance, chance) == 0 &&
|
||||||
preventDrops == reward.preventDrops &&
|
preventDrops == reward.preventDrops &&
|
||||||
|
|
@ -59,4 +63,8 @@ public record Reward(List<Material> materials, double chance, List<ItemStack> it
|
||||||
Objects.equals(commands, reward.commands);
|
Objects.equals(commands, reward.commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(materials, chance, items, commands, preventDrops);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,69 +1,29 @@
|
||||||
package dev.loki.lomines.data.reward;
|
package dev.loki.lomines.data.reward.parse;
|
||||||
|
|
||||||
import dev.loki.lomines.data.config.parser.ConfigParseException;
|
import dev.loki.lomines.data.config.parser.ConfigParseException;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import dev.loki.lomines.data.reward.entity.Reward;
|
||||||
|
import dev.loki.lomines.data.reward.parse.command.RewardCommandParser;
|
||||||
|
import dev.loki.lomines.data.reward.parse.item.RewardItemParser;
|
||||||
|
import dev.loki.lomines.data.reward.parse.item.RewardMaterialParser;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
public final class RewardEntryParser {
|
||||||
* Parses reward configurations from YAML.
|
|
||||||
* Handles reward chance, materials, items, commands, and prevent-drops settings.
|
|
||||||
*/
|
|
||||||
public final class RewardParser {
|
|
||||||
|
|
||||||
private final RewardItemParser itemParser;
|
private final RewardItemParser itemParser;
|
||||||
private final RewardMaterialParser materialParser;
|
private final RewardMaterialParser materialParser;
|
||||||
private final RewardCommandParser commandParser;
|
private final RewardCommandParser commandParser;
|
||||||
|
|
||||||
public RewardParser() {
|
public RewardEntryParser() {
|
||||||
this.itemParser = new RewardItemParser();
|
this.itemParser = new RewardItemParser();
|
||||||
this.materialParser = new RewardMaterialParser();
|
this.materialParser = new RewardMaterialParser();
|
||||||
this.commandParser = new RewardCommandParser();
|
this.commandParser = new RewardCommandParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Reward> parseRewards(YamlConfiguration yaml) throws ConfigParseException {
|
public Reward parseReward(Map<String, Object> rewardMap) throws ConfigParseException {
|
||||||
List<Reward> rewards = new ArrayList<>();
|
|
||||||
|
|
||||||
if (!yaml.contains("random-rewards")) {
|
|
||||||
return rewards;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<?> rewardsList = yaml.getList("random-rewards");
|
|
||||||
if (rewardsList == null) {
|
|
||||||
return rewards;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < rewardsList.size(); i++) {
|
|
||||||
Object rewardObj = rewardsList.get(i);
|
|
||||||
|
|
||||||
if (!(rewardObj instanceof Map)) {
|
|
||||||
throw new ConfigParseException(
|
|
||||||
"Invalid reward at index " + i + ": expected map, got " +
|
|
||||||
(rewardObj != null ? rewardObj.getClass().getSimpleName() : "null")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Map<String, Object> rewardMap = (Map<String, Object>) rewardObj;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Reward reward = parseReward(rewardMap);
|
|
||||||
rewards.add(reward);
|
|
||||||
} catch (ConfigParseException e) {
|
|
||||||
throw new ConfigParseException(
|
|
||||||
"Error parsing reward at index " + i + ": " + e.getMessage(),
|
|
||||||
e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rewards;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Reward parseReward(Map<String, Object> rewardMap) throws ConfigParseException {
|
|
||||||
double chance = parseRewardField(rewardMap, "chance", Double.class);
|
double chance = parseRewardField(rewardMap, "chance", Double.class);
|
||||||
boolean preventDrops = parseRewardField(rewardMap, "prevent-drops", Boolean.class, false);
|
boolean preventDrops = parseRewardField(rewardMap, "prevent-drops", Boolean.class, false);
|
||||||
|
|
||||||
|
|
@ -75,7 +35,7 @@ public final class RewardParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T> T parseRewardField(Map<String, Object> map, String key, Class<T> type)
|
public <T> T parseRewardField(Map<String, Object> map, String key, Class<T> type)
|
||||||
throws ConfigParseException {
|
throws ConfigParseException {
|
||||||
if (!map.containsKey(key)) {
|
if (!map.containsKey(key)) {
|
||||||
throw new ConfigParseException("Missing required '" + key + "' field in reward");
|
throw new ConfigParseException("Missing required '" + key + "' field in reward");
|
||||||
|
|
@ -96,7 +56,7 @@ public final class RewardParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T> T parseRewardField(Map<String, Object> map, String key, Class<T> type, T defaultValue) {
|
public <T> T parseRewardField(Map<String, Object> map, String key, Class<T> type, T defaultValue) {
|
||||||
if (!map.containsKey(key)) {
|
if (!map.containsKey(key)) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
@ -107,7 +67,7 @@ public final class RewardParser {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Double parseDoubleValue(String key, Object value) throws ConfigParseException {
|
public Double parseDoubleValue(String key, Object value) throws ConfigParseException {
|
||||||
try {
|
try {
|
||||||
if (value instanceof Number) {
|
if (value instanceof Number) {
|
||||||
return ((Number) value).doubleValue();
|
return ((Number) value).doubleValue();
|
||||||
|
|
@ -124,7 +84,7 @@ public final class RewardParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ItemStack> parseRewardItems(Map<String, Object> rewardMap)
|
public List<ItemStack> parseRewardItems(Map<String, Object> rewardMap)
|
||||||
throws ConfigParseException {
|
throws ConfigParseException {
|
||||||
List<ItemStack> items = new ArrayList<>();
|
List<ItemStack> items = new ArrayList<>();
|
||||||
if (!rewardMap.containsKey("items")) {
|
if (!rewardMap.containsKey("items")) {
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package dev.loki.lomines.data.reward.parse;
|
||||||
|
|
||||||
|
import dev.loki.lomines.data.config.parser.ConfigParseException;
|
||||||
|
import dev.loki.lomines.data.reward.entity.Reward;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class RewardParser {
|
||||||
|
|
||||||
|
private final RewardEntryParser entryParser;
|
||||||
|
|
||||||
|
public RewardParser() {
|
||||||
|
this.entryParser = new RewardEntryParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Reward> parseRewards(YamlConfiguration yaml) throws ConfigParseException {
|
||||||
|
List<Reward> rewards = new ArrayList<>();
|
||||||
|
|
||||||
|
if (!yaml.contains("random-rewards")) {
|
||||||
|
return rewards;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<?> rewardsList = yaml.getList("random-rewards");
|
||||||
|
if (rewardsList == null) {
|
||||||
|
return rewards;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < rewardsList.size(); i++) {
|
||||||
|
rewards.add(parseRewardAt(rewardsList.get(i), i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rewards;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Reward parseRewardAt(Object rewardObj, int i) throws ConfigParseException {
|
||||||
|
if (!(rewardObj instanceof Map)) {
|
||||||
|
throw new ConfigParseException(
|
||||||
|
"Invalid reward at index " + i + ": expected map, got " +
|
||||||
|
(rewardObj != null ? rewardObj.getClass().getSimpleName() : "null")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> rewardMap = (Map<String, Object>) rewardObj;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return entryParser.parseReward(rewardMap);
|
||||||
|
} catch (ConfigParseException e) {
|
||||||
|
throw new ConfigParseException(
|
||||||
|
"Error parsing reward at index " + i + ": " + e.getMessage(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.loki.lomines.data.reward;
|
package dev.loki.lomines.data.reward.parse.command;
|
||||||
|
|
||||||
import dev.loki.lomines.data.config.parser.ConfigParseException;
|
import dev.loki.lomines.data.config.parser.ConfigParseException;
|
||||||
|
|
||||||
|
|
@ -9,10 +9,10 @@ import java.util.Map;
|
||||||
/**
|
/**
|
||||||
* Parses reward commands from configuration.
|
* Parses reward commands from configuration.
|
||||||
*/
|
*/
|
||||||
final class RewardCommandParser {
|
public final class RewardCommandParser {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
List<String> parseCommands(Map<String, Object> rewardMap) throws ConfigParseException {
|
public List<String> parseCommands(Map<String, Object> rewardMap) throws ConfigParseException {
|
||||||
List<String> commands = new ArrayList<>();
|
List<String> commands = new ArrayList<>();
|
||||||
if (!rewardMap.containsKey("commands")) {
|
if (!rewardMap.containsKey("commands")) {
|
||||||
return commands;
|
return commands;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.loki.lomines.data.reward;
|
package dev.loki.lomines.data.reward.parse.item;
|
||||||
|
|
||||||
import dev.loki.lomines.data.config.parser.ConfigParseException;
|
import dev.loki.lomines.data.config.parser.ConfigParseException;
|
||||||
import dev.loki.lomines.util.ValidationUtils;
|
import dev.loki.lomines.util.ValidationUtils;
|
||||||
|
|
@ -12,7 +12,7 @@ import java.util.Map;
|
||||||
* Parses ItemStack objects from YAML reward item configurations.
|
* Parses ItemStack objects from YAML reward item configurations.
|
||||||
* Handles material type, amount, and basic item properties.
|
* Handles material type, amount, and basic item properties.
|
||||||
*/
|
*/
|
||||||
final class RewardItemParser {
|
public final class RewardItemParser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a single reward item from a map configuration.
|
* Parses a single reward item from a map configuration.
|
||||||
|
|
@ -22,7 +22,7 @@ final class RewardItemParser {
|
||||||
* @return the parsed ItemStack
|
* @return the parsed ItemStack
|
||||||
* @throws ConfigParseException if the item configuration is invalid
|
* @throws ConfigParseException if the item configuration is invalid
|
||||||
*/
|
*/
|
||||||
ItemStack parseItem(Map<String, Object> itemMap, int index) throws ConfigParseException {
|
public ItemStack parseItem(Map<String, Object> itemMap, int index) throws ConfigParseException {
|
||||||
if (!itemMap.containsKey("type")) {
|
if (!itemMap.containsKey("type")) {
|
||||||
throw new ConfigParseException("Missing required 'type' field in item at index " + index);
|
throw new ConfigParseException("Missing required 'type' field in item at index " + index);
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.loki.lomines.data.reward;
|
package dev.loki.lomines.data.reward.parse.item;
|
||||||
|
|
||||||
import dev.loki.lomines.data.config.parser.ConfigParseException;
|
import dev.loki.lomines.data.config.parser.ConfigParseException;
|
||||||
import dev.loki.lomines.util.ValidationUtils;
|
import dev.loki.lomines.util.ValidationUtils;
|
||||||
|
|
@ -11,10 +11,10 @@ import java.util.Map;
|
||||||
/**
|
/**
|
||||||
* Parses reward materials (blocks) from configuration.
|
* Parses reward materials (blocks) from configuration.
|
||||||
*/
|
*/
|
||||||
final class RewardMaterialParser {
|
public final class RewardMaterialParser {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
List<Material> parseMaterials(Map<String, Object> rewardMap) throws ConfigParseException {
|
public List<Material> parseMaterials(Map<String, Object> rewardMap) throws ConfigParseException {
|
||||||
if (!rewardMap.containsKey("blocks")) {
|
if (!rewardMap.containsKey("blocks")) {
|
||||||
throw new ConfigParseException("Missing required 'blocks' field in reward");
|
throw new ConfigParseException("Missing required 'blocks' field in reward");
|
||||||
}
|
}
|
||||||
|
|
@ -1,181 +0,0 @@
|
||||||
package dev.loki.lomines.data.stats;
|
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
|
||||||
import dev.lolib.scheduler.ScheduledTask;
|
|
||||||
import dev.lolib.scheduler.Scheduler;
|
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages player statistics for all mines.
|
|
||||||
* Handles loading, saving, and auto-saving of player statistics.
|
|
||||||
*/
|
|
||||||
public final class StatsManager {
|
|
||||||
|
|
||||||
private final Map<UUID, PlayerStats> stats = new ConcurrentHashMap<>();
|
|
||||||
private final LoMinesPlugin plugin;
|
|
||||||
private final Path statsFile;
|
|
||||||
private final Leaderboard leaderboard;
|
|
||||||
private ScheduledTask saveTask;
|
|
||||||
|
|
||||||
public StatsManager(LoMinesPlugin plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.statsFile = plugin.getDataFolder().toPath().resolve("stats.yml");
|
|
||||||
this.leaderboard = new Leaderboard(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads statistics from file.
|
|
||||||
*/
|
|
||||||
public void load() throws IOException {
|
|
||||||
if (!Files.exists(statsFile)) {
|
|
||||||
plugin.loLogger().info("Stats file does not exist, starting with empty statistics");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
File file = statsFile.toFile();
|
|
||||||
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file);
|
|
||||||
|
|
||||||
ConfigurationSection playersSection = yaml.getConfigurationSection("players");
|
|
||||||
if (playersSection == null) {
|
|
||||||
plugin.loLogger().info("No players section found in stats.yml");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String uuidString : playersSection.getKeys(false)) {
|
|
||||||
try {
|
|
||||||
UUID playerId = UUID.fromString(uuidString);
|
|
||||||
ConfigurationSection playerSection = playersSection.getConfigurationSection(uuidString);
|
|
||||||
|
|
||||||
if (playerSection == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerStats playerStats = getOrCreate(playerId);
|
|
||||||
|
|
||||||
// Load total blocks
|
|
||||||
long totalBlocks = playerSection.getLong("total", 0);
|
|
||||||
playerStats.setTotalBlocks(totalBlocks);
|
|
||||||
|
|
||||||
// Load mine-specific stats
|
|
||||||
ConfigurationSection minesSection = playerSection.getConfigurationSection("mines");
|
|
||||||
if (minesSection != null) {
|
|
||||||
for (String mineName : minesSection.getKeys(false)) {
|
|
||||||
long mineBlocks = minesSection.getLong(mineName, 0);
|
|
||||||
playerStats.setMineBlocks(mineName, mineBlocks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
plugin.loLogger().warn("Invalid UUID in stats.yml: " + uuidString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
plugin.loLogger().info("Loaded statistics for " + stats.size() + " player(s)");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves statistics to file.
|
|
||||||
*/
|
|
||||||
public void save() throws IOException {
|
|
||||||
YamlConfiguration yaml = new YamlConfiguration();
|
|
||||||
|
|
||||||
for (Map.Entry<UUID, PlayerStats> entry : stats.entrySet()) {
|
|
||||||
UUID playerId = entry.getKey();
|
|
||||||
PlayerStats playerStats = entry.getValue();
|
|
||||||
|
|
||||||
String path = "players." + playerId.toString();
|
|
||||||
|
|
||||||
// Save total blocks
|
|
||||||
yaml.set(path + ".total", playerStats.getTotalBlocks());
|
|
||||||
|
|
||||||
// Save mine-specific stats
|
|
||||||
Map<String, Long> mineStats = playerStats.getMineStatsSnapshot();
|
|
||||||
if (!mineStats.isEmpty()) {
|
|
||||||
for (Map.Entry<String, Long> mineEntry : mineStats.entrySet()) {
|
|
||||||
yaml.set(path + ".mines." + mineEntry.getKey(), mineEntry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure parent directory exists
|
|
||||||
if (!Files.exists(statsFile.getParent())) {
|
|
||||||
Files.createDirectories(statsFile.getParent());
|
|
||||||
}
|
|
||||||
|
|
||||||
yaml.save(statsFile.toFile());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts auto-save task.
|
|
||||||
*/
|
|
||||||
public void startAutoSave() {
|
|
||||||
// Auto-save every 5 minutes (20 ticks/second * 60 seconds * 5 minutes)
|
|
||||||
long interval = 20L * 60L * 5L;
|
|
||||||
|
|
||||||
saveTask = Scheduler.get(plugin).runTimerAsync(
|
|
||||||
() -> {
|
|
||||||
try {
|
|
||||||
save();
|
|
||||||
plugin.loLogger().info("Statistics auto-saved");
|
|
||||||
} catch (IOException e) {
|
|
||||||
plugin.loLogger().error("Failed to auto-save statistics: " + e.getMessage());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
interval,
|
|
||||||
interval
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops auto-save task.
|
|
||||||
*/
|
|
||||||
public void stopAutoSave() {
|
|
||||||
if (saveTask != null) {
|
|
||||||
saveTask.cancel();
|
|
||||||
saveTask = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets or creates player statistics.
|
|
||||||
*/
|
|
||||||
public PlayerStats getOrCreate(UUID playerId) {
|
|
||||||
return stats.computeIfAbsent(playerId, PlayerStats::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increments block count for a player in a specific mine.
|
|
||||||
*/
|
|
||||||
public void incrementBlocks(UUID playerId, String mineName) {
|
|
||||||
PlayerStats playerStats = getOrCreate(playerId);
|
|
||||||
playerStats.incrementTotal();
|
|
||||||
playerStats.incrementMine(mineName);
|
|
||||||
|
|
||||||
// Invalidate leaderboard cache
|
|
||||||
leaderboard.invalidateCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all player statistics.
|
|
||||||
*/
|
|
||||||
public Collection<PlayerStats> getAllStats() {
|
|
||||||
return Collections.unmodifiableCollection(stats.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the leaderboard instance.
|
|
||||||
*/
|
|
||||||
public Leaderboard getLeaderboard() {
|
|
||||||
return leaderboard;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
package dev.loki.lomines.data.stats;
|
package dev.loki.lomines.data.stats.model;
|
||||||
|
|
||||||
|
import dev.loki.lomines.data.stats.service.StatsManager;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.loki.lomines.data.stats;
|
package dev.loki.lomines.data.stats.model;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -16,12 +16,21 @@ public record LeaderboardEntry(UUID playerId, long count) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
LeaderboardEntry that = (LeaderboardEntry) o;
|
LeaderboardEntry that = (LeaderboardEntry) o;
|
||||||
return count == that.count && playerId.equals(that.playerId);
|
return count == that.count && playerId.equals(that.playerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(playerId, count);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "LeaderboardEntry{playerId=" + playerId + ", count=" + count + "}";
|
return "LeaderboardEntry{playerId=" + playerId + ", count=" + count + "}";
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.loki.lomines.data.stats;
|
package dev.loki.lomines.data.stats.model;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
package dev.loki.lomines.data.stats.service;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import dev.loki.lomines.data.stats.model.Leaderboard;
|
||||||
|
import dev.loki.lomines.data.stats.model.PlayerStats;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public final class StatsManager {
|
||||||
|
|
||||||
|
private final Map<UUID, PlayerStats> stats = new ConcurrentHashMap<>();
|
||||||
|
private final LoMinesPlugin plugin;
|
||||||
|
private final Leaderboard leaderboard;
|
||||||
|
private final StatsPersistence persistence;
|
||||||
|
|
||||||
|
public StatsManager(LoMinesPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.leaderboard = new Leaderboard(this);
|
||||||
|
this.persistence = new StatsPersistence(plugin, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatsPersistence getPersistence() {
|
||||||
|
return persistence;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load() throws IOException {
|
||||||
|
persistence.load(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save() throws IOException {
|
||||||
|
persistence.save(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startAutoSave() {
|
||||||
|
persistence.startAutoSave(() -> {
|
||||||
|
try {
|
||||||
|
save();
|
||||||
|
plugin.getLogger().info("Statistics auto-saved");
|
||||||
|
} catch (IOException e) {
|
||||||
|
plugin.getLogger().severe("Failed to auto-save statistics: " + e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopAutoSave() {
|
||||||
|
persistence.stopAutoSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerStats getOrCreate(UUID playerId) {
|
||||||
|
return stats.computeIfAbsent(playerId, PlayerStats::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementBlocks(UUID playerId, String mineName) {
|
||||||
|
PlayerStats playerStats = getOrCreate(playerId);
|
||||||
|
playerStats.incrementTotal();
|
||||||
|
playerStats.incrementMine(mineName);
|
||||||
|
leaderboard.invalidateCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<PlayerStats> getAllStats() {
|
||||||
|
return Collections.unmodifiableCollection(stats.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Leaderboard getLeaderboard() {
|
||||||
|
return leaderboard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
package dev.loki.lomines.data.stats.service;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import dev.loki.lomines.data.stats.model.PlayerStats;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public final class StatsPersistence {
|
||||||
|
|
||||||
|
private final LoMinesPlugin plugin;
|
||||||
|
private final Path statsFile;
|
||||||
|
private final StatsManager statsManager;
|
||||||
|
private BukkitTask saveTask;
|
||||||
|
|
||||||
|
public StatsPersistence(LoMinesPlugin plugin, StatsManager statsManager) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.statsFile = plugin.getDataFolder().toPath().resolve("stats.yml");
|
||||||
|
this.statsManager = statsManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load(Map<UUID, PlayerStats> stats) throws IOException {
|
||||||
|
if (!Files.exists(statsFile)) {
|
||||||
|
plugin.getLogger().info("Stats file does not exist, starting with empty statistics");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File file = statsFile.toFile();
|
||||||
|
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file);
|
||||||
|
|
||||||
|
ConfigurationSection playersSection = yaml.getConfigurationSection("players");
|
||||||
|
if (playersSection == null) {
|
||||||
|
plugin.getLogger().info("No players section found in stats.yml");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String uuidString : playersSection.getKeys(false)) {
|
||||||
|
loadPlayer(stats, uuidString, playersSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.getLogger().info("Loaded statistics for " + stats.size() + " player(s)");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPlayer(Map<UUID, PlayerStats> stats, String uuidString, ConfigurationSection playersSection) {
|
||||||
|
try {
|
||||||
|
UUID playerId = UUID.fromString(uuidString);
|
||||||
|
ConfigurationSection playerSection = playersSection.getConfigurationSection(uuidString);
|
||||||
|
|
||||||
|
if (playerSection == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerStats playerStats = stats.computeIfAbsent(playerId, PlayerStats::new);
|
||||||
|
|
||||||
|
long totalBlocks = playerSection.getLong("total", 0);
|
||||||
|
playerStats.setTotalBlocks(totalBlocks);
|
||||||
|
|
||||||
|
ConfigurationSection minesSection = playerSection.getConfigurationSection("mines");
|
||||||
|
if (minesSection != null) {
|
||||||
|
for (String mineName : minesSection.getKeys(false)) {
|
||||||
|
long mineBlocks = minesSection.getLong(mineName, 0);
|
||||||
|
playerStats.setMineBlocks(mineName, mineBlocks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
plugin.getLogger().warning("Invalid UUID in stats.yml: " + uuidString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(Map<UUID, PlayerStats> stats) throws IOException {
|
||||||
|
YamlConfiguration yaml = new YamlConfiguration();
|
||||||
|
|
||||||
|
for (Map.Entry<UUID, PlayerStats> entry : stats.entrySet()) {
|
||||||
|
UUID playerId = entry.getKey();
|
||||||
|
PlayerStats playerStats = entry.getValue();
|
||||||
|
|
||||||
|
String path = "players." + playerId.toString();
|
||||||
|
yaml.set(path + ".total", playerStats.getTotalBlocks());
|
||||||
|
|
||||||
|
Map<String, Long> mineStats = playerStats.getMineStatsSnapshot();
|
||||||
|
if (!mineStats.isEmpty()) {
|
||||||
|
for (Map.Entry<String, Long> mineEntry : mineStats.entrySet()) {
|
||||||
|
yaml.set(path + ".mines." + mineEntry.getKey(), mineEntry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Files.exists(statsFile.getParent())) {
|
||||||
|
Files.createDirectories(statsFile.getParent());
|
||||||
|
}
|
||||||
|
|
||||||
|
yaml.save(statsFile.toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startAutoSave(Runnable saveAction) {
|
||||||
|
long interval = 20L * 60L * 5L;
|
||||||
|
saveTask = Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, saveAction, interval, interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopAutoSave() {
|
||||||
|
if (saveTask != null) {
|
||||||
|
saveTask.cancel();
|
||||||
|
saveTask = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,11 +13,12 @@ import java.util.List;
|
||||||
/**
|
/**
|
||||||
* Factory for creating ItemStacks with name and lore.
|
* Factory for creating ItemStacks with name and lore.
|
||||||
*/
|
*/
|
||||||
final class ItemStackFactory {
|
public final class ItemStackFactory {
|
||||||
|
|
||||||
private ItemStackFactory() {}
|
private ItemStackFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
static ItemStack create(Material mat, String name, String... loreLines) {
|
public static ItemStack create(Material mat, String name, String... loreLines) {
|
||||||
ItemStack stack = new ItemStack(mat);
|
ItemStack stack = new ItemStack(mat);
|
||||||
ItemMeta meta = stack.getItemMeta();
|
ItemMeta meta = stack.getItemMeta();
|
||||||
if (meta != null) {
|
if (meta != null) {
|
||||||
|
|
@ -32,17 +33,21 @@ final class ItemStackFactory {
|
||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
static String loc(Location l) {
|
public static String loc(Location l) {
|
||||||
if (l == null || l.getWorld() == null) return "§7не задана";
|
if (l == null || l.getWorld() == null) {
|
||||||
|
return "§7не задана";
|
||||||
|
}
|
||||||
return String.format("§7%.0f§8/§7%.0f§8/§7%.0f", l.getX(), l.getY(), l.getZ());
|
return String.format("§7%.0f§8/§7%.0f§8/§7%.0f", l.getX(), l.getY(), l.getZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
static String trunc(String s, int max) {
|
public static String trunc(String s, int max) {
|
||||||
if (s == null || s.isEmpty()) return "§7нет";
|
if (s == null || s.isEmpty()) {
|
||||||
|
return "§7нет";
|
||||||
|
}
|
||||||
return s.length() > max ? s.substring(0, max - 3) + "..." : s;
|
return s.length() > max ? s.substring(0, max - 3) + "..." : s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ItemStack filler() {
|
public static ItemStack filler() {
|
||||||
ItemStack pane = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);
|
ItemStack pane = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);
|
||||||
ItemMeta meta = pane.getItemMeta();
|
ItemMeta meta = pane.getItemMeta();
|
||||||
if (meta != null) {
|
if (meta != null) {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
/**
|
|
||||||
* GUI editors for mine configuration.
|
|
||||||
* <p>
|
|
||||||
* This package contains graphical user interface implementations using LoAPI's InventoryGUI:
|
|
||||||
* <ul>
|
|
||||||
* <li>{@code MinesEditor} - List view of all mines</li>
|
|
||||||
* <li>{@code MineEditor} - Main menu for a specific mine</li>
|
|
||||||
* <li>{@code MineContentsEditor} - Editor for mine block contents</li>
|
|
||||||
* <li>{@code MineRewardsEditor} - List view of mine rewards</li>
|
|
||||||
* <li>{@code MineRewardEditor} - Editor for a specific reward</li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
package dev.loki.lomines.gui;
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package dev.loki.lomines.gui.group;
|
package dev.loki.lomines.gui.group;
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.core.mine.Mines;
|
import dev.loki.lomines.core.mine.registry.Mines;
|
||||||
import dev.loki.lomines.wand.group.GroupWandManager;
|
import dev.loki.lomines.wand.group.GroupWandManager;
|
||||||
import dev.loki.lomines.wand.group.GroupWandSession;
|
import dev.loki.lomines.wand.group.GroupWandSession;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
|
|
@ -20,7 +20,8 @@ public final class GroupCreateGui {
|
||||||
private static final int SIZE = 54;
|
private static final int SIZE = 54;
|
||||||
private static final int[] MINE_SLOTS = {10, 11, 12, 19, 20, 21, 28, 29, 30};
|
private static final int[] MINE_SLOTS = {10, 11, 12, 19, 20, 21, 28, 29, 30};
|
||||||
|
|
||||||
private GroupCreateGui() {}
|
private GroupCreateGui() {
|
||||||
|
}
|
||||||
|
|
||||||
public static void open(LoMinesPlugin plugin, Player player) {
|
public static void open(LoMinesPlugin plugin, Player player) {
|
||||||
GroupCreateGuiHolder holder = new GroupCreateGuiHolder(player.getUniqueId());
|
GroupCreateGuiHolder holder = new GroupCreateGuiHolder(player.getUniqueId());
|
||||||
|
|
@ -93,12 +94,28 @@ public final class GroupCreateGui {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CreationResult result = createSlots(plugin, player, session, base);
|
||||||
|
if (result.created() == 0) {
|
||||||
|
player.sendMessage(Component.text("Нет готовых слотов (2 точки в каждом).", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
player.sendMessage(Component.text(
|
||||||
|
"Создано шахт: " + result.created() + (result.skipped() > 0 ? " (пропущено: " + result.skipped() + ")" : ""),
|
||||||
|
NamedTextColor.GREEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
private record CreationResult(int created, int skipped) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CreationResult createSlots(LoMinesPlugin plugin, Player player, GroupWandSession session, String base) {
|
||||||
Mines mines = plugin.getMines();
|
Mines mines = plugin.getMines();
|
||||||
int created = 0;
|
int created = 0;
|
||||||
int skipped = 0;
|
int skipped = 0;
|
||||||
|
|
||||||
for (int i = 0; i < 9; i++) {
|
for (int i = 0; i < 9; i++) {
|
||||||
if (!session.isSlotReady(i)) continue;
|
if (!session.isSlotReady(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
String mineName = base + "_" + (i + 1);
|
String mineName = base + "_" + (i + 1);
|
||||||
try {
|
try {
|
||||||
mines.create(mineName, session.getPos1(i), session.getPos2(i));
|
mines.create(mineName, session.getPos1(i), session.getPos2(i));
|
||||||
|
|
@ -108,16 +125,10 @@ public final class GroupCreateGui {
|
||||||
skipped++;
|
skipped++;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
player.sendMessage(Component.text("Ошибка " + mineName + ": " + e.getMessage(), NamedTextColor.RED));
|
player.sendMessage(Component.text("Ошибка " + mineName + ": " + e.getMessage(), NamedTextColor.RED));
|
||||||
plugin.loLogger().error("Group create failed for " + mineName, e);
|
plugin.getLogger().severe("Group create failed for " + mineName);
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return new CreationResult(created, skipped);
|
||||||
if (created == 0) {
|
|
||||||
player.sendMessage(Component.text("Нет готовых слотов (2 точки в каждом).", NamedTextColor.RED));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
player.sendMessage(Component.text(
|
|
||||||
"Создано шахт: " + created + (skipped > 0 ? " (пропущено: " + skipped + ")" : ""),
|
|
||||||
NamedTextColor.GREEN));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
final class GroupCreateItems {
|
final class GroupCreateItems {
|
||||||
|
|
||||||
private GroupCreateItems() {}
|
private GroupCreateItems() {
|
||||||
|
}
|
||||||
|
|
||||||
static ItemStack filler() {
|
static ItemStack filler() {
|
||||||
ItemStack pane = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);
|
ItemStack pane = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);
|
||||||
|
|
|
||||||
|
|
@ -1,178 +0,0 @@
|
||||||
package dev.loki.lomines.gui.mine.edit;
|
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
|
||||||
import dev.loki.lomines.gui.common.ItemStackFactory;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GUI for selecting a material to add to mine blocks.
|
|
||||||
* Shows paginated list of available materials.
|
|
||||||
*/
|
|
||||||
public final class BlockMaterialSelectionGui {
|
|
||||||
|
|
||||||
private static final int SIZE = 54;
|
|
||||||
private static final String TITLE = "Выберите материал";
|
|
||||||
private static final int SLOT_BACK = 49;
|
|
||||||
private static final int SLOT_PREV = 45;
|
|
||||||
private static final int SLOT_NEXT = 53;
|
|
||||||
private static final int ITEMS_PER_PAGE = 45;
|
|
||||||
|
|
||||||
private static final List<Material> AVAILABLE_MATERIALS = new ArrayList<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
// Initialize with common mine materials
|
|
||||||
for (Material mat : Material.values()) {
|
|
||||||
if (mat.isBlock() && !mat.isAir() && mat.isSolid()) {
|
|
||||||
AVAILABLE_MATERIALS.add(mat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AVAILABLE_MATERIALS.sort((a, b) -> a.name().compareTo(b.name()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private BlockMaterialSelectionGui() {}
|
|
||||||
|
|
||||||
public static void open(LoMinesPlugin plugin, Player player, String mineName) {
|
|
||||||
open(plugin, player, mineName, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void open(LoMinesPlugin plugin, Player player, String mineName, int page) {
|
|
||||||
BlockMaterialSelectionGuiHolder holder = new BlockMaterialSelectionGuiHolder(
|
|
||||||
player.getUniqueId(), mineName, page);
|
|
||||||
Inventory inv = Bukkit.createInventory(holder,
|
|
||||||
Component.text(TITLE + " (стр. " + (page + 1) + ")", NamedTextColor.DARK_GREEN));
|
|
||||||
holder.setInventory(inv);
|
|
||||||
fill(inv, page);
|
|
||||||
player.openInventory(inv);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void fill(Inventory inv, int page) {
|
|
||||||
// Fill background
|
|
||||||
for (int i = 0; i < SIZE; i++) {
|
|
||||||
inv.setItem(i, ItemStackFactory.filler());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate range for this page
|
|
||||||
int start = page * ITEMS_PER_PAGE;
|
|
||||||
int end = Math.min(start + ITEMS_PER_PAGE, AVAILABLE_MATERIALS.size());
|
|
||||||
|
|
||||||
// Fill materials
|
|
||||||
for (int i = 0; i < ITEMS_PER_PAGE && (start + i) < end; i++) {
|
|
||||||
Material mat = AVAILABLE_MATERIALS.get(start + i);
|
|
||||||
inv.setItem(i, materialItem(mat));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Navigation buttons
|
|
||||||
if (page > 0) {
|
|
||||||
inv.setItem(SLOT_PREV, prevPageItem());
|
|
||||||
}
|
|
||||||
if (end < AVAILABLE_MATERIALS.size()) {
|
|
||||||
inv.setItem(SLOT_NEXT, nextPageItem());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Back button
|
|
||||||
inv.setItem(SLOT_BACK, backItem());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ItemStack materialItem(Material material) {
|
|
||||||
String name = formatMaterialName(material);
|
|
||||||
return ItemStackFactory.create(material, "§a§l" + name,
|
|
||||||
"§8───────────────",
|
|
||||||
"§7Нажмите для добавления",
|
|
||||||
"§7в конфигурацию шахты",
|
|
||||||
"",
|
|
||||||
"§8ID: §7" + material.name().toLowerCase()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String formatMaterialName(Material material) {
|
|
||||||
String name = material.name().toLowerCase().replace("_", " ");
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
for (String word : name.split(" ")) {
|
|
||||||
if (!word.isEmpty()) {
|
|
||||||
result.append(Character.toUpperCase(word.charAt(0)))
|
|
||||||
.append(word.substring(1))
|
|
||||||
.append(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toString().trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ItemStack prevPageItem() {
|
|
||||||
return ItemStackFactory.create(Material.ARROW, "§e§l← Предыдущая",
|
|
||||||
"§8───────────────",
|
|
||||||
"§7Нажмите для перехода",
|
|
||||||
"§7на предыдущую страницу"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ItemStack nextPageItem() {
|
|
||||||
return ItemStackFactory.create(Material.ARROW, "§e§lСледующая →",
|
|
||||||
"§8───────────────",
|
|
||||||
"§7Нажмите для перехода",
|
|
||||||
"§7на следующую страницу"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ItemStack backItem() {
|
|
||||||
return ItemStackFactory.create(Material.BARRIER, "§c§lОтмена",
|
|
||||||
"§8───────────────",
|
|
||||||
"§7Вернуться без добавления"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean handleClick(LoMinesPlugin plugin, Player player, int rawSlot,
|
|
||||||
String mineName, int page, boolean leftClick) {
|
|
||||||
if (rawSlot < 0 || rawSlot >= SIZE) return false;
|
|
||||||
|
|
||||||
if (rawSlot == SLOT_BACK) {
|
|
||||||
BlocksGui.open(plugin, player, mineName);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawSlot == SLOT_PREV && page > 0) {
|
|
||||||
open(plugin, player, mineName, page - 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawSlot == SLOT_NEXT && (page + 1) * ITEMS_PER_PAGE < AVAILABLE_MATERIALS.size()) {
|
|
||||||
open(plugin, player, mineName, page + 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Material selected
|
|
||||||
if (rawSlot < ITEMS_PER_PAGE && leftClick) {
|
|
||||||
int index = page * ITEMS_PER_PAGE + rawSlot;
|
|
||||||
if (index < AVAILABLE_MATERIALS.size()) {
|
|
||||||
Material selected = AVAILABLE_MATERIALS.get(index);
|
|
||||||
addBlockToMine(plugin, player, mineName, selected);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addBlockToMine(LoMinesPlugin plugin, Player player,
|
|
||||||
String mineName, Material material) {
|
|
||||||
player.sendMessage(Component.text("§aДобавлен блок: §f" + material.name().toLowerCase()));
|
|
||||||
player.sendMessage(Component.text("§7Вес установлен: §f10% (настройте в редакторе)"));
|
|
||||||
BlocksGui.open(plugin, player, mineName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Material getMaterialAtSlot(int slot, int page) {
|
|
||||||
int index = page * ITEMS_PER_PAGE + slot;
|
|
||||||
if (index >= 0 && index < AVAILABLE_MATERIALS.size()) {
|
|
||||||
return AVAILABLE_MATERIALS.get(index);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
package dev.loki.lomines.gui.mine.edit;
|
|
||||||
|
|
||||||
import dev.loki.lomines.data.config.reset.ResetConfig;
|
|
||||||
import dev.loki.lomines.gui.common.ItemStackFactory;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Item factory for ResetGui.
|
|
||||||
*/
|
|
||||||
final class ResetGuiItems {
|
|
||||||
|
|
||||||
private ResetGuiItems() {}
|
|
||||||
|
|
||||||
static ItemStack intervalItem(ResetConfig config) {
|
|
||||||
return ItemStackFactory.create(Material.CLOCK, "§a§lИнтервал сброса",
|
|
||||||
"§8───────────────",
|
|
||||||
"§7Текущее: §f" + config.intervalDisplay(),
|
|
||||||
"",
|
|
||||||
"§e▸ ЛКМ §7+1 минута",
|
|
||||||
"§e▸ ПКМ §7-1 минута",
|
|
||||||
"§e▸ Shift+ЛКМ §7+10 минут",
|
|
||||||
"§e▸ Shift+ПКМ §7-10 минут"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ItemStack percentTriggerItem(ResetConfig config) {
|
|
||||||
String status = config.isPercentTriggerEnabled() ? "§aвкл" : "§7выкл";
|
|
||||||
String value = config.isPercentTriggerEnabled() ? config.percentTrigger() + "%" : "—";
|
|
||||||
|
|
||||||
return ItemStackFactory.create(Material.COMPARATOR, "§a§lТриггер по %",
|
|
||||||
"§8───────────────",
|
|
||||||
"§7Статус: " + status,
|
|
||||||
"§7Значение: §f" + value,
|
|
||||||
"",
|
|
||||||
"§7Сброс шахты когда",
|
|
||||||
"§7добыто указанный %",
|
|
||||||
"",
|
|
||||||
"§e▸ ЛКМ §7вкл/выкл",
|
|
||||||
"§e▸ ПКМ §7изменить %"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ItemStack messageItem(ResetConfig config) {
|
|
||||||
String message = config.broadcastMessage().isEmpty() ? "§7(не задано)" : "§f" + config.broadcastMessage();
|
|
||||||
return ItemStackFactory.create(Material.BOOK, "§a§lСообщение при сбросе",
|
|
||||||
"§8───────────────",
|
|
||||||
"§7Текущее:",
|
|
||||||
" " + ItemStackFactory.trunc(message, 30),
|
|
||||||
"",
|
|
||||||
"§7Поддерживает MiniMessage",
|
|
||||||
"§8{mine} §7— название шахты",
|
|
||||||
"§8{player} §7— игрок вызвавший сброс",
|
|
||||||
"",
|
|
||||||
"§e▸ ЛКМ §7изменить сообщение",
|
|
||||||
"§e▸ ПКМ §7очистить"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ItemStack commandsItem(ResetConfig config) {
|
|
||||||
int count = config.commands().size();
|
|
||||||
return ItemStackFactory.create(Material.COMMAND_BLOCK, "§a§lКоманды при сбросе",
|
|
||||||
"§8───────────────",
|
|
||||||
"§7Количество: §f" + count,
|
|
||||||
"",
|
|
||||||
"§7Плейсхолдеры:",
|
|
||||||
"§8{mine} §7— название шахты",
|
|
||||||
"§8{world} §7— мир",
|
|
||||||
"",
|
|
||||||
"§e▸ ЛКМ §7редактировать список"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ItemStack backItem() {
|
|
||||||
return ItemStackFactory.create(Material.ARROW, "§c§lНазад",
|
|
||||||
"§8───────────────",
|
|
||||||
"§7Вернуться в редактор шахты",
|
|
||||||
"",
|
|
||||||
"§e▸ Нажмите для возврата"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
package dev.loki.lomines.gui.mine.edit.blocks.edit;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
|
import dev.loki.lomines.data.config.block.BlockConfig;
|
||||||
|
import dev.loki.lomines.data.config.block.BlockKey;
|
||||||
|
import dev.loki.lomines.gui.mine.edit.blocks.view.BlocksGui;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class BlockWeightEditor {
|
||||||
|
|
||||||
|
private BlockWeightEditor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void adjustBlockWeight(LoMinesPlugin plugin, Player player, String mineName,
|
||||||
|
BlockKey blockKey, boolean leftClick, boolean shiftClick,
|
||||||
|
boolean rightClick) {
|
||||||
|
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
||||||
|
if (mine == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockConfig config = mine.getConfig().blocks();
|
||||||
|
Map<BlockKey, Double> weights = new java.util.HashMap<>(config.weights());
|
||||||
|
double currentWeight = weights.getOrDefault(blockKey, 0.0) * 100.0;
|
||||||
|
|
||||||
|
double newWeight = Math.max(0, Math.min(100,
|
||||||
|
currentWeight + computeDelta(leftClick, shiftClick, rightClick)));
|
||||||
|
|
||||||
|
applyWeightChange(player, blockKey, weights, newWeight, rightClick, shiftClick);
|
||||||
|
|
||||||
|
BlocksGui.refresh(player, player.getOpenInventory().getTopInventory(), mineName, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double computeDelta(boolean leftClick, boolean shiftClick, boolean rightClick) {
|
||||||
|
if (leftClick && shiftClick) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
if (rightClick && shiftClick) {
|
||||||
|
return -1.0;
|
||||||
|
}
|
||||||
|
if (leftClick) {
|
||||||
|
return 5.0;
|
||||||
|
}
|
||||||
|
if (rightClick) {
|
||||||
|
return -5.0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyWeightChange(Player player, BlockKey blockKey, Map<BlockKey, Double> weights,
|
||||||
|
double newWeight, boolean rightClick, boolean shiftClick) {
|
||||||
|
if (rightClick && shiftClick && newWeight <= 0.1) {
|
||||||
|
weights.remove(blockKey);
|
||||||
|
player.sendMessage(Component.text(
|
||||||
|
"\u00a7c\u0411\u043b\u043e\u043a \u00a7f" + blockKey.serialize() + " \u00a7c\u0443\u0434\u0430\u043b\u0435\u043d!"));
|
||||||
|
} else {
|
||||||
|
weights.put(blockKey, newWeight / 100.0);
|
||||||
|
player.sendMessage(Component.text(
|
||||||
|
"\u00a7a\u0412\u0435\u0441 \u00a7f" + blockKey.serialize() + " \u00a7a\u0438\u0437\u043c\u0435\u043d\u0435\u043d: \u00a7f"
|
||||||
|
+ String.format("%.1f%%", newWeight)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockKey getBlockAtSlot(Inventory inv, int slot, LoMinesPlugin plugin, String mineName) {
|
||||||
|
if (slot < 0 || slot >= 36) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
||||||
|
if (mine == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockConfig config = mine.getConfig().blocks();
|
||||||
|
List<Map.Entry<BlockKey, Double>> sortedEntries = new ArrayList<>(config.weights().entrySet());
|
||||||
|
sortedEntries.sort((a, b) -> Double.compare(b.getValue(), a.getValue()));
|
||||||
|
|
||||||
|
if (slot < sortedEntries.size()) {
|
||||||
|
return sortedEntries.get(slot).getKey();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
package dev.loki.lomines.gui.mine.edit.blocks.select;
|
||||||
|
|
||||||
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
|
import dev.loki.lomines.gui.common.ItemStackFactory;
|
||||||
|
import dev.loki.lomines.gui.mine.edit.blocks.view.BlocksGui;
|
||||||
|
import dev.loki.lomines.gui.mine.holder.edit.BlockMaterialSelectionGuiHolder;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class BlockMaterialSelectionGui {
|
||||||
|
|
||||||
|
private static final int SIZE = 54;
|
||||||
|
private static final String TITLE = "Выберите материал";
|
||||||
|
private static final int SLOT_BACK = 49;
|
||||||
|
private static final int SLOT_PREV = 45;
|
||||||
|
private static final int SLOT_NEXT = 53;
|
||||||
|
private static final int ITEMS_PER_PAGE = 45;
|
||||||
|
|
||||||
|
private BlockMaterialSelectionGui() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void open(LoMinesPlugin plugin, Player player, String mineName) {
|
||||||
|
open(plugin, player, mineName, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void open(LoMinesPlugin plugin, Player player, String mineName, int page) {
|
||||||
|
BlockMaterialSelectionGuiHolder holder = new BlockMaterialSelectionGuiHolder(
|
||||||
|
player.getUniqueId(), mineName, page);
|
||||||
|
Inventory inv = Bukkit.createInventory(holder, SIZE,
|
||||||
|
TITLE + " (стр. " + (page + 1) + ")");
|
||||||
|
holder.setInventory(inv);
|
||||||
|
fill(inv, page);
|
||||||
|
player.openInventory(inv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fill(Inventory inv, int page) {
|
||||||
|
List<Material> materials = BlockMaterialSelector.getAvailableMaterials();
|
||||||
|
|
||||||
|
for (int i = 0; i < SIZE; i++) {
|
||||||
|
inv.setItem(i, ItemStackFactory.filler());
|
||||||
|
}
|
||||||
|
|
||||||
|
int start = page * ITEMS_PER_PAGE;
|
||||||
|
int end = Math.min(start + ITEMS_PER_PAGE, materials.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < ITEMS_PER_PAGE && (start + i) < end; i++) {
|
||||||
|
Material mat = materials.get(start + i);
|
||||||
|
inv.setItem(i, BlockMaterialSelector.materialItem(mat));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page > 0) {
|
||||||
|
inv.setItem(SLOT_PREV, BlockMaterialSelector.prevPageItem());
|
||||||
|
}
|
||||||
|
if (end < materials.size()) {
|
||||||
|
inv.setItem(SLOT_NEXT, BlockMaterialSelector.nextPageItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
inv.setItem(SLOT_BACK, BlockMaterialSelector.backItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean handleClick(LoMinesPlugin plugin, Player player, int rawSlot,
|
||||||
|
String mineName, int page, boolean leftClick) {
|
||||||
|
if (rawSlot < 0 || rawSlot >= SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Material> materials = BlockMaterialSelector.getAvailableMaterials();
|
||||||
|
|
||||||
|
if (rawSlot == SLOT_BACK) {
|
||||||
|
BlocksGui.open(plugin, player, mineName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawSlot == SLOT_PREV && page > 0) {
|
||||||
|
open(plugin, player, mineName, page - 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawSlot == SLOT_NEXT && (page + 1) * ITEMS_PER_PAGE < materials.size()) {
|
||||||
|
open(plugin, player, mineName, page + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawSlot < ITEMS_PER_PAGE && leftClick) {
|
||||||
|
int index = page * ITEMS_PER_PAGE + rawSlot;
|
||||||
|
if (index < materials.size()) {
|
||||||
|
Material selected = materials.get(index);
|
||||||
|
player.sendMessage(Component.text("§aДобавлен блок: §f" + selected.name().toLowerCase()));
|
||||||
|
player.sendMessage(Component.text("§7Вес установлен: §f10% (настройте в редакторе)"));
|
||||||
|
BlocksGui.open(plugin, player, mineName);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Material getMaterialAtSlot(int slot, int page) {
|
||||||
|
return BlockMaterialSelector.getMaterialAtSlot(slot, page, ITEMS_PER_PAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
package dev.loki.lomines.gui.mine.edit.blocks.select;
|
||||||
|
|
||||||
|
import dev.loki.lomines.gui.common.ItemStackFactory;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class BlockMaterialSelector {
|
||||||
|
|
||||||
|
private static final List<Material> AVAILABLE_MATERIALS = new ArrayList<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (Material mat : Material.values()) {
|
||||||
|
if (mat.isBlock() && !mat.isAir() && mat.isSolid()) {
|
||||||
|
AVAILABLE_MATERIALS.add(mat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AVAILABLE_MATERIALS.sort((a, b) -> a.name().compareTo(b.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockMaterialSelector() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Material> getAvailableMaterials() {
|
||||||
|
return AVAILABLE_MATERIALS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Material getMaterialAtSlot(int slot, int page, int itemsPerPage) {
|
||||||
|
int index = page * itemsPerPage + slot;
|
||||||
|
if (index >= 0 && index < AVAILABLE_MATERIALS.size()) {
|
||||||
|
return AVAILABLE_MATERIALS.get(index);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatMaterialName(Material material) {
|
||||||
|
String name = material.name().toLowerCase().replace("_", " ");
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
for (String word : name.split(" ")) {
|
||||||
|
if (!word.isEmpty()) {
|
||||||
|
result.append(Character.toUpperCase(word.charAt(0)))
|
||||||
|
.append(word.substring(1))
|
||||||
|
.append(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStack materialItem(Material material) {
|
||||||
|
String name = formatMaterialName(material);
|
||||||
|
return ItemStackFactory.create(material, "\u00a7a\u00a7l" + name,
|
||||||
|
"\u00a78\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
||||||
|
"\u00a77\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u0434\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f",
|
||||||
|
"\u00a77\u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0448\u0430\u0445\u0442\u044b",
|
||||||
|
"",
|
||||||
|
"\u00a78ID: \u00a77" + material.name().toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStack prevPageItem() {
|
||||||
|
return ItemStackFactory.create(Material.ARROW, "\u00a7e\u00a7l\u2190 \u041f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0430\u044f",
|
||||||
|
"\u00a78\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
||||||
|
"\u00a77\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430",
|
||||||
|
"\u00a77\u043d\u0430 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStack nextPageItem() {
|
||||||
|
return ItemStackFactory.create(Material.ARROW, "\u00a7e\u00a7l\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u2192",
|
||||||
|
"\u00a78\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
||||||
|
"\u00a77\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430",
|
||||||
|
"\u00a77\u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStack backItem() {
|
||||||
|
return ItemStackFactory.create(Material.BARRIER, "\u00a7c\u00a7l\u041e\u0442\u043c\u0435\u043d\u0430",
|
||||||
|
"\u00a78\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
||||||
|
"\u00a77\u0412\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f"
|
||||||
|
+ " \u0431\u0435\u0437 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
package dev.loki.lomines.gui.mine.edit;
|
package dev.loki.lomines.gui.mine.edit.blocks.view;
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.core.mine.Mine;
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
import dev.loki.lomines.data.config.block.BlockConfig;
|
import dev.loki.lomines.data.config.block.BlockConfig;
|
||||||
import dev.loki.lomines.data.config.block.BlockKey;
|
import dev.loki.lomines.data.config.block.BlockKey;
|
||||||
import dev.loki.lomines.gui.common.ItemStackFactory;
|
import dev.loki.lomines.gui.common.ItemStackFactory;
|
||||||
import dev.loki.lomines.gui.mine.holder.BlocksGuiHolder;
|
import dev.loki.lomines.gui.mine.edit.blocks.edit.BlockWeightEditor;
|
||||||
|
import dev.loki.lomines.gui.mine.edit.blocks.select.BlockMaterialSelectionGui;
|
||||||
|
import dev.loki.lomines.gui.mine.holder.edit.BlocksGuiHolder;
|
||||||
|
import dev.loki.lomines.gui.mine.main.MineEditGui;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
|
@ -16,9 +19,6 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
|
||||||
* GUI for editing block weights in a mine.
|
|
||||||
*/
|
|
||||||
public final class BlocksGui {
|
public final class BlocksGui {
|
||||||
|
|
||||||
private static final int SIZE = 54;
|
private static final int SIZE = 54;
|
||||||
|
|
@ -26,7 +26,8 @@ public final class BlocksGui {
|
||||||
private static final int SLOT_BACK = 49;
|
private static final int SLOT_BACK = 49;
|
||||||
private static final int SLOT_ADD_BLOCK = 52;
|
private static final int SLOT_ADD_BLOCK = 52;
|
||||||
|
|
||||||
private BlocksGui() {}
|
private BlocksGui() {
|
||||||
|
}
|
||||||
|
|
||||||
public static void open(LoMinesPlugin plugin, Player player, String mineName) {
|
public static void open(LoMinesPlugin plugin, Player player, String mineName) {
|
||||||
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
||||||
|
|
@ -64,7 +65,9 @@ public final class BlocksGui {
|
||||||
|
|
||||||
int slot = 0;
|
int slot = 0;
|
||||||
for (Map.Entry<BlockKey, Double> entry : sortedEntries) {
|
for (Map.Entry<BlockKey, Double> entry : sortedEntries) {
|
||||||
if (slot >= 36) break;
|
if (slot >= 36) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
inv.setItem(slot, BlocksGuiItems.blockItem(entry.getKey(), entry.getValue()));
|
inv.setItem(slot, BlocksGuiItems.blockItem(entry.getKey(), entry.getValue()));
|
||||||
slot++;
|
slot++;
|
||||||
}
|
}
|
||||||
|
|
@ -73,10 +76,13 @@ public final class BlocksGui {
|
||||||
inv.setItem(SLOT_BACK, BlocksGuiItems.backItem());
|
inv.setItem(SLOT_BACK, BlocksGuiItems.backItem());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:ParameterNumber")
|
||||||
public static boolean handleClick(LoMinesPlugin plugin, Player player, int rawSlot,
|
public static boolean handleClick(LoMinesPlugin plugin, Player player, int rawSlot,
|
||||||
String mineName, boolean leftClick, boolean shiftClick,
|
String mineName, boolean leftClick, boolean shiftClick,
|
||||||
boolean rightClick, BlockKey clickedBlock) {
|
boolean rightClick, BlockKey clickedBlock) {
|
||||||
if (rawSlot < 0 || rawSlot >= SIZE) return false;
|
if (rawSlot < 0 || rawSlot >= SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (rawSlot == SLOT_BACK) {
|
if (rawSlot == SLOT_BACK) {
|
||||||
MineEditGui.open(plugin, player, mineName);
|
MineEditGui.open(plugin, player, mineName);
|
||||||
|
|
@ -89,61 +95,14 @@ public final class BlocksGui {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawSlot < 36 && clickedBlock != null) {
|
if (rawSlot < 36 && clickedBlock != null) {
|
||||||
adjustBlockWeight(plugin, player, mineName, clickedBlock, leftClick, shiftClick, rightClick);
|
BlockWeightEditor.adjustBlockWeight(plugin, player, mineName, clickedBlock, leftClick, shiftClick, rightClick);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void adjustBlockWeight(LoMinesPlugin plugin, Player player, String mineName,
|
|
||||||
BlockKey blockKey, boolean leftClick, boolean shiftClick,
|
|
||||||
boolean rightClick) {
|
|
||||||
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
|
||||||
if (mine == null) return;
|
|
||||||
|
|
||||||
BlockConfig config = mine.getConfig().blocks();
|
|
||||||
Map<BlockKey, Double> weights = new java.util.HashMap<>(config.weights());
|
|
||||||
double currentWeight = weights.getOrDefault(blockKey, 0.0) * 100.0;
|
|
||||||
|
|
||||||
double delta = 0;
|
|
||||||
if (leftClick && shiftClick) {
|
|
||||||
delta = 1.0;
|
|
||||||
} else if (rightClick && shiftClick) {
|
|
||||||
delta = -1.0;
|
|
||||||
} else if (leftClick) {
|
|
||||||
delta = 5.0;
|
|
||||||
} else if (rightClick) {
|
|
||||||
delta = -5.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double newWeight = Math.max(0, Math.min(100, currentWeight + delta));
|
|
||||||
|
|
||||||
if (rightClick && shiftClick && newWeight <= 0.1) {
|
|
||||||
weights.remove(blockKey);
|
|
||||||
player.sendMessage(Component.text("§cБлок §f" + blockKey.serialize() + " §cудален!"));
|
|
||||||
} else {
|
|
||||||
weights.put(blockKey, newWeight / 100.0);
|
|
||||||
player.sendMessage(Component.text("§aВес §f" + blockKey.serialize() + " §aизменен: §f" +
|
|
||||||
String.format("%.1f%%", newWeight)));
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh(player, player.getOpenInventory().getTopInventory(), mineName, plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BlockKey getBlockAtSlot(Inventory inv, int slot, LoMinesPlugin plugin, String mineName) {
|
public static BlockKey getBlockAtSlot(Inventory inv, int slot, LoMinesPlugin plugin, String mineName) {
|
||||||
if (slot < 0 || slot >= 36) return null;
|
return BlockWeightEditor.getBlockAtSlot(inv, slot, plugin, mineName);
|
||||||
|
|
||||||
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
|
||||||
if (mine == null) return null;
|
|
||||||
|
|
||||||
BlockConfig config = mine.getConfig().blocks();
|
|
||||||
List<Map.Entry<BlockKey, Double>> sortedEntries = new ArrayList<>(config.weights().entrySet());
|
|
||||||
sortedEntries.sort((a, b) -> Double.compare(b.getValue(), a.getValue()));
|
|
||||||
|
|
||||||
if (slot < sortedEntries.size()) {
|
|
||||||
return sortedEntries.get(slot).getKey();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.loki.lomines.gui.mine.edit;
|
package dev.loki.lomines.gui.mine.edit.blocks.view;
|
||||||
|
|
||||||
import dev.loki.lomines.data.config.block.BlockKey;
|
import dev.loki.lomines.data.config.block.BlockKey;
|
||||||
import dev.loki.lomines.gui.common.ItemStackFactory;
|
import dev.loki.lomines.gui.common.ItemStackFactory;
|
||||||
|
|
@ -10,7 +10,8 @@ import org.bukkit.inventory.ItemStack;
|
||||||
*/
|
*/
|
||||||
final class BlocksGuiItems {
|
final class BlocksGuiItems {
|
||||||
|
|
||||||
private BlocksGuiItems() {}
|
private BlocksGuiItems() {
|
||||||
|
}
|
||||||
|
|
||||||
static ItemStack blockItem(BlockKey key, double weight) {
|
static ItemStack blockItem(BlockKey key, double weight) {
|
||||||
Material material = getMaterialForKey(key);
|
Material material = getMaterialForKey(key);
|
||||||
|
|
@ -50,8 +51,8 @@ final class BlocksGuiItems {
|
||||||
}
|
}
|
||||||
|
|
||||||
static Material getMaterialForKey(BlockKey key) {
|
static Material getMaterialForKey(BlockKey key) {
|
||||||
if (key instanceof BlockKey.Vanilla vanilla) {
|
if (key instanceof BlockKey.Vanilla(Material material)) {
|
||||||
return vanilla.material();
|
return material;
|
||||||
}
|
}
|
||||||
if (key instanceof BlockKey.Oraxen) {
|
if (key instanceof BlockKey.Oraxen) {
|
||||||
return Material.NETHER_STAR;
|
return Material.NETHER_STAR;
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package dev.loki.lomines.gui.mine.edit;
|
package dev.loki.lomines.gui.mine.edit.reset;
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.core.mine.Mine;
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
import dev.loki.lomines.gui.common.ItemStackFactory;
|
import dev.loki.lomines.gui.common.ItemStackFactory;
|
||||||
import dev.loki.lomines.gui.mine.holder.ResetGuiHolder;
|
import dev.loki.lomines.gui.mine.holder.edit.reset.ResetGuiHolder;
|
||||||
import dev.loki.lomines.gui.mine.main.MineEditGui;
|
import dev.loki.lomines.gui.mine.main.MineEditGui;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
|
@ -24,7 +24,8 @@ public final class ResetGui {
|
||||||
private static final int SLOT_MESSAGE = 14;
|
private static final int SLOT_MESSAGE = 14;
|
||||||
private static final int SLOT_COMMANDS = 16;
|
private static final int SLOT_COMMANDS = 16;
|
||||||
|
|
||||||
private ResetGui() {}
|
private ResetGui() {
|
||||||
|
}
|
||||||
|
|
||||||
public static void open(LoMinesPlugin plugin, Player player, String mineName) {
|
public static void open(LoMinesPlugin plugin, Player player, String mineName) {
|
||||||
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
||||||
|
|
@ -66,7 +67,9 @@ public final class ResetGui {
|
||||||
public static boolean handleClick(LoMinesPlugin plugin, Player player, int rawSlot,
|
public static boolean handleClick(LoMinesPlugin plugin, Player player, int rawSlot,
|
||||||
String mineName, boolean leftClick, boolean rightClick,
|
String mineName, boolean leftClick, boolean rightClick,
|
||||||
boolean shiftClick) {
|
boolean shiftClick) {
|
||||||
if (rawSlot < 0 || rawSlot >= SIZE) return false;
|
if (rawSlot < 0 || rawSlot >= SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (rawSlot == SLOT_BACK) {
|
if (rawSlot == SLOT_BACK) {
|
||||||
MineEditGui.open(plugin, player, mineName);
|
MineEditGui.open(plugin, player, mineName);
|
||||||
|
|
@ -84,14 +87,7 @@ public final class ResetGui {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawSlot == SLOT_MESSAGE) {
|
if (rawSlot == SLOT_MESSAGE) {
|
||||||
if (leftClick) {
|
return handleMessageSlot(plugin, player, mineName, leftClick, rightClick);
|
||||||
player.sendMessage(Component.text("§eВведите сообщение в чат (или 'cancel' для отмены):"));
|
|
||||||
player.closeInventory();
|
|
||||||
} else if (rightClick) {
|
|
||||||
player.sendMessage(Component.text("§cСообщение очищено."));
|
|
||||||
refresh(player, player.getOpenInventory().getTopInventory(), mineName, plugin);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawSlot == SLOT_COMMANDS && leftClick) {
|
if (rawSlot == SLOT_COMMANDS && leftClick) {
|
||||||
|
|
@ -102,6 +98,18 @@ public final class ResetGui {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean handleMessageSlot(LoMinesPlugin plugin, Player player, String mineName,
|
||||||
|
boolean leftClick, boolean rightClick) {
|
||||||
|
if (leftClick) {
|
||||||
|
player.sendMessage(Component.text("§eВведите сообщение в чат (или 'cancel' для отмены):"));
|
||||||
|
player.closeInventory();
|
||||||
|
} else if (rightClick) {
|
||||||
|
player.sendMessage(Component.text("§cСообщение очищено."));
|
||||||
|
refresh(player, player.getOpenInventory().getTopInventory(), mineName, plugin);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static void adjustInterval(LoMinesPlugin plugin, Player player, String mineName,
|
private static void adjustInterval(LoMinesPlugin plugin, Player player, String mineName,
|
||||||
boolean leftClick, boolean rightClick, boolean shiftClick) {
|
boolean leftClick, boolean rightClick, boolean shiftClick) {
|
||||||
int delta = 0;
|
int delta = 0;
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
package dev.loki.lomines.gui.mine.edit.reset;
|
||||||
|
|
||||||
|
import dev.loki.lomines.data.config.reset.ResetConfig;
|
||||||
|
import dev.loki.lomines.gui.common.ItemStackFactory;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item factory for ResetGui.
|
||||||
|
*/
|
||||||
|
final class ResetGuiItems {
|
||||||
|
|
||||||
|
private ResetGuiItems() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static ItemStack intervalItem(ResetConfig config) {
|
||||||
|
return ItemStackFactory.create(Material.CLOCK, "§a§lИнтервал сброса",
|
||||||
|
"§8───────────────",
|
||||||
|
"§7Текущее: §f" + config.intervalDisplay(),
|
||||||
|
"",
|
||||||
|
"§e▸ ЛКМ §7+1 минута",
|
||||||
|
"§e▸ ПКМ §7-1 минута",
|
||||||
|
"§e▸ Shift+ЛКМ §7+10 минут",
|
||||||
|
"§e▸ Shift+ПКМ §7-10 минут"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ItemStack percentTriggerItem(ResetConfig config) {
|
||||||
|
String status = config.isPercentTriggerEnabled() ? "§aвкл" : "§7выкл";
|
||||||
|
String value = config.isPercentTriggerEnabled() ? config.percentTrigger() + "%" : "—";
|
||||||
|
|
||||||
|
return ItemStackFactory.create(Material.COMPARATOR, "§a§lТриггер по %",
|
||||||
|
"§8───────────────",
|
||||||
|
"§7Статус: " + status,
|
||||||
|
"§7Значение: §f" + value,
|
||||||
|
"",
|
||||||
|
"§7Сброс шахты когда",
|
||||||
|
"§7добыто указанный %",
|
||||||
|
"",
|
||||||
|
"§e▸ ЛКМ §7вкл/выкл",
|
||||||
|
"§e▸ ПКМ §7изменить %"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ItemStack messageItem(ResetConfig config) {
|
||||||
|
String message = config.broadcastMessage().isEmpty() ? "§7(не задано)" : "§f" + config.broadcastMessage();
|
||||||
|
return ItemStackFactory.create(Material.BOOK, "§a§lСообщение при сбросе",
|
||||||
|
"§8───────────────",
|
||||||
|
"§7Текущее:",
|
||||||
|
" " + ItemStackFactory.trunc(message, 30),
|
||||||
|
"",
|
||||||
|
"§7Поддерживает MiniMessage",
|
||||||
|
"§8{mine} §7— название шахты",
|
||||||
|
"§8{player} §7— игрок вызвавший сброс",
|
||||||
|
"",
|
||||||
|
"§e▸ ЛКМ §7изменить сообщение",
|
||||||
|
"§e▸ ПКМ §7очистить"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ItemStack commandsItem(ResetConfig config) {
|
||||||
|
int count = config.commands().size();
|
||||||
|
return ItemStackFactory.create(Material.COMMAND_BLOCK, "§a§lКоманды при сбросе",
|
||||||
|
"§8───────────────",
|
||||||
|
"§7Количество: §f" + count,
|
||||||
|
"",
|
||||||
|
"§7Плейсхолдеры:",
|
||||||
|
"§8{mine} §7— название шахты",
|
||||||
|
"§8{world} §7— мир",
|
||||||
|
"",
|
||||||
|
"§e▸ ЛКМ §7редактировать список"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ItemStack backItem() {
|
||||||
|
return ItemStackFactory.create(Material.ARROW, "§c§lНазад",
|
||||||
|
"§8───────────────",
|
||||||
|
"§7Вернуться в редактор шахты",
|
||||||
|
"",
|
||||||
|
"§e▸ Нажмите для возврата"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package dev.loki.lomines.gui.mine.edit;
|
package dev.loki.lomines.gui.mine.edit.rewards;
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.core.mine.Mine;
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
import dev.loki.lomines.data.config.RewardConfig;
|
import dev.loki.lomines.data.config.reward.RewardConfig;
|
||||||
import dev.loki.lomines.gui.common.ItemStackFactory;
|
import dev.loki.lomines.gui.common.ItemStackFactory;
|
||||||
|
import dev.loki.lomines.gui.mine.holder.edit.rewards.RewardsGuiHolder;
|
||||||
|
import dev.loki.lomines.gui.mine.main.MineEditGui;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
|
@ -25,7 +27,8 @@ public final class RewardsGui {
|
||||||
private static final int SLOT_BACK = 49;
|
private static final int SLOT_BACK = 49;
|
||||||
private static final int SLOT_ADD_REWARD = 52;
|
private static final int SLOT_ADD_REWARD = 52;
|
||||||
|
|
||||||
private RewardsGui() {}
|
private RewardsGui() {
|
||||||
|
}
|
||||||
|
|
||||||
public static void open(LoMinesPlugin plugin, Player player, String mineName) {
|
public static void open(LoMinesPlugin plugin, Player player, String mineName) {
|
||||||
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
||||||
|
|
@ -72,7 +75,7 @@ public final class RewardsGui {
|
||||||
int commandsCount = entry.commands().size();
|
int commandsCount = entry.commands().size();
|
||||||
String blocksList = String.join(", ", entry.blocks().stream()
|
String blocksList = String.join(", ", entry.blocks().stream()
|
||||||
.limit(3)
|
.limit(3)
|
||||||
.map(b -> b.name().toLowerCase())
|
.map(b -> b.serialize().toLowerCase())
|
||||||
.toList());
|
.toList());
|
||||||
if (entry.blocks().size() > 3) {
|
if (entry.blocks().size() > 3) {
|
||||||
blocksList += "...";
|
blocksList += "...";
|
||||||
|
|
@ -113,7 +116,9 @@ public final class RewardsGui {
|
||||||
public static boolean handleClick(LoMinesPlugin plugin, Player player, int rawSlot,
|
public static boolean handleClick(LoMinesPlugin plugin, Player player, int rawSlot,
|
||||||
String mineName, boolean leftClick, boolean rightClick,
|
String mineName, boolean leftClick, boolean rightClick,
|
||||||
int rewardIndex) {
|
int rewardIndex) {
|
||||||
if (rawSlot < 0 || rawSlot >= SIZE) return false;
|
if (rawSlot < 0 || rawSlot >= SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (rawSlot == SLOT_BACK) {
|
if (rawSlot == SLOT_BACK) {
|
||||||
MineEditGui.open(plugin, player, mineName);
|
MineEditGui.open(plugin, player, mineName);
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.loki.lomines.gui.mine.holder;
|
package dev.loki.lomines.gui.mine.holder.edit;
|
||||||
|
|
||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.InventoryHolder;
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.loki.lomines.gui.mine.holder;
|
package dev.loki.lomines.gui.mine.holder.edit;
|
||||||
|
|
||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.InventoryHolder;
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.loki.lomines.gui.mine.holder;
|
package dev.loki.lomines.gui.mine.holder.edit.reset;
|
||||||
|
|
||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.InventoryHolder;
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.loki.lomines.gui.mine.holder;
|
package dev.loki.lomines.gui.mine.holder.edit.rewards;
|
||||||
|
|
||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.InventoryHolder;
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.loki.lomines.gui.mine.holder;
|
package dev.loki.lomines.gui.mine.holder.main;
|
||||||
|
|
||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.InventoryHolder;
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
package dev.loki.lomines.gui.mine.main;
|
||||||
|
|
||||||
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
|
import dev.loki.lomines.gui.common.ItemStackFactory;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class MineEditExtraItems {
|
||||||
|
|
||||||
|
private static final String SEP = "§8───────────────";
|
||||||
|
private static final String EDIT = "§e▸ Нажмите для редактирования";
|
||||||
|
private static final String SAVE = "§a▸ Нажмите для сохранения";
|
||||||
|
private static final String DEL = "§c▸ Нажмите для удаления";
|
||||||
|
|
||||||
|
private MineEditExtraItems() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static ItemStack teleportItem(MineConfig config) {
|
||||||
|
List<String> lore = new ArrayList<>();
|
||||||
|
lore.add(SEP);
|
||||||
|
lore.add("§7Настройки:");
|
||||||
|
lore.add(" §fТелепорт: " + (config.teleport().enabled() ? "§aвкл" : "§7выкл"));
|
||||||
|
if (config.teleport().enabled()) {
|
||||||
|
config.teleport().getLocation().ifPresent(loc -> lore.add(" " + ItemStackFactory.loc(loc)));
|
||||||
|
}
|
||||||
|
lore.add("");
|
||||||
|
lore.add(" §fСпавн (застрявших): " + (config.playerSpawn().enabled() ? "§aвкл" : "§7выкл"));
|
||||||
|
if (config.playerSpawn().enabled()) {
|
||||||
|
config.playerSpawn().getLocation().ifPresent(loc -> lore.add(" " + ItemStackFactory.loc(loc)));
|
||||||
|
}
|
||||||
|
lore.add("");
|
||||||
|
lore.add("§8Команды:");
|
||||||
|
lore.add(" §8• §7/lm setteleport §f<mine>");
|
||||||
|
lore.add(" §8• §7/lm setspawn §f<mine>");
|
||||||
|
lore.add(" §8• §7/lm clearspawn §f<mine>");
|
||||||
|
lore.add("");
|
||||||
|
lore.add(EDIT);
|
||||||
|
return ItemStackFactory.create(Material.ENDER_PEARL, "§a§lТелепорт", lore.toArray(new String[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ItemStack uiItem(MineConfig config) {
|
||||||
|
return ItemStackFactory.create(Material.PAINTING, "§a§lИнтерфейс",
|
||||||
|
SEP,
|
||||||
|
"§7Настройки:",
|
||||||
|
" §fAction Bar: " + (config.ui().actionBarEnabled() ? "§aвкл" : "§7выкл"),
|
||||||
|
" §fРадиус: §7" + (int) config.ui().actionBarRange() + " §8блоков",
|
||||||
|
"",
|
||||||
|
"§7Формат ActionBar:",
|
||||||
|
" §8" + ItemStackFactory.trunc(config.ui().actionBarFormat(), 28),
|
||||||
|
"",
|
||||||
|
"§7Формат таймера: §8" + config.ui().timerFormat(),
|
||||||
|
"",
|
||||||
|
EDIT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ItemStack saveItem() {
|
||||||
|
return ItemStackFactory.create(Material.LIME_DYE, "§a§lСохранить изменения",
|
||||||
|
SEP,
|
||||||
|
"§7Сохраняет конфигурацию шахты",
|
||||||
|
"§7в файл на диске",
|
||||||
|
"",
|
||||||
|
SAVE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ItemStack deleteItem(String mineName) {
|
||||||
|
return ItemStackFactory.create(Material.TNT, "§c§lУдалить шахту",
|
||||||
|
SEP,
|
||||||
|
"§c§lВнимание!",
|
||||||
|
"§cЭто действие нельзя отменить!",
|
||||||
|
"",
|
||||||
|
"§7Шахта: §f" + mineName,
|
||||||
|
"",
|
||||||
|
DEL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ItemStack infoItem(Mine mine) {
|
||||||
|
int blocks = mine.getBlocks();
|
||||||
|
int total = mine.getTotalVolume();
|
||||||
|
double percent = mine.getPercentFilled();
|
||||||
|
String c = percent > 50 ? "§a" : percent > 25 ? "§e" : "§c";
|
||||||
|
int filled = (int) Math.round(percent / 100.0 * 10);
|
||||||
|
String bar = "§a" + "█".repeat(filled) + "§8" + "░".repeat(10 - filled);
|
||||||
|
return ItemStackFactory.create(Material.BOOK, "§b§lИнформация",
|
||||||
|
SEP,
|
||||||
|
"§7Базовые данные:",
|
||||||
|
" §fНазвание: §7" + mine.getName(),
|
||||||
|
" §fМир: §7" + mine.getConfig().worldName(),
|
||||||
|
"",
|
||||||
|
"§7Состояние заполнения:",
|
||||||
|
" §fБлоков: " + c + blocks + "§8/§7" + total,
|
||||||
|
" §fПроцент: " + c + String.format("%.1f%%", percent),
|
||||||
|
" " + bar
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ItemStack backItem() {
|
||||||
|
return ItemStackFactory.create(Material.ARROW, "§c§lЗакрыть",
|
||||||
|
SEP,
|
||||||
|
"§7Закрыть редактор",
|
||||||
|
"",
|
||||||
|
"§c▸ Нажмите чтобы закрыть"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
package dev.loki.lomines.gui.mine.main;
|
package dev.loki.lomines.gui.mine.main;
|
||||||
|
|
||||||
import dev.loki.lomines.LoMinesPlugin;
|
import dev.loki.lomines.LoMinesPlugin;
|
||||||
import dev.loki.lomines.core.mine.Mine;
|
import dev.loki.lomines.core.mine.model.Mine;
|
||||||
import dev.loki.lomines.data.config.MineConfig;
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
import dev.loki.lomines.gui.common.ItemStackFactory;
|
|
||||||
import dev.loki.lomines.gui.confirm.ConfirmDeleteGui;
|
import dev.loki.lomines.gui.confirm.ConfirmDeleteGui;
|
||||||
|
import dev.loki.lomines.gui.mine.edit.blocks.view.BlocksGui;
|
||||||
|
import dev.loki.lomines.gui.mine.edit.reset.ResetGui;
|
||||||
|
import dev.loki.lomines.gui.mine.edit.rewards.RewardsGui;
|
||||||
|
import dev.loki.lomines.gui.mine.holder.main.MineEditGuiHolder;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
|
@ -18,9 +21,6 @@ import org.bukkit.inventory.Inventory;
|
||||||
*/
|
*/
|
||||||
public final class MineEditGui {
|
public final class MineEditGui {
|
||||||
|
|
||||||
private static final int SIZE = 54;
|
|
||||||
private static final String TITLE_PREFIX = "Редактор шахты: ";
|
|
||||||
|
|
||||||
// Slot constants
|
// Slot constants
|
||||||
public static final int SLOT_BACK = 49;
|
public static final int SLOT_BACK = 49;
|
||||||
public static final int SLOT_BLOCKS = 10;
|
public static final int SLOT_BLOCKS = 10;
|
||||||
|
|
@ -32,8 +32,11 @@ public final class MineEditGui {
|
||||||
public static final int SLOT_SAVE = 32;
|
public static final int SLOT_SAVE = 32;
|
||||||
public static final int SLOT_DELETE = 34;
|
public static final int SLOT_DELETE = 34;
|
||||||
public static final int SLOT_INFO = 40;
|
public static final int SLOT_INFO = 40;
|
||||||
|
private static final int SIZE = 54;
|
||||||
|
private static final String TITLE_PREFIX = "Редактор шахты: ";
|
||||||
|
|
||||||
private MineEditGui() {}
|
private MineEditGui() {
|
||||||
|
}
|
||||||
|
|
||||||
public static void open(LoMinesPlugin plugin, Player player, String mineName) {
|
public static void open(LoMinesPlugin plugin, Player player, String mineName) {
|
||||||
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
||||||
|
|
@ -42,8 +45,7 @@ public final class MineEditGui {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MineEditGuiHolder holder = new MineEditGuiHolder(player.getUniqueId(), mineName);
|
MineEditGuiHolder holder = new MineEditGuiHolder(player.getUniqueId(), mineName);
|
||||||
Inventory inv = Bukkit.createInventory(holder, SIZE,
|
Inventory inv = Bukkit.createInventory(holder, SIZE, Component.text(TITLE_PREFIX + mineName, NamedTextColor.DARK_GREEN));
|
||||||
Component.text(TITLE_PREFIX + mineName, NamedTextColor.DARK_GREEN));
|
|
||||||
holder.setInventory(inv);
|
holder.setInventory(inv);
|
||||||
fill(plugin, mine, inv);
|
fill(plugin, mine, inv);
|
||||||
player.openInventory(inv);
|
player.openInventory(inv);
|
||||||
|
|
@ -60,26 +62,30 @@ public final class MineEditGui {
|
||||||
|
|
||||||
private static void fill(LoMinesPlugin plugin, Mine mine, Inventory inv) {
|
private static void fill(LoMinesPlugin plugin, Mine mine, Inventory inv) {
|
||||||
MineConfig config = mine.getConfig();
|
MineConfig config = mine.getConfig();
|
||||||
for (int i = 0; i < SIZE; i++) inv.setItem(i, MineEditItems.filler());
|
for (int i = 0; i < SIZE; i++) {
|
||||||
|
inv.setItem(i, MineEditItems.filler());
|
||||||
|
}
|
||||||
inv.setItem(SLOT_BLOCKS, MineEditItems.blocksItem(config));
|
inv.setItem(SLOT_BLOCKS, MineEditItems.blocksItem(config));
|
||||||
inv.setItem(SLOT_REGIONS, MineEditItems.regionsItem(config));
|
inv.setItem(SLOT_REGIONS, MineEditItems.regionsItem(config));
|
||||||
inv.setItem(SLOT_RESET, MineEditItems.resetItem(config));
|
inv.setItem(SLOT_RESET, MineEditItems.resetItem(config));
|
||||||
inv.setItem(SLOT_REWARDS, MineEditItems.rewardsItem(config));
|
inv.setItem(SLOT_REWARDS, MineEditItems.rewardsItem(config));
|
||||||
inv.setItem(SLOT_TELEPORT, MineEditItems.teleportItem(config));
|
inv.setItem(SLOT_TELEPORT, MineEditExtraItems.teleportItem(config));
|
||||||
inv.setItem(SLOT_UI, MineEditItems.uiItem(config));
|
inv.setItem(SLOT_UI, MineEditExtraItems.uiItem(config));
|
||||||
inv.setItem(SLOT_SAVE, MineEditItems.saveItem());
|
inv.setItem(SLOT_SAVE, MineEditExtraItems.saveItem());
|
||||||
inv.setItem(SLOT_DELETE, MineEditItems.deleteItem(mine.getName()));
|
inv.setItem(SLOT_DELETE, MineEditExtraItems.deleteItem(mine.getName()));
|
||||||
inv.setItem(SLOT_INFO, MineEditItems.infoItem(mine));
|
inv.setItem(SLOT_INFO, MineEditExtraItems.infoItem(mine));
|
||||||
inv.setItem(SLOT_BACK, MineEditItems.backItem());
|
inv.setItem(SLOT_BACK, MineEditExtraItems.backItem());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean handleClick(LoMinesPlugin plugin, Player player, int rawSlot, String mineName) {
|
public static boolean handleClick(LoMinesPlugin plugin, Player player, int rawSlot, String mineName) {
|
||||||
if (rawSlot < 0 || rawSlot >= SIZE) return false;
|
if (rawSlot < 0 || rawSlot >= SIZE) {
|
||||||
switch (rawSlot) {
|
return false;
|
||||||
case SLOT_BACK -> {
|
|
||||||
player.closeInventory();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return handleNavigationSlot(plugin, player, mineName, rawSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean handleNavigationSlot(LoMinesPlugin plugin, Player player, String mineName, int rawSlot) {
|
||||||
|
switch (rawSlot) {
|
||||||
case SLOT_BLOCKS -> {
|
case SLOT_BLOCKS -> {
|
||||||
BlocksGui.open(plugin, player, mineName);
|
BlocksGui.open(plugin, player, mineName);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -92,6 +98,22 @@ public final class MineEditGui {
|
||||||
RewardsGui.open(plugin, player, mineName);
|
RewardsGui.open(plugin, player, mineName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case SLOT_DELETE -> {
|
||||||
|
ConfirmDeleteGui.open(plugin, player, mineName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
return handleInfoSlot(player, mineName, rawSlot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean handleInfoSlot(Player player, String mineName, int rawSlot) {
|
||||||
|
switch (rawSlot) {
|
||||||
|
case SLOT_BACK -> {
|
||||||
|
player.closeInventory();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
case SLOT_TELEPORT -> {
|
case SLOT_TELEPORT -> {
|
||||||
player.sendMessage(Component.text("Используйте команды /lm setteleport и /lm setspawn", NamedTextColor.YELLOW));
|
player.sendMessage(Component.text("Используйте команды /lm setteleport и /lm setspawn", NamedTextColor.YELLOW));
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -105,13 +127,11 @@ public final class MineEditGui {
|
||||||
player.closeInventory();
|
player.closeInventory();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case SLOT_DELETE -> {
|
|
||||||
ConfirmDeleteGui.open(plugin, player, mineName);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case SLOT_INFO, SLOT_REGIONS -> {
|
case SLOT_INFO, SLOT_REGIONS -> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
default -> {}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
package dev.loki.lomines.gui.mine.main;
|
package dev.loki.lomines.gui.mine.main;
|
||||||
|
|
||||||
import dev.loki.lomines.core.mine.Mine;
|
import dev.loki.lomines.data.config.model.MineConfig;
|
||||||
import dev.loki.lomines.data.config.MineConfig;
|
import dev.loki.lomines.data.config.reward.RewardConfig;
|
||||||
import dev.loki.lomines.data.config.RewardConfig;
|
|
||||||
import dev.loki.lomines.gui.common.ItemStackFactory;
|
import dev.loki.lomines.gui.common.ItemStackFactory;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
@ -17,24 +16,24 @@ final class MineEditItems {
|
||||||
|
|
||||||
private static final String SEP = "§8───────────────";
|
private static final String SEP = "§8───────────────";
|
||||||
private static final String EDIT = "§e▸ Нажмите для редактирования";
|
private static final String EDIT = "§e▸ Нажмите для редактирования";
|
||||||
private static final String CLOSE = "§c▸ Нажмите чтобы закрыть";
|
|
||||||
private static final String SAVE = "§a▸ Нажмите для сохранения";
|
|
||||||
private static final String DEL = "§c▸ Нажмите для удаления";
|
|
||||||
|
|
||||||
private MineEditItems() {}
|
private MineEditItems() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static ItemStack filler() {
|
static ItemStack filler() {
|
||||||
return ItemStackFactory.filler();
|
return ItemStackFactory.filler();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ItemStack blocksItem(MineConfig config) {
|
static ItemStack blocksItem(MineConfig config) {
|
||||||
var w = config.blocks().weights();
|
|
||||||
List<String> lore = new ArrayList<>();
|
List<String> lore = new ArrayList<>();
|
||||||
lore.add(SEP);
|
lore.add(SEP);
|
||||||
lore.add("§7Настройки:");
|
lore.add("§7Настройки:");
|
||||||
lore.add(" §fТипов: §7" + config.blocks().blockCount());
|
lore.add(" §fТипов: §7" + config.blocks().blockCount());
|
||||||
lore.add(" §fРежим: §7" + config.blocks().fillMode().name());
|
lore.add(" §fРежим: §7" + config.blocks().fillMode().name());
|
||||||
lore.add(" §fКастомные: " + (config.blocks().hasCustomBlocks() ? "§aДа" : "§7Нет"));
|
lore.add(" §fКастомные: " + (config.blocks().hasCustomBlocks() ? "§aДа" : "§7Нет"));
|
||||||
|
var w = config.blocks().weights();
|
||||||
if (!w.isEmpty()) {
|
if (!w.isEmpty()) {
|
||||||
lore.add("");
|
lore.add("");
|
||||||
lore.add("§7Топ блоков:");
|
lore.add("§7Топ блоков:");
|
||||||
|
|
@ -77,10 +76,10 @@ final class MineEditItems {
|
||||||
}
|
}
|
||||||
|
|
||||||
static ItemStack rewardsItem(MineConfig config) {
|
static ItemStack rewardsItem(MineConfig config) {
|
||||||
var entries = config.rewards().entries();
|
final var entries = config.rewards().entries();
|
||||||
long items = entries.stream().flatMap(e -> e.items().stream()).count();
|
final long items = entries.stream().flatMap(e -> e.items().stream()).count();
|
||||||
long cmds = entries.stream().filter(e -> !e.commands().isEmpty()).count();
|
final long cmds = entries.stream().filter(e -> !e.commands().isEmpty()).count();
|
||||||
long blockDrop = entries.stream().filter(RewardConfig.RewardEntry::preventVanillaDrops).count();
|
final long blockDrop = entries.stream().filter(RewardConfig.RewardEntry::preventVanillaDrops).count();
|
||||||
List<String> lore = new ArrayList<>();
|
List<String> lore = new ArrayList<>();
|
||||||
lore.add(SEP);
|
lore.add(SEP);
|
||||||
lore.add("§7Настройки:");
|
lore.add("§7Настройки:");
|
||||||
|
|
@ -90,100 +89,13 @@ final class MineEditItems {
|
||||||
lore.add("§7Типы наград:");
|
lore.add("§7Типы наград:");
|
||||||
lore.add(" §8• §fПредметов: §7" + items);
|
lore.add(" §8• §fПредметов: §7" + items);
|
||||||
lore.add(" §8• §fКоманд: §7" + cmds);
|
lore.add(" §8• §fКоманд: §7" + cmds);
|
||||||
if (blockDrop > 0) lore.add(" §8• §cБлокируют дроп: §7" + blockDrop);
|
if (blockDrop > 0) {
|
||||||
|
lore.add(" §8• §cБлокируют дроп: §7" + blockDrop);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lore.add("");
|
lore.add("");
|
||||||
lore.add(EDIT);
|
lore.add(EDIT);
|
||||||
return ItemStackFactory.create(Material.GOLD_INGOT, "§a§lНаграды", lore.toArray(new String[0]));
|
return ItemStackFactory.create(Material.GOLD_INGOT, "§a§lНаграды", lore.toArray(new String[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
static ItemStack teleportItem(MineConfig config) {
|
|
||||||
List<String> lore = new ArrayList<>();
|
|
||||||
lore.add(SEP);
|
|
||||||
lore.add("§7Настройки:");
|
|
||||||
lore.add(" §fТелепорт: " + (config.teleport().enabled() ? "§aвкл" : "§7выкл"));
|
|
||||||
if (config.teleport().enabled()) {
|
|
||||||
config.teleport().getLocation().ifPresent(loc -> lore.add(" " + ItemStackFactory.loc(loc)));
|
|
||||||
}
|
|
||||||
lore.add("");
|
|
||||||
lore.add(" §fСпавн (застрявших): " + (config.playerSpawn().enabled() ? "§aвкл" : "§7выкл"));
|
|
||||||
if (config.playerSpawn().enabled()) {
|
|
||||||
config.playerSpawn().getLocation().ifPresent(loc -> lore.add(" " + ItemStackFactory.loc(loc)));
|
|
||||||
}
|
|
||||||
lore.add("");
|
|
||||||
lore.add("§8Команды:");
|
|
||||||
lore.add(" §8• §7/lm setteleport §f<mine>");
|
|
||||||
lore.add(" §8• §7/lm setspawn §f<mine>");
|
|
||||||
lore.add(" §8• §7/lm clearspawn §f<mine>");
|
|
||||||
lore.add("");
|
|
||||||
lore.add(EDIT);
|
|
||||||
return ItemStackFactory.create(Material.ENDER_PEARL, "§a§lТелепорт", lore.toArray(new String[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
static ItemStack uiItem(MineConfig config) {
|
|
||||||
return ItemStackFactory.create(Material.PAINTING, "§a§lИнтерфейс",
|
|
||||||
SEP,
|
|
||||||
"§7Настройки:",
|
|
||||||
" §fAction Bar: " + (config.ui().actionBarEnabled() ? "§aвкл" : "§7выкл"),
|
|
||||||
" §fРадиус: §7" + (int) config.ui().actionBarRange() + " §8блоков",
|
|
||||||
"",
|
|
||||||
"§7Формат ActionBar:",
|
|
||||||
" §8" + ItemStackFactory.trunc(config.ui().actionBarFormat(), 28),
|
|
||||||
"",
|
|
||||||
"§7Формат таймера: §8" + config.ui().timerFormat(),
|
|
||||||
"",
|
|
||||||
EDIT
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ItemStack saveItem() {
|
|
||||||
return ItemStackFactory.create(Material.LIME_DYE, "§a§lСохранить изменения",
|
|
||||||
SEP,
|
|
||||||
"§7Сохраняет конфигурацию шахты",
|
|
||||||
"§7в файл на диске",
|
|
||||||
"",
|
|
||||||
SAVE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ItemStack deleteItem(String mineName) {
|
|
||||||
return ItemStackFactory.create(Material.TNT, "§c§lУдалить шахту",
|
|
||||||
SEP,
|
|
||||||
"§c§lВнимание!",
|
|
||||||
"§cЭто действие нельзя отменить!",
|
|
||||||
"",
|
|
||||||
"§7Шахта: §f" + mineName,
|
|
||||||
"",
|
|
||||||
DEL
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ItemStack infoItem(Mine mine) {
|
|
||||||
int blocks = mine.getBlocks();
|
|
||||||
int total = mine.getTotalVolume();
|
|
||||||
double percent = mine.getPercentFilled();
|
|
||||||
String c = percent > 50 ? "§a" : percent > 25 ? "§e" : "§c";
|
|
||||||
int filled = (int) Math.round(percent / 100.0 * 10);
|
|
||||||
String bar = "§a" + "█".repeat(filled) + "§8" + "░".repeat(10 - filled);
|
|
||||||
return ItemStackFactory.create(Material.BOOK, "§b§lИнформация",
|
|
||||||
SEP,
|
|
||||||
"§7Базовые данные:",
|
|
||||||
" §fНазвание: §7" + mine.getName(),
|
|
||||||
" §fМир: §7" + mine.getConfig().worldName(),
|
|
||||||
"",
|
|
||||||
"§7Состояние заполнения:",
|
|
||||||
" §fБлоков: " + c + blocks + "§8/§7" + total,
|
|
||||||
" §fПроцент: " + c + String.format("%.1f%%", percent),
|
|
||||||
" " + bar
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ItemStack backItem() {
|
|
||||||
return ItemStackFactory.create(Material.ARROW, "§c§lЗакрыть",
|
|
||||||
SEP,
|
|
||||||
"§7Закрыть редактор",
|
|
||||||
"",
|
|
||||||
CLOSE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue