Compare commits
144 Commits
v0.12-beta
...
master
Author | SHA1 | Date |
---|---|---|
Julius Riegel | 1950f83df5 | 3 years ago |
Trivernis | c12e96e8a0 | 4 years ago |
trivernis | a5bb701c80 | 4 years ago |
Trivernis | df2f59469d | 4 years ago |
Iván Pérez | 508cd7bf8f | 4 years ago |
Trivernis | da1ff68542 | 4 years ago |
trivernis | a8a9be2bdb | 4 years ago |
Trivernis | 65cd93e188 | 4 years ago |
NPBeta | e304f25103 | 4 years ago |
NPBeta | 6fedee2f1c | 4 years ago |
Trivernis | ebab533e08 | 4 years ago |
trivernis | ec7af49267 | 4 years ago |
trivernis | aad4143e8a | 4 years ago |
trivernis | f15dc646f6 | 4 years ago |
trivernis | e545445348 | 4 years ago |
trivernis | 90f2bbc2af | 4 years ago |
trivernis | a3f940c7d0 | 4 years ago |
Trivernis | 6680e82433 | 4 years ago |
trivernis | b46b3f78cd | 4 years ago |
trivernis | dcfd9f11f1 | 4 years ago |
Trivernis | 689fea28d1 | 4 years ago |
trivernis | 49b1f8f1c7 | 4 years ago |
trivernis | 930ae728d0 | 4 years ago |
Trivernis | b485bf9999 | 4 years ago |
Trivernis | fe3c59df5e | 4 years ago |
trivernis | 626003028a | 4 years ago |
trivernis | 7a27618100 | 4 years ago |
trivernis | 1119942082 | 4 years ago |
trivernis | e458757609 | 4 years ago |
trivernis | cbc0fcdb64 | 4 years ago |
trivernis | aad3bbdb07 | 4 years ago |
trivernis | af5ed6413d | 4 years ago |
trivernis | 8567a7ffd1 | 4 years ago |
Adam Tunnic | 0e0166c7e3 | 4 years ago |
Trivernis | 6144576440 | 4 years ago |
trivernis | fca6c9eea2 | 4 years ago |
trivernis | 3c07d317d2 | 4 years ago |
Trivernis | a0f6f4832f | 4 years ago |
trivernis | 99a48c1465 | 4 years ago |
trivernis | 4947731718 | 4 years ago |
Trivernis | b72c473854 | 4 years ago |
trivernis | 6df01dd137 | 4 years ago |
trivernis | 3cfc49baa8 | 4 years ago |
trivernis | 9b618eccc2 | 4 years ago |
trivernis | 65e3e090bb | 4 years ago |
trivernis | f0ea063026 | 4 years ago |
Trivernis | 82662bc4d3 | 4 years ago |
trivernis | 733996e6a9 | 4 years ago |
trivernis | a91e26652d | 4 years ago |
trivernis | 3417ffe339 | 4 years ago |
Trivernis | 441b13ec69 | 4 years ago |
trivernis | aaa614f651 | 4 years ago |
Trivernis | 4d4107aaf3 | 4 years ago |
trivernis | 7ffada4100 | 4 years ago |
trivernis | cf7ef7d887 | 4 years ago |
Trivernis | ac6a80ed86 | 4 years ago |
Trivernis | af875aaca0 | 4 years ago |
Trivernis | 1f167285b0 | 4 years ago |
Trivernis | acf302e8c1 | 4 years ago |
Trivernis | 400295ac73 | 4 years ago |
trivernis | 1ebf3c96a8 | 4 years ago |
Trivernis | 8987fe94aa | 4 years ago |
Corentin Boiteau | af3739bd65 | 4 years ago |
Trivernis | 434deb33d3 | 4 years ago |
trivernis | efb28e5ff9 | 4 years ago |
Trivernis | c69b8fd91d | 4 years ago |
NPBeta | a708c1cfdd | 4 years ago |
NPBeta | 984c49c644 | 4 years ago |
Trivernis | d078e1150f | 4 years ago |
trivernis | 94f70c39b0 | 4 years ago |
Trivernis | 602b7c92e7 | 4 years ago |
Trivernis | 4b93fc8dd8 | 4 years ago |
Trivernis | 64795b341b | 4 years ago |
trivernis | 3dec787d0e | 4 years ago |
Trivernis | 6a133410ac | 4 years ago |
trivernis | 615280a2ed | 4 years ago |
Trivernis | 8ad6bc44af | 4 years ago |
trivernis | 928dfbf264 | 4 years ago |
trivernis | c2fad45c26 | 4 years ago |
Trivernis | af03dc0325 | 4 years ago |
trivernis | 7b7fdd1969 | 4 years ago |
trivernis | a2673b4952 | 4 years ago |
Trivernis | f836052e20 | 4 years ago |
Trivernis | d5ce796f9e | 4 years ago |
trivernis | 3f28c89f8d | 4 years ago |
trivernis | 0bc38384b4 | 4 years ago |
trivernis | ecc9309b41 | 4 years ago |
trivernis | 29d0f8991e | 4 years ago |
Trivernis | 4a6f7d98cf | 4 years ago |
trivernis | fd6b1e9190 | 4 years ago |
Trivernis | 9c5f486414 | 4 years ago |
trivernis | a6d5462433 | 5 years ago |
Trivernis | 18ac52cab8 | 5 years ago |
trivernis | a377392fd2 | 5 years ago |
trivernis | ac3c068f5d | 5 years ago |
trivernis | 3367af9adc | 5 years ago |
trivernis | 3cfa38e03d | 5 years ago |
Trivernis | 95102b1e86 | 5 years ago |
trivernis | b587f491cf | 5 years ago |
trivernis | d52bc304f4 | 5 years ago |
trivernis | dc802f9e00 | 5 years ago |
trivernis | bd57087427 | 5 years ago |
trivernis | 72618e3e50 | 5 years ago |
trivernis | b06018ec0d | 5 years ago |
trivernis | 03eaf52dc3 | 5 years ago |
trivernis | c090977021 | 5 years ago |
trivernis | be130adc6f | 5 years ago |
trivernis | 2b121b67fd | 5 years ago |
trivernis | 0347730580 | 5 years ago |
trivernis | 572369192a | 5 years ago |
trivernis | 2eeff350f8 | 5 years ago |
trivernis | 53aec18a06 | 5 years ago |
trivernis | d967dfd06d | 5 years ago |
Trivernis | dca3f25358 | 5 years ago |
Trivernis | 36394d1768 | 5 years ago |
trivernis | 8953b63c2b | 5 years ago |
Trivernis | 832b1c4b55 | 5 years ago |
Trivernis | bdac496ce2 | 5 years ago |
trivernis | a09bebb6fa | 5 years ago |
trivernis | ecc3165f1f | 5 years ago |
trivernis | 6cb39ce843 | 5 years ago |
Trivernis | eb222ad582 | 5 years ago |
trivernis | 109699f639 | 5 years ago |
trivernis | e5f812e3bc | 5 years ago |
trivernis | 1c94ced875 | 5 years ago |
trivernis | a4d39bdb6b | 5 years ago |
Trivernis | 5f14885737 | 5 years ago |
Trivernis | 407edff2df | 5 years ago |
Trivernis | a4115ebdf7 | 5 years ago |
Trivernis | 3d8c47aee2 | 5 years ago |
Trivernis | 965598de2b | 5 years ago |
trivernis | bd731c85f0 | 5 years ago |
trivernis | 900600714e | 5 years ago |
trivernis | f88100be5f | 5 years ago |
Trivernis | f1ad4473de | 5 years ago |
trivernis | f89be3a73c | 5 years ago |
Trivernis | 24b2707293 | 5 years ago |
trivernis | f89262e538 | 5 years ago |
Trivernis | ce826419f1 | 5 years ago |
Trivernis | 3a0012ef3f | 5 years ago |
Trivernis | 8b63643f7a | 5 years ago |
Trivernis | b09b96d95c | 5 years ago |
Trivernis | d7052e1ac9 | 5 years ago |
Trivernis | 160ee13ac8 | 5 years ago |
@ -1,45 +1,45 @@
|
|||||||
# Java Gradle CircleCI 2.0 configuration file
|
# Java Gradle CircleCI 2.0 configuration file
|
||||||
#
|
#
|
||||||
# Check https://circleci.com/docs/2.0/language-java/ for more details
|
# Check https://circleci.com/docs/2.0/language-java/ for more details
|
||||||
#
|
#
|
||||||
version: 2
|
version: 2
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
docker:
|
docker:
|
||||||
# specify the version you desire here
|
# specify the version you desire here
|
||||||
- image: circleci/openjdk:8-jdk
|
- image: circleci/openjdk:8-jdk
|
||||||
|
|
||||||
# Specify service dependencies here if necessary
|
# Specify service dependencies here if necessary
|
||||||
# CircleCI maintains a library of pre-built images
|
# CircleCI maintains a library of pre-built images
|
||||||
# documented at https://circleci.com/docs/2.0/circleci-images/
|
# documented at https://circleci.com/docs/2.0/circleci-images/
|
||||||
# - image: circleci/postgres:9.4
|
# - image: circleci/postgres:9.4
|
||||||
|
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
# Customize the JVM maximum heap limit
|
# Customize the JVM maximum heap limit
|
||||||
JVM_OPTS: -Xmx3200m
|
JVM_OPTS: -Xmx3200m
|
||||||
TERM: dumb
|
TERM: dumb
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
|
|
||||||
# Download and cache dependencies
|
# Download and cache dependencies
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- v1-dependencies-{{ checksum "build.gradle" }}
|
- v1-dependencies-{{ checksum "build.gradle" }}
|
||||||
# fallback to using the latest cache if no exact match is found
|
# fallback to using the latest cache if no exact match is found
|
||||||
- v1-dependencies-
|
- v1-dependencies-
|
||||||
|
|
||||||
- run: gradle dependencies
|
- run: gradle dependencies
|
||||||
|
|
||||||
- save_cache:
|
- save_cache:
|
||||||
paths:
|
paths:
|
||||||
- ~/.gradle
|
- ~/.gradle
|
||||||
key: v1-dependencies-{{ checksum "build.gradle" }}
|
key: v1-dependencies-{{ checksum "build.gradle" }}
|
||||||
|
|
||||||
# Build jar
|
# Build jar
|
||||||
- run: gradle shadowJar
|
- run: gradle shadowJar
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: build/libs
|
path: build/libs
|
||||||
destination: build
|
destination: build
|
@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: "[BUG]"
|
||||||
|
labels: bug
|
||||||
|
assignees: Trivernis
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Logfile**
|
||||||
|
Please add a logfile as either a file attachment or a link to something like pastebin.
|
||||||
|
For crashes please also add the crashlog file.
|
||||||
|
|
||||||
|
**Server (please complete the following information):**
|
||||||
|
- OS: [e.g. Ubuntu 20]
|
||||||
|
- Specs [e.g. Intel Xeon E5-2680, 8GB Ram]
|
||||||
|
- Java Version [e.g. Java 1.12]
|
||||||
|
- Minecraft Server Version [e.g. Paper 1.15.2 #176](PLEASE PROVIDE THE EXACT VERSION OF THE /version COMMAND, NOT JUST spigot 1.16.1!)
|
||||||
|
- Plugin Version [e.g. 0.15]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: "[FEATURE]"
|
||||||
|
labels: enhancement
|
||||||
|
assignees: Trivernis
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
name: Question
|
||||||
|
about: Ask a question about this project
|
||||||
|
title: "[QUESTION]"
|
||||||
|
labels: question
|
||||||
|
assignees: Trivernis
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Question**
|
||||||
|
|
||||||
|
A clear and concise question that wasn't asked yet.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
|
||||||
|
Add any other context or screenshots that are needed to understand the question here.
|
@ -0,0 +1,19 @@
|
|||||||
|
# Number of days of inactivity before an issue becomes stale
|
||||||
|
daysUntilStale: 14
|
||||||
|
# Number of days of inactivity before a stale issue is closed
|
||||||
|
daysUntilClose: 7
|
||||||
|
# Issues with these labels will never be considered stale
|
||||||
|
exemptLabels:
|
||||||
|
- pinned
|
||||||
|
- security
|
||||||
|
- enhancement
|
||||||
|
- bug
|
||||||
|
# Label to use when marking an issue as stale
|
||||||
|
staleLabel: wontfix
|
||||||
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
|
markComment: >
|
||||||
|
This issue has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
|
for your contributions.
|
||||||
|
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||||
|
closeComment: false
|
@ -0,0 +1,42 @@
|
|||||||
|
name: Gradle Build
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: [ main, develop, actions ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main, develop, actions ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
shadowJar:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
|
||||||
|
- name: Cache build data
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('build.gradle') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-
|
||||||
|
|
||||||
|
- name: Build Jar
|
||||||
|
run: chmod +x gradlew && ./gradlew shadowJar
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: chunkmaster
|
||||||
|
path: build/libs/chunkmaster-*.jar
|
||||||
|
|
||||||
|
- name: Cleanup Gradle Cache
|
||||||
|
run: |
|
||||||
|
rm -f ~/.gradle/caches/modules-2/modules-2.lock
|
||||||
|
rm -f ~/.gradle/caches/modules-2/gc.properties
|
@ -0,0 +1,36 @@
|
|||||||
|
name: Gradle Unit Tests
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: [ main, develop, actions, feature/unit-tests ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main, develop, actions, feature/unit-tests ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
|
||||||
|
- name: Cache build data
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('build.gradle') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: chmod +x gradlew && ./gradlew test
|
||||||
|
|
||||||
|
- name: Cleanup Gradle Cache
|
||||||
|
run: |
|
||||||
|
rm -f ~/.gradle/caches/modules-2/modules-2.lock
|
||||||
|
rm -f ~/.gradle/caches/modules-2/gc.properties
|
@ -1,6 +1,4 @@
|
|||||||
gradle
|
.gradle
|
||||||
.gradle
|
.idea
|
||||||
.idea
|
build
|
||||||
build
|
out
|
||||||
out
|
|
||||||
gradlew*
|
|
@ -1,75 +1,159 @@
|
|||||||
# chunkmaster [![CircleCI](https://circleci.com/gh/Trivernis/spigot-chunkmaster.svg?style=svg)](https://circleci.com/gh/Trivernis/spigot-chunkmaster)
|
# chunkmaster [![](https://circleci.com/gh/Trivernis/spigot-chunkmaster.svg?style=shield)](https://app.circleci.com/pipelines/github/Trivernis/spigot-chunkmaster) [![CodeFactor](https://www.codefactor.io/repository/github/trivernis/spigot-chunkmaster/badge)](https://www.codefactor.io/repository/github/trivernis/spigot-chunkmaster) [![](https://img.shields.io/discord/729250668162056313)](https://discord.gg/KZcMAgN)
|
||||||
|
|
||||||
This plugin can be used to pre-generate the region of a world around the spawn chunk.
|
<div align="center">
|
||||||
The generation automatically pauses when a player joins the server (assuming the server was empty before)
|
<h1>This plugin isn't actively developed anymore. If you want to add features feel free to fork it and implement it yourself or use <a href="https://www.spigotmc.org/resources/chunky.81534/">Chunky</a> instead.</h1>
|
||||||
and resumes when the server is empty again. The generation also auto-resumes after a server
|
</div>
|
||||||
restart. The plugin tracks the ticks per second and pauses the generation when the tps
|
|
||||||
is lower than 2 (configurable).
|
This plugin can be used to pre-generate the region of a world around the spawn chunk(s).
|
||||||
|
The generation automatically pauses when a player joins the server (assuming the server was empty before)
|
||||||
## Commands
|
and resumes when the server is empty again. The generation also auto-resumes after a server
|
||||||
|
restart. The plugin tracks the ticks per second and pauses the generation when the tps
|
||||||
All features can be accessed with the command `/chunkmaster` or the aliases `/chm`, `chunkm`, `cmaster`.
|
is lower than 2 (configurable).
|
||||||
|
|
||||||
- `/chunkmaster generate [world] [chunk count] [unit]` Starts the generation until the specified chunk count or the world border is reached.
|
## Built with
|
||||||
- `/chunkmaster list` Lists all running generation tasks
|
|
||||||
- `/chunkmaster cancel <Task id>` Cancels the generation task with the specified id (if it is running).
|
- [Gradle](https://gradle.org/) - Dependency Management and Build Tool
|
||||||
- `/chunkmaster pause` Pauses all generation tasks until the resume command is executed.
|
- [Sqlite JDBC](https://bitbucket.org/xerial/sqlite-jdbc/) - Database Driver for JDBC
|
||||||
- `/chunkmaster resume` Resumes all paused generation tasks.
|
- [bStats](https://bstats.org/) - Statistical Insights
|
||||||
- `/chunkmaster reload` Reloads the configuration file.
|
|
||||||
- `/chunkmaster tpchunk <X> <Z>` Teleports you to the specified chunk coordinates.
|
## Features
|
||||||
|
|
||||||
## Config
|
- Pregeneration of a specific area around the world center
|
||||||
|
- Configuration of world centers
|
||||||
```yaml
|
- Integration into dynmap
|
||||||
generation:
|
- Teleportation to chunks
|
||||||
|
- Auto-Pause/Resume on player join/leave
|
||||||
# The maximum amount of chunks that are loaded before unloading and saving them.
|
- Highly configurable
|
||||||
# Higher values mean higher generation speed but greater memory usage.
|
|
||||||
# The value should be a positive integer.
|
## Installing
|
||||||
max-loaded-chunks: 10
|
|
||||||
|
Just download the jar from the latest release and place it into the servers plugins folder.
|
||||||
# Paper Only
|
|
||||||
# The maximum amount of requested chunks with the asynchronous paper chunk
|
## Usage and Configuration
|
||||||
# loading method. Higher values mean faster generation but more memory usage
|
|
||||||
# (and probably bigger performance impact).
|
### Commands
|
||||||
# The value should be a positive integer.
|
|
||||||
max-pending-chunks: 10
|
All features can be accessed with the command `/chunkmaster` or the aliases `/chm`, `chunkm`, `cmaster`.
|
||||||
|
|
||||||
# The period (in ticks) in which a generation step is run.
|
- `/chunkmaster generate [world] [radius] [<square|circle>]` Starts the generation until the specified chunk count or the world border is reached.
|
||||||
# Higher values mean less performance impact but slower generation.
|
- `/chunkmaster list` Lists all running generation tasks
|
||||||
# The value should be a positive integer.
|
- `/chunkmaster cancel <Task id|world name>` Cancels the generation task with the specified id (if it is running).
|
||||||
period: 2
|
- `/chunkmaster pause` Pauses all generation tasks until the resume command is executed.
|
||||||
|
- `/chunkmaster resume` Resumes all paused generation tasks.
|
||||||
# The max amount of chunks that should be generated per step.
|
- `/chunkmaster reload` Reloads the configuration file.
|
||||||
# Higher values mean higher generation speed but higher performance impact.
|
- `/chunkmaster tpchunk <X> <Z>` Teleports you to the specified chunk coordinates.
|
||||||
# The value should be a positive integer.
|
- `/<command> setCenter [[<world>] <chunkX> <chunkZ>]]` - sets the center chunk of the world
|
||||||
chunks-per-step: 4
|
- `/<command> getCenter [<world>]` - returns the center chunk of the world
|
||||||
|
- `/<command> stats [<world>]` - returns the stats of the server or a specific world
|
||||||
# Paper Only
|
|
||||||
# The number of already generated chunks that will be skipped for each step.
|
#### Examples
|
||||||
# Notice that these still have a performance impact because the server needs to check
|
**Generate a 100 chunks * 100 blocks square around the center:**
|
||||||
# if the chunk is generated.
|
|
||||||
# Higher values mean faster generation but greater performance impact.
|
`/chm generate [world] 50`
|
||||||
# The value should be a positive integer.
|
|
||||||
chunk-skips-per-step: 100
|
**Generate a circle with a radius of 100 blocks around the center:**
|
||||||
|
|
||||||
# The maximum milliseconds per tick the server is allowed to have
|
`/chm generate [world] 100 circle`
|
||||||
# during the cunk generation process.
|
|
||||||
# If the mspt is greather than this, the chunk generation task pauses.
|
### Config
|
||||||
# The value should be a positive integer greater than 50.
|
|
||||||
mspt-pause-threshold: 500
|
```yaml
|
||||||
|
|
||||||
# If the chunk generation process should pause on player join.
|
# The language settings.
|
||||||
# Notice that playing on a server that constantly generates chunks can be
|
# Supported out of the box are german (de) and english (en).
|
||||||
# very laggy and can cause it to crash.
|
# Additional languages can be configured in the plugins folder under i18n.
|
||||||
# You could configure the values above so that the performance impact of the generation
|
# The file name must be in the format <language>.i18n.properties and the content
|
||||||
# process is minimal.
|
# must be in the java-property-file format.
|
||||||
# The value should be a boolean <true/false>
|
# For non-defined translations the default (english) version is used.
|
||||||
pause-on-join: true
|
# For built-in support please create a PullRequest with your translation.
|
||||||
```
|
language: en
|
||||||
|
|
||||||
## Spigot and Paper
|
# Actiates/deactivates the dynmap integration.
|
||||||
|
# With the setting set to 'true' the plugin tries to trigger the rendering
|
||||||
The plugin works on spigot and paper servers but is significantly faster on paper servers
|
# of generated chunks right before unloading them. It also adds an area
|
||||||
(because it profits from asynchronous chunk loading an the better implementation of the
|
# marker to the dynmap to show the area that will be pregenerated.
|
||||||
isChunkGenerated method).
|
# The marker is removed automatically when the task is finished or canceled.
|
||||||
|
# The value should be a boolean <true/false>
|
||||||
|
dynmap: true
|
||||||
|
|
||||||
|
generation:
|
||||||
|
|
||||||
|
# If set to true the plugin ignores the vanilla world border and doesn't stop
|
||||||
|
# the chunk generation when reaching it.
|
||||||
|
# The value should be a boolean <true/false>
|
||||||
|
ignore-worldborder: false
|
||||||
|
|
||||||
|
# The maximum amount of chunks that are loaded before unloading and saving them.
|
||||||
|
# Higher values mean higher generation speed but greater memory usage.
|
||||||
|
# The value should be a positive integer.
|
||||||
|
max-loaded-chunks: 1000
|
||||||
|
|
||||||
|
# Paper Only
|
||||||
|
# The maximum amount of requested chunks with the asynchronous paper chunk
|
||||||
|
# loading method. Higher values mean faster generation but more memory usage and
|
||||||
|
# bigger performance impact. Configuring it too hight might crash the server.
|
||||||
|
# The value should be a positive integer.
|
||||||
|
max-pending-chunks: 500
|
||||||
|
|
||||||
|
# The maximum milliseconds per tick the server is allowed to have
|
||||||
|
# during the cunk generation process.
|
||||||
|
# If the mspt is greather than this, the chunk generation task pauses.
|
||||||
|
# The value should be a positive integer greater than 50.
|
||||||
|
mspt-pause-threshold: 500
|
||||||
|
|
||||||
|
# The period in ticks for how often loaded chunks get unloaded.
|
||||||
|
# Unloading happens in the main thread and can impact the server performance.
|
||||||
|
# You can tweak this setting with the max-loaded-chunks setting to have either
|
||||||
|
# a lot of chunks unloaded at once or fewer chunks unloaded more often.
|
||||||
|
# If the maximum number of loaded chunks is reached the generation pauses until the
|
||||||
|
# unloading task runs again so keep that in mind.
|
||||||
|
# The value should be a positive integer.
|
||||||
|
unloading-period: 50
|
||||||
|
|
||||||
|
# Pauses the generation if the number of players on the server is larger or equal
|
||||||
|
# to the configured value
|
||||||
|
# Notice that playing on a server that constantly generates chunks can be
|
||||||
|
# very laggy and can cause it to crash.
|
||||||
|
# The value should be a posivitve integer > 1.
|
||||||
|
pause-on-player-count: 1
|
||||||
|
|
||||||
|
# if the generation should automatically start on server startup
|
||||||
|
# the value should be a boolean
|
||||||
|
autostart: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Spigot and Paper
|
||||||
|
|
||||||
|
The plugin works on spigot and paper servers but is significantly faster on paper servers
|
||||||
|
(because it profits from asynchronous chunk loading an the better implementation of the
|
||||||
|
isChunkGenerated method).
|
||||||
|
|
||||||
|
## Translation
|
||||||
|
|
||||||
|
The **Mandarin** translation is provided by [NPBeta](https://github.com/NPBeta) and
|
||||||
|
was validated by [ed3d3d](https://twitter.com/ed3d3d).
|
||||||
|
|
||||||
|
The **French** translation is provided by [Corenb](https://github.com/Corenb) and
|
||||||
|
was validated by [Fiwel00](https://github.com/Fiwel00) and [Youssef Habri](https://github.com/youssefhabri).
|
||||||
|
|
||||||
|
The **German** and **English** translation is provided by me.
|
||||||
|
You can translate the plugin yourself and start a PR to this repository to add it to the
|
||||||
|
provided translation.
|
||||||
|
|
||||||
|
1. create an i18n folder in the plugins folder (plugins/Chunkmaster)
|
||||||
|
2. copy the [default translations file](https://github.com/Trivernis/spigot-chunkmaster/blob/master/src/main/resources/i18n/DEFAULT.i18n.properties)
|
||||||
|
into the newly created folder and rename it to <language-abbrevation>.i18n.properties
|
||||||
|
3. modify the values in the file for your translation (you can use minecraft § formatting sequences)
|
||||||
|
4. set the language property in the config file to your language abbrevation
|
||||||
|
5. start the plugin
|
||||||
|
|
||||||
|
Now you should see your translation being used by the plugin for localized messages.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the GPLv3.0 License - see the
|
||||||
|
[LICENSE](https://github.com/Trivernis/spigot-chunkmaster/blob/master/LICENSE) for details.
|
||||||
|
|
||||||
|
## bStats
|
||||||
|
|
||||||
|
[![Plugin statistics](https://bstats.org/signatures/bukkit/chunkmaster.svg)](https://bstats.org/plugin/bukkit/Chunkmaster/5639)
|
||||||
|
@ -1,79 +1,103 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "com.github.jengelman.gradle.plugins:shadow:2.0.2"
|
classpath "com.github.jengelman.gradle.plugins:shadow:2.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'idea'
|
id 'idea'
|
||||||
id 'org.jetbrains.kotlin.jvm' version '1.3.50'
|
id 'org.jetbrains.kotlin.jvm' version '1.4.10'
|
||||||
id 'com.github.johnrengelman.shadow' version '2.0.4'
|
id 'com.github.johnrengelman.shadow' version '2.0.4'
|
||||||
}
|
}
|
||||||
|
|
||||||
idea {
|
idea {
|
||||||
module {
|
module {
|
||||||
downloadJavadoc = true
|
downloadJavadoc = true
|
||||||
downloadSources = true
|
downloadSources = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group "net.trivernis"
|
group "net.trivernis"
|
||||||
version "0.12-beta"
|
version PLUGIN_VERSION
|
||||||
|
sourceCompatibility = 1.8
|
||||||
sourceCompatibility = 1.8
|
|
||||||
|
repositories {
|
||||||
repositories {
|
mavenCentral()
|
||||||
mavenCentral()
|
maven {
|
||||||
maven {
|
url "https://hub.spigotmc.org/nexus/content/repositories/snapshots"
|
||||||
url "https://hub.spigotmc.org/nexus/content/repositories/snapshots"
|
}
|
||||||
}
|
maven {
|
||||||
maven {
|
url 'https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc'
|
||||||
url 'https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc'
|
}
|
||||||
}
|
maven {
|
||||||
maven {
|
name 'papermc'
|
||||||
name 'papermc'
|
url 'https://papermc.io/repo/repository/maven-public/'
|
||||||
url 'https://papermc.io/repo/repository/maven-public/'
|
}
|
||||||
}
|
maven {
|
||||||
maven {
|
name 'CodeMc'
|
||||||
name 'CodeMc'
|
url 'https://repo.codemc.org/repository/maven-public'
|
||||||
url 'https://repo.codemc.org/repository/maven-public'
|
}
|
||||||
}
|
|
||||||
}
|
maven {
|
||||||
|
name 'mikeprimm'
|
||||||
dependencies {
|
url 'https://repo.mikeprimm.com'
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
}
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
|
||||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
maven {
|
||||||
compileOnly "org.spigotmc:spigot-api:1.14.4-R0.1-SNAPSHOT"
|
url 'https://jitpack.io'
|
||||||
compile group: 'org.xerial', name: 'sqlite-jdbc', version: '3.28.0'
|
}
|
||||||
compile "io.papermc:paperlib:1.0.2"
|
}
|
||||||
compile "org.bstats:bstats-bukkit:1.5"
|
|
||||||
}
|
dependencies {
|
||||||
|
compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
apply plugin: "com.github.johnrengelman.shadow"
|
compileOnly "com.destroystokyo.paper:paper-api:1.14.4-R0.1-SNAPSHOT"
|
||||||
apply plugin: 'java'
|
compileOnly "org.dynmap:dynmap-api:2.0"
|
||||||
|
compileOnly group: 'org.xerial', name: 'sqlite-jdbc', version: '3.28.0'
|
||||||
shadowJar {
|
|
||||||
relocate 'io.papermc.lib', 'net.trivernis.chunkmaster.paperlib'
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
relocate 'org.bstats', 'net.trivernis.chunkmaster.bstats'
|
implementation "io.papermc:paperlib:1.0.6"
|
||||||
}
|
implementation "org.bstats:bstats-bukkit:1.5"
|
||||||
|
|
||||||
jar {
|
testImplementation group: 'junit', name: 'junit', version: '4.12'
|
||||||
from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
|
testImplementation 'io.kotest:kotest-runner-junit5:4.3.2'
|
||||||
}
|
testImplementation 'io.mockk:mockk:1.10.6'
|
||||||
|
testImplementation "org.dynmap:dynmap-api:2.0"
|
||||||
compileKotlin {
|
}
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "1.8"
|
apply plugin: "com.github.johnrengelman.shadow"
|
||||||
}
|
apply plugin: 'java'
|
||||||
}
|
|
||||||
|
shadowJar {
|
||||||
compileTestKotlin {
|
relocate 'io.papermc.lib', 'net.trivernis.chunkmaster.paperlib'
|
||||||
kotlinOptions {
|
relocate 'org.bstats', 'net.trivernis.chunkmaster.bstats'
|
||||||
jvmTarget = "1.8"
|
relocate 'kotlin', 'net.trivernis.chunkmaster.kotlin'
|
||||||
}
|
relocate 'org.intellij', 'net.trivernis.chunkmaster.intellij'
|
||||||
|
relocate 'org.jetbrains', 'net.trivernis.chunkmaster.jetbrains'
|
||||||
|
}
|
||||||
|
|
||||||
|
processResources {
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||||
|
with copySpec {
|
||||||
|
from 'src/main/resources/'
|
||||||
|
include 'plugin.yml'
|
||||||
|
filter { String line ->
|
||||||
|
line.replace('$$PLUGIN_VERSION$$', PLUGIN_VERSION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
}
|
}
|
@ -1 +1,2 @@
|
|||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
|
PLUGIN_VERSION=1.4.2
|
Binary file not shown.
@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# 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"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
which java >/dev/null 2>&1 || 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
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=`expr $i + 1`
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
@ -0,0 +1,89 @@
|
|||||||
|
@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
|
||||||
|
|
||||||
|
@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
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@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%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="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!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
@ -1,2 +1,2 @@
|
|||||||
rootProject.name = 'chunkmaster'
|
rootProject.name = 'chunkmaster'
|
||||||
|
|
||||||
|
@ -1,91 +1,118 @@
|
|||||||
package net.trivernis.chunkmaster
|
package net.trivernis.chunkmaster
|
||||||
|
|
||||||
import io.papermc.lib.PaperLib
|
import io.papermc.lib.PaperLib
|
||||||
import net.trivernis.chunkmaster.commands.*
|
import net.trivernis.chunkmaster.commands.CommandChunkmaster
|
||||||
import net.trivernis.chunkmaster.lib.generation.GenerationManager
|
import net.trivernis.chunkmaster.lib.LanguageManager
|
||||||
import net.trivernis.chunkmaster.lib.SqlUpdateManager
|
import net.trivernis.chunkmaster.lib.database.SqliteManager
|
||||||
import org.bstats.bukkit.Metrics
|
import net.trivernis.chunkmaster.lib.generation.GenerationManager
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bstats.bukkit.Metrics
|
||||||
import org.bukkit.scheduler.BukkitTask
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
import java.lang.Exception
|
import org.bukkit.scheduler.BukkitTask
|
||||||
import java.sql.Connection
|
import org.dynmap.DynmapAPI
|
||||||
import java.sql.DriverManager
|
|
||||||
|
open class Chunkmaster : JavaPlugin() {
|
||||||
class Chunkmaster: JavaPlugin() {
|
lateinit var sqliteManager: SqliteManager
|
||||||
lateinit var sqliteConnection: Connection
|
lateinit var generationManager: GenerationManager
|
||||||
var dbname: String? = null
|
lateinit var langManager: LanguageManager
|
||||||
lateinit var generationManager: GenerationManager
|
private lateinit var tpsTask: BukkitTask
|
||||||
private lateinit var tpsTask: BukkitTask
|
var dynmapApi: DynmapAPI? = null
|
||||||
var mspt = 50L // keep track of the milliseconds per tick
|
private set
|
||||||
private set
|
var mspt = 20 // keep track of the milliseconds per tick
|
||||||
|
private set
|
||||||
/**
|
|
||||||
* On enable of the plugin
|
/**
|
||||||
*/
|
* On enable of the plugin
|
||||||
override fun onEnable() {
|
*/
|
||||||
PaperLib.suggestPaper(this)
|
override fun onEnable() {
|
||||||
configure()
|
PaperLib.suggestPaper(this)
|
||||||
|
logger.fine("LogLevel: FINE")
|
||||||
val metrics = Metrics(this)
|
logger.finer("LogLevel: FINER")
|
||||||
|
logger.finest("LogLevel: FINEST")
|
||||||
initDatabase()
|
configure()
|
||||||
generationManager = GenerationManager(this, server)
|
|
||||||
generationManager.init()
|
Metrics(this)
|
||||||
|
|
||||||
getCommand("chunkmaster")?.aliases = mutableListOf("chm", "chunkm", "cmaster")
|
langManager = LanguageManager(this)
|
||||||
getCommand("chunkmaster")?.setExecutor(CommandChunkmaster(this, server))
|
langManager.loadProperties()
|
||||||
|
|
||||||
server.pluginManager.registerEvents(ChunkmasterEvents(this, server), this)
|
this.dynmapApi = getDynmap()
|
||||||
|
|
||||||
tpsTask = server.scheduler.runTaskTimer(this, Runnable {
|
initDatabase()
|
||||||
val start = System.currentTimeMillis()
|
generationManager = GenerationManager(this, server)
|
||||||
server.scheduler.runTaskLater(this, Runnable {
|
generationManager.init()
|
||||||
mspt = System.currentTimeMillis() - start
|
|
||||||
}, 1)
|
getCommand("chunkmaster")?.aliases = mutableListOf("chm", "chunkm", "cmaster")
|
||||||
}, 1, 300)
|
getCommand("chunkmaster")?.setExecutor(CommandChunkmaster(this, server))
|
||||||
}
|
|
||||||
|
server.pluginManager.registerEvents(ChunkmasterEvents(this, server), this)
|
||||||
/**
|
|
||||||
* Stop all tasks and close database connection on disable
|
if (PaperLib.isPaper() && PaperLib.getMinecraftPatchVersion() >= 225) {
|
||||||
*/
|
tpsTask = server.scheduler.runTaskTimer(this, Runnable {
|
||||||
override fun onDisable() {
|
mspt = 1000 / server.currentTick // use papers exposed tick rather than calculating it
|
||||||
logger.info("Stopping all generation tasks...")
|
}, 1, 300)
|
||||||
generationManager.stopAll()
|
} else {
|
||||||
sqliteConnection.close()
|
tpsTask = server.scheduler.runTaskTimer(this, Runnable {
|
||||||
}
|
val start = System.currentTimeMillis()
|
||||||
|
server.scheduler.runTaskLater(this, Runnable {
|
||||||
/**
|
mspt = (System.currentTimeMillis() - start).toInt()
|
||||||
* Cofigure the config file
|
}, 1)
|
||||||
*/
|
}, 1, 300)
|
||||||
private fun configure() {
|
}
|
||||||
dataFolder.mkdir()
|
}
|
||||||
config.addDefault("generation.period", 2L)
|
|
||||||
config.addDefault("generation.chunks-per-step", 2)
|
/**
|
||||||
config.addDefault("generation.chunk-skips-per-step", 100)
|
* Stop all tasks and close database connection on disable
|
||||||
config.addDefault("generation.mspt-pause-threshold", 500L)
|
*/
|
||||||
config.addDefault("generation.pause-on-join", true)
|
override fun onDisable() {
|
||||||
config.addDefault("generation.max-pending-chunks", 10)
|
logger.info(langManager.getLocalized("STOPPING_ALL_TASKS"))
|
||||||
config.addDefault("generation.max-loaded-chunks", 10)
|
generationManager.stopAll()
|
||||||
config.options().copyDefaults(true)
|
server.scheduler.cancelTasks(this)
|
||||||
saveConfig()
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Cofigure the config file
|
||||||
* Initializes the database
|
*/
|
||||||
*/
|
private fun configure() {
|
||||||
private fun initDatabase() {
|
dataFolder.mkdir()
|
||||||
logger.info("Initializing Database...")
|
config.addDefault("generation.mspt-pause-threshold", 500L)
|
||||||
try {
|
config.addDefault("generation.pause-on-player-count", 1)
|
||||||
Class.forName("org.sqlite.JDBC")
|
config.addDefault("generation.max-pending-chunks", 500)
|
||||||
sqliteConnection = DriverManager.getConnection("jdbc:sqlite:${dataFolder.absolutePath}/chunkmaster.db")
|
config.addDefault("generation.max-loaded-chunks", 1000)
|
||||||
logger.info("Database connection established.")
|
config.addDefault("generation.unloading-period", 50L)
|
||||||
|
config.addDefault("generation.ignore-worldborder", false)
|
||||||
val updateManager = SqlUpdateManager(sqliteConnection, this)
|
config.addDefault("generation.autostart", true)
|
||||||
updateManager.checkUpdate()
|
config.addDefault("database.filename", "chunkmaster.db")
|
||||||
updateManager.performUpdate()
|
config.addDefault("language", "en")
|
||||||
logger.info("Database fully initialized.")
|
config.addDefault("dynmap", true)
|
||||||
} catch(e: Exception) {
|
config.options().copyDefaults(true)
|
||||||
logger.warning("Failed to init database: ${e.message}")
|
saveConfig()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Initializes the database
|
||||||
|
*/
|
||||||
|
private fun initDatabase() {
|
||||||
|
logger.info(langManager.getLocalized("DB_INIT"))
|
||||||
|
try {
|
||||||
|
this.sqliteManager = SqliteManager(this)
|
||||||
|
sqliteManager.init()
|
||||||
|
logger.info(langManager.getLocalized("DB_INIT_FINISHED"))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.warning(langManager.getLocalized("DB_INIT_EROR", e.message!!))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDynmap(): DynmapAPI? {
|
||||||
|
return try {
|
||||||
|
val dynmap = server.pluginManager.getPlugin("dynmap")
|
||||||
|
if (dynmap != null && dynmap is DynmapAPI) {
|
||||||
|
logger.info(langManager.getLocalized("PLUGIN_DETECTED", "dynmap", dynmap.dynmapVersion))
|
||||||
|
dynmap
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} catch (e: IllegalStateException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,68 +1,50 @@
|
|||||||
package net.trivernis.chunkmaster
|
package net.trivernis.chunkmaster
|
||||||
|
|
||||||
import net.trivernis.chunkmaster.lib.generation.GenerationTaskPaper
|
import org.bukkit.Server
|
||||||
import org.bukkit.Server
|
import org.bukkit.event.EventHandler
|
||||||
import org.bukkit.event.EventHandler
|
import org.bukkit.event.Listener
|
||||||
import org.bukkit.event.Listener
|
import org.bukkit.event.player.PlayerJoinEvent
|
||||||
import org.bukkit.event.player.PlayerJoinEvent
|
import org.bukkit.event.player.PlayerQuitEvent
|
||||||
import org.bukkit.event.player.PlayerQuitEvent
|
|
||||||
import org.bukkit.event.world.WorldSaveEvent
|
class ChunkmasterEvents(private val chunkmaster: Chunkmaster, private val server: Server) : Listener {
|
||||||
|
|
||||||
class ChunkmasterEvents(private val chunkmaster: Chunkmaster, private val server: Server) : Listener {
|
private val pauseOnPlayerCount: Int
|
||||||
|
get() {
|
||||||
private val pauseOnJoin = chunkmaster.config.getBoolean("generation.pause-on-join")
|
return chunkmaster.config.getInt("generation.pause-on-player-count")
|
||||||
private var playerPaused = false
|
}
|
||||||
|
private var playerPaused = false
|
||||||
/**
|
|
||||||
* Autostart generation tasks
|
/**
|
||||||
*/
|
* Autostart generation tasks
|
||||||
@EventHandler
|
*/
|
||||||
fun onPlayerQuit(event: PlayerQuitEvent) {
|
@EventHandler
|
||||||
if (pauseOnJoin) {
|
fun onPlayerQuit(event: PlayerQuitEvent) {
|
||||||
if (server.onlinePlayers.size == 1 && server.onlinePlayers.contains(event.player) ||
|
if (server.onlinePlayers.size == pauseOnPlayerCount) {
|
||||||
server.onlinePlayers.isEmpty()
|
if (!playerPaused) {
|
||||||
) {
|
if (chunkmaster.generationManager.pausedTasks.isNotEmpty()) {
|
||||||
if (!playerPaused) {
|
chunkmaster.logger.info(chunkmaster.langManager.getLocalized("RESUME_PLAYER_LEAVE"))
|
||||||
if (chunkmaster.generationManager.pausedTasks.isNotEmpty()) {
|
}
|
||||||
chunkmaster.logger.info("Server is empty. Resuming chunk generation tasks.")
|
chunkmaster.generationManager.resumeAll()
|
||||||
}
|
} else if (chunkmaster.generationManager.paused) {
|
||||||
chunkmaster.generationManager.resumeAll()
|
chunkmaster.logger.info(chunkmaster.langManager.getLocalized("PAUSE_MANUALLY"))
|
||||||
} else if (chunkmaster.generationManager.paused){
|
playerPaused = chunkmaster.generationManager.paused
|
||||||
chunkmaster.logger.info("Generation was manually paused. Not resuming automatically.")
|
}
|
||||||
playerPaused = chunkmaster.generationManager.paused
|
}
|
||||||
} else {
|
}
|
||||||
chunkmaster.logger.info("Generation tasks are already running.")
|
|
||||||
}
|
/**
|
||||||
}
|
* Autostop generation tasks
|
||||||
}
|
*/
|
||||||
}
|
@EventHandler
|
||||||
|
fun onPlayerJoin(event: PlayerJoinEvent) {
|
||||||
/**
|
if (server.onlinePlayers.size >= pauseOnPlayerCount) {
|
||||||
* Autostop generation tasks
|
if (chunkmaster.generationManager.tasks.isNotEmpty()) {
|
||||||
*/
|
chunkmaster.logger.info(chunkmaster.langManager.getLocalized("PAUSE_PLAYER_JOIN"))
|
||||||
@EventHandler
|
}
|
||||||
fun onPlayerJoin(event: PlayerJoinEvent) {
|
if (!chunkmaster.generationManager.paused) {
|
||||||
if (pauseOnJoin) {
|
playerPaused = chunkmaster.generationManager.paused
|
||||||
if (server.onlinePlayers.size == 1 || server.onlinePlayers.isEmpty()) {
|
chunkmaster.generationManager.pauseAll()
|
||||||
if (chunkmaster.generationManager.tasks.isNotEmpty()) {
|
}
|
||||||
chunkmaster.logger.info("Pausing generation tasks because of player join.")
|
}
|
||||||
}
|
}
|
||||||
playerPaused = chunkmaster.generationManager.paused
|
|
||||||
chunkmaster.generationManager.pauseAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unload all chunks before a save.
|
|
||||||
*/
|
|
||||||
@EventHandler
|
|
||||||
fun onWorldSave(event: WorldSaveEvent) {
|
|
||||||
val task = chunkmaster.generationManager.tasks.find { it.generationTask.world == event.world }
|
|
||||||
if (task != null) {
|
|
||||||
if (task.generationTask is GenerationTaskPaper) {
|
|
||||||
task.generationTask.unloadAllChunks()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,48 +1,51 @@
|
|||||||
package net.trivernis.chunkmaster.commands
|
package net.trivernis.chunkmaster.commands
|
||||||
|
|
||||||
import net.md_5.bungee.api.ChatColor
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
import net.md_5.bungee.api.chat.ComponentBuilder
|
import net.trivernis.chunkmaster.lib.Subcommand
|
||||||
import net.trivernis.chunkmaster.Chunkmaster
|
import org.bukkit.command.Command
|
||||||
import net.trivernis.chunkmaster.lib.Subcommand
|
import org.bukkit.command.CommandSender
|
||||||
import net.trivernis.chunkmaster.lib.generation.TaskEntry
|
|
||||||
import org.bukkit.command.Command
|
class CmdCancel(private val chunkmaster: Chunkmaster) : Subcommand {
|
||||||
import org.bukkit.command.CommandSender
|
override val name = "cancel"
|
||||||
|
|
||||||
class CmdCancel(private val chunkmaster: Chunkmaster): Subcommand {
|
/**
|
||||||
override val name = "cancel"
|
* TabComplete for subcommand cancel.
|
||||||
|
*/
|
||||||
/**
|
override fun onTabComplete(
|
||||||
* TabComplete for subcommand cancel.
|
sender: CommandSender,
|
||||||
*/
|
command: Command,
|
||||||
override fun onTabComplete(
|
alias: String,
|
||||||
sender: CommandSender,
|
args: List<String>
|
||||||
command: Command,
|
): MutableList<String> {
|
||||||
alias: String,
|
val genManager = chunkmaster.generationManager
|
||||||
args: List<String>
|
val allTasks = genManager.allTasks
|
||||||
): MutableList<String> {
|
return allTasks.filter { it.id.toString().indexOf(args[0]) == 0 }
|
||||||
val genManager = chunkmaster.generationManager
|
.map { it.id.toString() }.toMutableList()
|
||||||
val allTasks = genManager.allTasks
|
}
|
||||||
return allTasks.filter {it.id.toString().indexOf(args[0]) == 0}
|
|
||||||
.map { it.id.toString() }.toMutableList()
|
/**
|
||||||
}
|
* Cancels the generation task if it exists.
|
||||||
|
*/
|
||||||
/**
|
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
||||||
* Cancels the generation task if it exists.
|
return if (args.isNotEmpty()) {
|
||||||
*/
|
val entry = chunkmaster.generationManager.tasks.find { it.generationTask.world.name == args[0] }
|
||||||
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
val index = if (args[0].toIntOrNull() != null && entry == null) {
|
||||||
return if (args.isNotEmpty() && args[0].toIntOrNull() != null) {
|
args[0].toInt()
|
||||||
if (chunkmaster.generationManager.removeTask(args[0].toInt())) {
|
} else {
|
||||||
sender.sendMessage("Task ${args[0]} canceled.")
|
entry?.id
|
||||||
true
|
}
|
||||||
} else {
|
|
||||||
sender.spigot().sendMessage(*ComponentBuilder("Task ${args[0]} not found!")
|
if (index != null && chunkmaster.generationManager.removeTask(index)) {
|
||||||
.color(ChatColor.RED).create())
|
sender.sendMessage(chunkmaster.langManager.getLocalized("TASK_CANCELLED", index))
|
||||||
false
|
true
|
||||||
}
|
} else {
|
||||||
} else {
|
sender.sendMessage(chunkmaster.langManager.getLocalized("TASK_NOT_FOUND", args[0]))
|
||||||
sender.spigot().sendMessage(*ComponentBuilder("You need to provide a task id to cancel.")
|
false
|
||||||
.color(ChatColor.RED).create())
|
}
|
||||||
false
|
|
||||||
}
|
} else {
|
||||||
}
|
sender.sendMessage(chunkmaster.langManager.getLocalized("TASK_ID_REQUIRED"))
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package net.trivernis.chunkmaster.commands
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
|
import net.trivernis.chunkmaster.lib.Subcommand
|
||||||
|
import org.bukkit.command.Command
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
|
||||||
|
class CmdCompleted(private val plugin: Chunkmaster) : Subcommand {
|
||||||
|
override val name = "completed"
|
||||||
|
|
||||||
|
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
||||||
|
plugin.sqliteManager.completedGenerationTasks.getCompletedTasks().thenAccept { tasks ->
|
||||||
|
val worlds = tasks.map { it.world }.toHashSet()
|
||||||
|
var response = "\n" + plugin.langManager.getLocalized("COMPLETED_TASKS_HEADER") + "\n\n"
|
||||||
|
|
||||||
|
for (world in worlds) {
|
||||||
|
response += plugin.langManager.getLocalized("COMPLETED_WORLD_HEADER", world) + "\n"
|
||||||
|
|
||||||
|
for (task in tasks.filter { it.world == world }) {
|
||||||
|
response += plugin.langManager.getLocalized(
|
||||||
|
"COMPLETED_TASK_ENTRY",
|
||||||
|
task.id,
|
||||||
|
task.radius,
|
||||||
|
task.center.x,
|
||||||
|
task.center.z,
|
||||||
|
task.shape
|
||||||
|
) + "\n"
|
||||||
|
}
|
||||||
|
response += "\n"
|
||||||
|
}
|
||||||
|
sender.sendMessage(response)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTabComplete(
|
||||||
|
sender: CommandSender,
|
||||||
|
command: Command,
|
||||||
|
alias: String,
|
||||||
|
args: List<String>
|
||||||
|
): MutableList<String> {
|
||||||
|
return mutableListOf()
|
||||||
|
}
|
||||||
|
}
|
@ -1,133 +1,123 @@
|
|||||||
package net.trivernis.chunkmaster.commands
|
package net.trivernis.chunkmaster.commands
|
||||||
|
|
||||||
import net.md_5.bungee.api.ChatColor
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
import net.md_5.bungee.api.chat.ComponentBuilder
|
import net.trivernis.chunkmaster.lib.Subcommand
|
||||||
import net.trivernis.chunkmaster.Chunkmaster
|
import org.bukkit.command.Command
|
||||||
import net.trivernis.chunkmaster.lib.Subcommand
|
import org.bukkit.command.CommandSender
|
||||||
import org.bukkit.command.Command
|
import org.bukkit.entity.Player
|
||||||
import org.bukkit.command.CommandSender
|
|
||||||
import org.bukkit.entity.Player
|
class CmdGenerate(private val chunkmaster: Chunkmaster) : Subcommand {
|
||||||
import kotlin.math.pow
|
override val name = "generate"
|
||||||
|
|
||||||
class CmdGenerate(private val chunkmaster: Chunkmaster): Subcommand {
|
/**
|
||||||
override val name = "generate"
|
* TabComplete for generate command.
|
||||||
|
*/
|
||||||
/**
|
override fun onTabComplete(
|
||||||
* TabComplete for generate command.
|
sender: CommandSender,
|
||||||
*/
|
command: Command,
|
||||||
override fun onTabComplete(
|
alias: String,
|
||||||
sender: CommandSender,
|
args: List<String>
|
||||||
command: Command,
|
): MutableList<String> {
|
||||||
alias: String,
|
if (args.size == 1) {
|
||||||
args: List<String>
|
return sender.server.worlds.filter { it.name.indexOf(args[0]) == 0 }
|
||||||
): MutableList<String> {
|
.map { it.name }.toMutableList()
|
||||||
if (args.size == 1) {
|
} else if (args.size == 2) {
|
||||||
return sender.server.worlds.filter { it.name.indexOf(args[0]) == 0 }
|
if (args[0].toIntOrNull() != null) {
|
||||||
.map {it.name}.toMutableList()
|
return shapes.filter { it.indexOf(args[1]) == 0 }.toMutableList()
|
||||||
} else if (args.size == 2) {
|
}
|
||||||
if (args[0].toIntOrNull() != null) {
|
} else if (args.size > 2) {
|
||||||
return units.filter {it.indexOf(args[1]) == 0}.toMutableList()
|
if (args[1].toIntOrNull() != null) {
|
||||||
}
|
return shapes.filter { it.indexOf(args[2]) == 0 }.toMutableList()
|
||||||
} else if (args.size > 2) {
|
}
|
||||||
if (args[1].toIntOrNull() != null) {
|
}
|
||||||
return units.filter {it.indexOf(args[2]) == 0}.toMutableList()
|
return emptyList<String>().toMutableList()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return emptyList<String>().toMutableList()
|
val shapes = listOf("circle", "square")
|
||||||
}
|
|
||||||
val units = listOf("blockradius", "radius", "diameter")
|
|
||||||
|
/**
|
||||||
|
* Creates a new generation task for the world and chunk count.
|
||||||
/**
|
*/
|
||||||
* Creates a new generation task for the world and chunk count.
|
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
||||||
*/
|
var worldName = ""
|
||||||
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
var blockRadius = -1
|
||||||
var worldName = ""
|
var shape = "square"
|
||||||
var stopAfter = -1
|
|
||||||
if (sender is Player) {
|
if (sender is Player) {
|
||||||
if (args.isNotEmpty()) {
|
worldName = sender.world.name
|
||||||
if (args[0].toIntOrNull() != null) {
|
}
|
||||||
stopAfter = args[0].toInt()
|
if (args.isEmpty()) {
|
||||||
worldName = sender.world.name
|
if (sender is Player) {
|
||||||
} else {
|
return createTask(sender, worldName, blockRadius, shape)
|
||||||
worldName = args[0]
|
} else {
|
||||||
}
|
sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NAME_REQUIRED"))
|
||||||
if (args.size > 1) {
|
return false
|
||||||
if (args[1].toIntOrNull() != null) {
|
}
|
||||||
stopAfter = args[1].toInt()
|
}
|
||||||
} else if (args[1] in units && args[0].toIntOrNull() != null) {
|
if (args[0].toIntOrNull() != null && sender.server.worlds.find { it.name == args[0] } == null) {
|
||||||
stopAfter = getStopAfter(stopAfter, args[1])
|
if (sender !is Player) {
|
||||||
} else {
|
sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NAME_REQUIRED"))
|
||||||
worldName = args[1]
|
return false
|
||||||
}
|
}
|
||||||
}
|
blockRadius = args[0].toInt()
|
||||||
if (args.size > 2 && args[2] in units && args[1].toIntOrNull() != null) {
|
} else {
|
||||||
stopAfter = getStopAfter(stopAfter, args[2])
|
worldName = args[0]
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
worldName = sender.world.name
|
if (args.size == 1) {
|
||||||
}
|
return createTask(sender, worldName, blockRadius, shape)
|
||||||
} else {
|
}
|
||||||
if (args.isNotEmpty()) {
|
|
||||||
worldName = args[0]
|
when {
|
||||||
if (args.size > 1) {
|
args[1].toIntOrNull() != null -> blockRadius = args[1].toInt()
|
||||||
if (args[1].toIntOrNull() != null) {
|
args[1] in shapes -> shape = args[1]
|
||||||
stopAfter = args[1].toInt()
|
else -> {
|
||||||
}
|
sender.sendMessage(chunkmaster.langManager.getLocalized("INVALID_ARGUMENT", 2, args[1]))
|
||||||
}
|
return false
|
||||||
if (args.size > 2 && args[2] in units) {
|
}
|
||||||
stopAfter = getStopAfter(stopAfter, args[2])
|
}
|
||||||
}
|
if (args.size == 2) {
|
||||||
} else {
|
return createTask(sender, worldName, blockRadius, shape)
|
||||||
sender.spigot().sendMessage(
|
}
|
||||||
*ComponentBuilder("You need to provide a world name").color(ChatColor.RED).create())
|
if (args[2] in shapes) {
|
||||||
return false
|
shape = args[2]
|
||||||
}
|
} else {
|
||||||
}
|
sender.sendMessage(chunkmaster.langManager.getLocalized("INVALID_ARGUMENT", 3, args[2]))
|
||||||
return createTask(sender, worldName, stopAfter)
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return createTask(sender, worldName, blockRadius, shape)
|
||||||
* Returns stopAfter for a given unit
|
}
|
||||||
*/
|
|
||||||
private fun getStopAfter(number: Int, unit: String): Int {
|
/**
|
||||||
if (unit in units) {
|
* Creates the task with the given arguments.
|
||||||
return when (unit) {
|
*/
|
||||||
"radius" -> {
|
private fun createTask(sender: CommandSender, worldName: String, blockRadius: Int, shape: String): Boolean {
|
||||||
((number * 2)+1).toDouble().pow(2.0).toInt()
|
val world = chunkmaster.server.getWorld(worldName)
|
||||||
}
|
val allTasks = chunkmaster.generationManager.allTasks
|
||||||
"diameter" -> {
|
return if (world != null && (allTasks.find { it.generationTask.world == world }) == null) {
|
||||||
number.toDouble().pow(2.0).toInt()
|
chunkmaster.generationManager.addTask(world, if (blockRadius > 0) blockRadius / 16 else -1, shape)
|
||||||
}
|
sender.sendMessage(
|
||||||
"blockradius" -> {
|
chunkmaster.langManager
|
||||||
((number.toDouble()+1)/8).pow(2.0).toInt()
|
.getLocalized(
|
||||||
}
|
"TASK_CREATION_SUCCESS",
|
||||||
else -> number
|
worldName,
|
||||||
}
|
if (blockRadius > 0) {
|
||||||
}
|
chunkmaster.langManager.getLocalized("TASK_UNIT_RADIUS", blockRadius)
|
||||||
return number
|
} else {
|
||||||
}
|
chunkmaster.langManager.getLocalized("TASK_UNIT_WORLDBORDER")
|
||||||
|
},
|
||||||
/**
|
shape
|
||||||
* Creates the task with the given arguments.
|
)
|
||||||
*/
|
)
|
||||||
private fun createTask(sender: CommandSender, worldName: String, stopAfter: Int): Boolean {
|
true
|
||||||
val world = chunkmaster.server.getWorld(worldName)
|
} else if (world == null) {
|
||||||
val allTasks = chunkmaster.generationManager.allTasks
|
sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NOT_FOUND", worldName))
|
||||||
return if (world != null && (allTasks.find { it.generationTask.world == world }) == null) {
|
false
|
||||||
chunkmaster.generationManager.addTask(world, stopAfter)
|
} else {
|
||||||
sender.spigot().sendMessage(*ComponentBuilder("Generation task for world ").color(ChatColor.BLUE)
|
sender.sendMessage(chunkmaster.langManager.getLocalized("TASK_ALREADY_EXISTS", worldName))
|
||||||
.append(worldName).color(ChatColor.GREEN).append(" until ").color(ChatColor.BLUE)
|
return false
|
||||||
.append(if (stopAfter > 0) "$stopAfter chunks" else "WorldBorder").color(ChatColor.GREEN)
|
}
|
||||||
.append(" successfully created").color(ChatColor.BLUE).create())
|
}
|
||||||
true
|
|
||||||
} else if (world == null){
|
|
||||||
sender.spigot().sendMessage(*ComponentBuilder("World ").color(ChatColor.RED)
|
|
||||||
.append(worldName).color(ChatColor.GREEN).append(" not found!").color(ChatColor.RED).create())
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
sender.spigot().sendMessage(*ComponentBuilder("Task already exists!").color(ChatColor.RED).create())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package net.trivernis.chunkmaster.commands
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
|
import net.trivernis.chunkmaster.lib.Subcommand
|
||||||
|
import org.bukkit.command.Command
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
class CmdGetCenter(private val chunkmaster: Chunkmaster) : Subcommand {
|
||||||
|
override val name = "getCenter"
|
||||||
|
|
||||||
|
override fun onTabComplete(
|
||||||
|
sender: CommandSender,
|
||||||
|
command: Command,
|
||||||
|
alias: String,
|
||||||
|
args: List<String>
|
||||||
|
): MutableList<String> {
|
||||||
|
if (args.size == 1) {
|
||||||
|
return sender.server.worlds.filter { it.name.indexOf(args[0]) == 0 }
|
||||||
|
.map { it.name }.toMutableList()
|
||||||
|
}
|
||||||
|
return emptyList<String>().toMutableList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
||||||
|
val worldName: String = if (sender is Player) {
|
||||||
|
if (args.isNotEmpty()) {
|
||||||
|
args[0]
|
||||||
|
} else {
|
||||||
|
sender.world.name
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (args.isEmpty()) {
|
||||||
|
sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NAME_REQUIRED"))
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
args[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendCenterInfo(sender, worldName)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the center information
|
||||||
|
*/
|
||||||
|
private fun sendCenterInfo(sender: CommandSender, worldName: String) {
|
||||||
|
chunkmaster.generationManager.worldProperties.getWorldCenter(worldName).thenAccept { worldCenter ->
|
||||||
|
var center = worldCenter
|
||||||
|
if (center == null) {
|
||||||
|
val world = sender.server.worlds.find { it.name == worldName }
|
||||||
|
if (world == null) {
|
||||||
|
sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NOT_FOUND", worldName))
|
||||||
|
return@thenAccept
|
||||||
|
}
|
||||||
|
center = Pair(world.spawnLocation.chunk.x, world.spawnLocation.chunk.z)
|
||||||
|
}
|
||||||
|
sender.sendMessage(
|
||||||
|
chunkmaster.langManager.getLocalized(
|
||||||
|
"CENTER_INFO",
|
||||||
|
worldName,
|
||||||
|
center.first,
|
||||||
|
center.second
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,63 +1,66 @@
|
|||||||
package net.trivernis.chunkmaster.commands
|
package net.trivernis.chunkmaster.commands
|
||||||
|
|
||||||
import net.md_5.bungee.api.ChatColor
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
import net.md_5.bungee.api.chat.ComponentBuilder
|
import net.trivernis.chunkmaster.lib.Subcommand
|
||||||
import net.trivernis.chunkmaster.Chunkmaster
|
import net.trivernis.chunkmaster.lib.generation.taskentry.TaskEntry
|
||||||
import net.trivernis.chunkmaster.lib.Subcommand
|
import org.bukkit.command.Command
|
||||||
import org.bukkit.command.Command
|
import org.bukkit.command.CommandSender
|
||||||
import org.bukkit.command.CommandSender
|
import kotlin.math.ceil
|
||||||
|
|
||||||
class CmdList(private val chunkmaster: Chunkmaster): Subcommand {
|
class CmdList(private val chunkmaster: Chunkmaster) : Subcommand {
|
||||||
override val name = "list"
|
override val name = "list"
|
||||||
|
|
||||||
override fun onTabComplete(
|
override fun onTabComplete(
|
||||||
sender: CommandSender,
|
sender: CommandSender,
|
||||||
command: Command,
|
command: Command,
|
||||||
alias: String,
|
alias: String,
|
||||||
args: List<String>
|
args: List<String>
|
||||||
): MutableList<String> {
|
): MutableList<String> {
|
||||||
return emptyList<String>().toMutableList()
|
return emptyList<String>().toMutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists all running or paused generation tasks.
|
* Lists all running or paused generation tasks.
|
||||||
*/
|
*/
|
||||||
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
||||||
val runningTasks = chunkmaster.generationManager.tasks
|
val runningTasks = chunkmaster.generationManager.tasks
|
||||||
val pausedTasks = chunkmaster.generationManager.pausedTasks
|
val pausedTasks = chunkmaster.generationManager.pausedTasks
|
||||||
|
|
||||||
if (runningTasks.isEmpty() && pausedTasks.isEmpty()) {
|
if (runningTasks.isEmpty() && pausedTasks.isEmpty()) {
|
||||||
sender.spigot().sendMessage(*ComponentBuilder("There are no generation tasks.")
|
sender.sendMessage(chunkmaster.langManager.getLocalized("NO_GENERATION_TASKS"))
|
||||||
.color(ChatColor.BLUE).create())
|
} else if (runningTasks.isEmpty()) {
|
||||||
} else if (runningTasks.isEmpty()) {
|
var response = chunkmaster.langManager.getLocalized("PAUSED_TASKS_HEADER")
|
||||||
val response = ComponentBuilder("Currently paused generation tasks:").color(ChatColor.WHITE).bold(true)
|
for (taskEntry in pausedTasks) {
|
||||||
for (taskEntry in pausedTasks) {
|
response += getGenerationEntry(taskEntry)
|
||||||
val genTask = taskEntry.generationTask
|
}
|
||||||
response.append("\n - ").color(ChatColor.WHITE).bold(false)
|
sender.sendMessage(response)
|
||||||
response.append("#${taskEntry.id}").color(ChatColor.BLUE).append(" - ").color(ChatColor.WHITE)
|
} else {
|
||||||
response.append(genTask.world.name).color(ChatColor.GREEN).append(" - Progress: ").color(ChatColor.WHITE)
|
var response = chunkmaster.langManager.getLocalized("RUNNING_TASKS_HEADER")
|
||||||
response.append("${genTask.count} chunks").color(ChatColor.BLUE)
|
for (task in runningTasks) {
|
||||||
|
response += getGenerationEntry(task)
|
||||||
if (genTask.stopAfter > 0) {
|
}
|
||||||
response.append(" (${(genTask.count.toDouble()/genTask.stopAfter.toDouble())*100}%).")
|
sender.sendMessage(response)
|
||||||
}
|
}
|
||||||
}
|
return true
|
||||||
sender.spigot().sendMessage(*response.create())
|
}
|
||||||
} else {
|
|
||||||
val response = ComponentBuilder("Currently running generation tasks:").color(ChatColor.WHITE).bold(true)
|
/**
|
||||||
for (task in runningTasks) {
|
* Returns the report string for one generation task
|
||||||
val genTask = task.generationTask
|
*/
|
||||||
response.append("\n - ").color(ChatColor.WHITE).bold(false)
|
private fun getGenerationEntry(task: TaskEntry): String {
|
||||||
.append("#${task.id}").color(ChatColor.BLUE).append(" - ").color(ChatColor.WHITE)
|
val genTask = task.generationTask
|
||||||
.append(genTask.world.name).color(ChatColor.GREEN).append(" - Progress: ").color(ChatColor.WHITE)
|
val progress =
|
||||||
.append("${genTask.count} chunks").color(ChatColor.BLUE)
|
genTask.shape.progress(if (genTask.radius < 0) (genTask.world.worldBorder.size / 32).toInt() else null)
|
||||||
|
val percentage = " (%.1f".format(progress * 100) + "%)."
|
||||||
if (genTask.stopAfter > 0) {
|
|
||||||
response.append(" (${(genTask.count.toDouble()/genTask.stopAfter.toDouble())*100}%).")
|
val count = if (genTask.radius > 0) {
|
||||||
}
|
"${genTask.count} / ${ceil(genTask.shape.total()).toInt()}"
|
||||||
}
|
} else {
|
||||||
sender.spigot().sendMessage(*response.create())
|
"${genTask.count} / worldborder"
|
||||||
}
|
}
|
||||||
return true
|
return "\n" + chunkmaster.langManager.getLocalized(
|
||||||
}
|
"TASKS_ENTRY",
|
||||||
|
task.id, genTask.world.name, genTask.state.toString(), count, percentage
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,37 +1,30 @@
|
|||||||
package net.trivernis.chunkmaster.commands
|
package net.trivernis.chunkmaster.commands
|
||||||
|
|
||||||
import net.md_5.bungee.api.ChatColor
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
import net.md_5.bungee.api.chat.ComponentBuilder
|
import net.trivernis.chunkmaster.lib.Subcommand
|
||||||
import net.trivernis.chunkmaster.Chunkmaster
|
import org.bukkit.command.Command
|
||||||
import net.trivernis.chunkmaster.lib.Subcommand
|
import org.bukkit.command.CommandSender
|
||||||
import org.bukkit.command.Command
|
|
||||||
import org.bukkit.command.CommandSender
|
class CmdPause(private val chunkmaster: Chunkmaster) : Subcommand {
|
||||||
|
override val name: String = "pause"
|
||||||
class CmdPause(private val chunkmaster: Chunkmaster) : Subcommand {
|
|
||||||
override val name: String = "pause"
|
override fun onTabComplete(
|
||||||
|
sender: CommandSender,
|
||||||
override fun onTabComplete(
|
command: Command,
|
||||||
sender: CommandSender,
|
alias: String,
|
||||||
command: Command,
|
args: List<String>
|
||||||
alias: String,
|
): MutableList<String> {
|
||||||
args: List<String>
|
return emptyList<String>().toMutableList()
|
||||||
): MutableList<String> {
|
}
|
||||||
return emptyList<String>().toMutableList()
|
|
||||||
}
|
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
||||||
|
return if (!chunkmaster.generationManager.paused) {
|
||||||
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
chunkmaster.generationManager.pauseAll()
|
||||||
return if (!chunkmaster.generationManager.paused) {
|
sender.sendMessage(chunkmaster.langManager.getLocalized("PAUSE_SUCCESS"))
|
||||||
chunkmaster.generationManager.pauseAll()
|
true
|
||||||
sender.spigot().sendMessage(
|
} else {
|
||||||
*ComponentBuilder("Paused all generation tasks.")
|
sender.sendMessage(chunkmaster.langManager.getLocalized("ALREADY_PAUSED"))
|
||||||
.color(ChatColor.BLUE).create()
|
false
|
||||||
)
|
}
|
||||||
true
|
}
|
||||||
} else {
|
|
||||||
sender.spigot().sendMessage(
|
|
||||||
*ComponentBuilder("The generation process is already paused.").color(ChatColor.RED).create()
|
|
||||||
)
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,34 +1,32 @@
|
|||||||
package net.trivernis.chunkmaster.commands
|
package net.trivernis.chunkmaster.commands
|
||||||
|
|
||||||
import net.md_5.bungee.api.ChatColor
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
import net.md_5.bungee.api.chat.ComponentBuilder
|
import net.trivernis.chunkmaster.lib.Subcommand
|
||||||
import net.trivernis.chunkmaster.Chunkmaster
|
import org.bukkit.command.Command
|
||||||
import net.trivernis.chunkmaster.lib.Subcommand
|
import org.bukkit.command.CommandSender
|
||||||
import org.bukkit.command.Command
|
|
||||||
import org.bukkit.command.CommandSender
|
class CmdReload(private val chunkmaster: Chunkmaster) : Subcommand {
|
||||||
|
override val name = "reload"
|
||||||
class CmdReload(private val chunkmaster: Chunkmaster): Subcommand {
|
|
||||||
override val name = "reload"
|
override fun onTabComplete(
|
||||||
|
sender: CommandSender,
|
||||||
override fun onTabComplete(
|
command: Command,
|
||||||
sender: CommandSender,
|
alias: String,
|
||||||
command: Command,
|
args: List<String>
|
||||||
alias: String,
|
): MutableList<String> {
|
||||||
args: List<String>
|
return emptyList<String>().toMutableList()
|
||||||
): MutableList<String> {
|
}
|
||||||
return emptyList<String>().toMutableList()
|
|
||||||
}
|
/**
|
||||||
|
* Reload command to reload the config and restart the tasks.
|
||||||
/**
|
*/
|
||||||
* Reload command to reload the config and restart the tasks.
|
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
||||||
*/
|
sender.sendMessage(chunkmaster.langManager.getLocalized("CONFIG_RELOADING"))
|
||||||
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
chunkmaster.generationManager.stopAll()
|
||||||
sender.spigot().sendMessage(*ComponentBuilder("Reloading config and restarting tasks...")
|
chunkmaster.reloadConfig()
|
||||||
.color(ChatColor.YELLOW).create())
|
chunkmaster.generationManager.startAll()
|
||||||
chunkmaster.generationManager.stopAll()
|
chunkmaster.langManager.loadProperties()
|
||||||
chunkmaster.reloadConfig()
|
sender.sendMessage(chunkmaster.langManager.getLocalized("CONFIG_RELOADED"))
|
||||||
chunkmaster.generationManager.startAll()
|
return true
|
||||||
sender.spigot().sendMessage(*ComponentBuilder("Config reload complete!").color(ChatColor.GREEN).create())
|
}
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,34 +1,30 @@
|
|||||||
package net.trivernis.chunkmaster.commands
|
package net.trivernis.chunkmaster.commands
|
||||||
|
|
||||||
import net.md_5.bungee.api.ChatColor
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
import net.md_5.bungee.api.chat.ComponentBuilder
|
import net.trivernis.chunkmaster.lib.Subcommand
|
||||||
import net.trivernis.chunkmaster.Chunkmaster
|
import org.bukkit.command.Command
|
||||||
import net.trivernis.chunkmaster.lib.Subcommand
|
import org.bukkit.command.CommandSender
|
||||||
import org.bukkit.command.Command
|
|
||||||
import org.bukkit.command.CommandSender
|
class CmdResume(private val chunkmaster: Chunkmaster) : Subcommand {
|
||||||
|
override val name = "resume"
|
||||||
class CmdResume(private val chunkmaster: Chunkmaster): Subcommand {
|
|
||||||
override val name = "resume"
|
override fun onTabComplete(
|
||||||
|
sender: CommandSender,
|
||||||
override fun onTabComplete(
|
command: Command,
|
||||||
sender: CommandSender,
|
alias: String,
|
||||||
command: Command,
|
args: List<String>
|
||||||
alias: String,
|
): MutableList<String> {
|
||||||
args: List<String>
|
return emptyList<String>().toMutableList()
|
||||||
): MutableList<String> {
|
}
|
||||||
return emptyList<String>().toMutableList()
|
|
||||||
}
|
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
||||||
|
return if (chunkmaster.generationManager.paused) {
|
||||||
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
chunkmaster.generationManager.resumeAll()
|
||||||
return if (chunkmaster.generationManager.paused) {
|
sender.sendMessage(chunkmaster.langManager.getLocalized("RESUME_SUCCESS"))
|
||||||
chunkmaster.generationManager.resumeAll()
|
true
|
||||||
sender.spigot().sendMessage(
|
} else {
|
||||||
*ComponentBuilder("Resumed all generation tasks.").color(ChatColor.BLUE).create())
|
sender.sendMessage(chunkmaster.langManager.getLocalized("NOT_PAUSED"))
|
||||||
true
|
false
|
||||||
} else {
|
}
|
||||||
sender.spigot().sendMessage(
|
}
|
||||||
*ComponentBuilder("There are no paused generation tasks.").color(ChatColor.RED).create())
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
package net.trivernis.chunkmaster.commands
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
|
import net.trivernis.chunkmaster.lib.Subcommand
|
||||||
|
import org.bukkit.command.Command
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
class CmdSetCenter(private val chunkmaster: Chunkmaster) : Subcommand {
|
||||||
|
override val name = "setCenter"
|
||||||
|
|
||||||
|
override fun onTabComplete(
|
||||||
|
sender: CommandSender,
|
||||||
|
command: Command,
|
||||||
|
alias: String,
|
||||||
|
args: List<String>
|
||||||
|
): MutableList<String> {
|
||||||
|
if (args.size == 1) {
|
||||||
|
if (args[0].toIntOrNull() == null) {
|
||||||
|
return sender.server.worlds.filter { it.name.indexOf(args[0]) == 0 }
|
||||||
|
.map { it.name }.toMutableList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return emptyList<String>().toMutableList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
||||||
|
val world: String
|
||||||
|
val centerX: Int
|
||||||
|
val centerZ: Int
|
||||||
|
|
||||||
|
if (sender is Player) {
|
||||||
|
when {
|
||||||
|
args.isEmpty() -> {
|
||||||
|
world = sender.world.name
|
||||||
|
centerX = sender.location.chunk.x
|
||||||
|
centerZ = sender.location.chunk.z
|
||||||
|
}
|
||||||
|
args.size == 1 -> {
|
||||||
|
world = args[0]
|
||||||
|
centerX = sender.location.chunk.x
|
||||||
|
centerZ = sender.location.chunk.z
|
||||||
|
}
|
||||||
|
args.size == 2 -> {
|
||||||
|
world = sender.world.name
|
||||||
|
if (args[0].toIntOrNull() == null || args[1].toIntOrNull() == null) {
|
||||||
|
sender.sendMessage(chunkmaster.langManager.getLocalized("COORD_INVALID", args[0], args[1]))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
centerX = args[0].toInt()
|
||||||
|
centerZ = args[1].toInt()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
if (!validateThreeArgs(sender, args)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
world = args[0]
|
||||||
|
centerX = args[1].toInt()
|
||||||
|
centerZ = args[2].toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (args.size < 3) {
|
||||||
|
sender.sendMessage(chunkmaster.langManager.getLocalized("TOO_FEW_ARGUMENTS"))
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
if (!validateThreeArgs(sender, args)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
world = args[0]
|
||||||
|
centerX = args[1].toInt()
|
||||||
|
centerZ = args[2].toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chunkmaster.generationManager.worldProperties.setWorldCenter(world, Pair(centerX, centerZ))
|
||||||
|
sender.sendMessage(chunkmaster.langManager.getLocalized("CENTER_UPDATED", world, centerX, centerZ))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the command values with three arguments
|
||||||
|
*/
|
||||||
|
private fun validateThreeArgs(sender: CommandSender, args: List<String>): Boolean {
|
||||||
|
return if (sender.server.worlds.none { it.name == args[0] }) {
|
||||||
|
sender.sendMessage(chunkmaster.langManager.getLocalized("WORLD_NOT_FOUND", args[0]))
|
||||||
|
false
|
||||||
|
} else if (args[1].toIntOrNull() == null || args[2].toIntOrNull() == null) {
|
||||||
|
sender.sendMessage(chunkmaster.langManager.getLocalized("COORD_INVALID", args[1], args[2]))
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
package net.trivernis.chunkmaster.commands
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
|
import net.trivernis.chunkmaster.lib.Subcommand
|
||||||
|
import org.bukkit.World
|
||||||
|
import org.bukkit.command.Command
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
|
||||||
|
class CmdStats(private val chunkmaster: Chunkmaster) : Subcommand {
|
||||||
|
override val name = "stats"
|
||||||
|
|
||||||
|
override fun onTabComplete(
|
||||||
|
sender: CommandSender,
|
||||||
|
command: Command,
|
||||||
|
alias: String,
|
||||||
|
args: List<String>
|
||||||
|
): MutableList<String> {
|
||||||
|
return sender.server.worlds.map { it.name }.toMutableList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
||||||
|
if (args.isNotEmpty()) {
|
||||||
|
val world = sender.server.getWorld(args[0])
|
||||||
|
if (world == null) {
|
||||||
|
sender.sendMessage(
|
||||||
|
chunkmaster.langManager.getLocalized("STATS_HEADER") + "\n" +
|
||||||
|
chunkmaster.langManager.getLocalized("WORLD_NOT_FOUND", args[0])
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
sender.sendMessage(getWorldStatsMessage(sender, world))
|
||||||
|
} else {
|
||||||
|
sender.sendMessage(getServerStatsMessage(sender))
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getWorldStatsMessage(sender: CommandSender, world: World): String {
|
||||||
|
return """
|
||||||
|
${chunkmaster.langManager.getLocalized("STATS_WORLD_NAME", world.name)}
|
||||||
|
${chunkmaster.langManager.getLocalized("STATS_ENTITY_COUNT", world.entities.size)}
|
||||||
|
${chunkmaster.langManager.getLocalized("STATS_LOADED_CHUNKS", world.loadedChunks.size)}
|
||||||
|
${
|
||||||
|
chunkmaster.langManager.getLocalized(
|
||||||
|
"STATS_GENERATING",
|
||||||
|
chunkmaster.generationManager.tasks.find { it.generationTask.world == world } != null)
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getServerStatsMessage(sender: CommandSender): String {
|
||||||
|
val runtime = Runtime.getRuntime()
|
||||||
|
val memUsed = runtime.maxMemory() - runtime.freeMemory()
|
||||||
|
var message = "\n" + """
|
||||||
|
${chunkmaster.langManager.getLocalized("STATS_HEADER")}
|
||||||
|
|
||||||
|
${chunkmaster.langManager.getLocalized("STATS_SERVER")}
|
||||||
|
${chunkmaster.langManager.getLocalized("STATS_SERVER_VERSION", sender.server.version)}
|
||||||
|
${chunkmaster.langManager.getLocalized("STATS_PLUGIN_VERSION", chunkmaster.description.version)}
|
||||||
|
${
|
||||||
|
chunkmaster.langManager.getLocalized(
|
||||||
|
"STATS_MEMORY",
|
||||||
|
memUsed / 1000000,
|
||||||
|
runtime.maxMemory() / 1000000,
|
||||||
|
(memUsed.toFloat() / runtime.maxMemory().toFloat()) * 100
|
||||||
|
)
|
||||||
|
}
|
||||||
|
${chunkmaster.langManager.getLocalized("STATS_CORES", runtime.availableProcessors())}
|
||||||
|
|
||||||
|
${
|
||||||
|
chunkmaster.langManager.getLocalized(
|
||||||
|
"STATS_PLUGIN_LOADED_CHUNKS",
|
||||||
|
chunkmaster.generationManager.loadedChunkCount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
for (world in sender.server.worlds) {
|
||||||
|
message += "\n\n" + getWorldStatsMessage(sender, world)
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
}
|
@ -1,52 +1,49 @@
|
|||||||
package net.trivernis.chunkmaster.commands
|
package net.trivernis.chunkmaster.commands
|
||||||
|
|
||||||
import io.papermc.lib.PaperLib
|
import io.papermc.lib.PaperLib
|
||||||
import net.md_5.bungee.api.ChatColor
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
import net.md_5.bungee.api.chat.ComponentBuilder
|
import net.trivernis.chunkmaster.lib.Subcommand
|
||||||
import net.trivernis.chunkmaster.lib.Subcommand
|
import org.bukkit.Material
|
||||||
import org.bukkit.Material
|
import org.bukkit.command.Command
|
||||||
import org.bukkit.command.Command
|
import org.bukkit.command.CommandSender
|
||||||
import org.bukkit.command.CommandSender
|
import org.bukkit.entity.Player
|
||||||
import org.bukkit.entity.Player
|
|
||||||
|
class CmdTpChunk(private val chunkmaster: Chunkmaster) : Subcommand {
|
||||||
class CmdTpChunk: Subcommand {
|
override val name = "tpchunk"
|
||||||
override val name = "tpchunk"
|
|
||||||
|
override fun onTabComplete(
|
||||||
override fun onTabComplete(
|
sender: CommandSender,
|
||||||
sender: CommandSender,
|
command: Command,
|
||||||
command: Command,
|
alias: String,
|
||||||
alias: String,
|
args: List<String>
|
||||||
args: List<String>
|
): MutableList<String> {
|
||||||
): MutableList<String> {
|
return emptyList<String>().toMutableList()
|
||||||
return emptyList<String>().toMutableList()
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Teleports the player to a save location in the chunk
|
||||||
* Teleports the player to a save location in the chunk
|
*/
|
||||||
*/
|
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
||||||
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
if (sender is Player) {
|
||||||
if (sender is Player) {
|
if (args.size == 2 && args[0].toIntOrNull() != null && args[1].toIntOrNull() != null) {
|
||||||
if (args.size == 2 && args[0].toIntOrNull() != null && args[1].toIntOrNull() != null) {
|
val location = sender.world.getChunkAt(args[0].toInt(), args[1].toInt()).getBlock(8, 60, 8).location
|
||||||
val location = sender.world.getChunkAt(args[0].toInt(), args[1].toInt()).getBlock(8, 60, 8).location
|
|
||||||
|
while (location.block.blockData.material != Material.AIR) {
|
||||||
while (location.block.blockData.material != Material.AIR) {
|
location.y++
|
||||||
location.y++
|
}
|
||||||
}
|
if (PaperLib.isPaper()) {
|
||||||
if (PaperLib.isPaper()) {
|
PaperLib.teleportAsync(sender, location)
|
||||||
PaperLib.teleportAsync(sender, location)
|
} else {
|
||||||
} else {
|
sender.teleport(location)
|
||||||
sender.teleport(location)
|
}
|
||||||
}
|
sender.sendMessage(chunkmaster.langManager.getLocalized("TELEPORTED", args[0], args[1]))
|
||||||
sender.spigot().sendMessage(*ComponentBuilder("You have been teleportet to chunk")
|
return true
|
||||||
.color(ChatColor.YELLOW).append("${args[0]}, ${args[1]}").color(ChatColor.BLUE).create())
|
} else {
|
||||||
return true
|
return false
|
||||||
} else {
|
}
|
||||||
return false
|
} else {
|
||||||
}
|
sender.sendMessage(chunkmaster.langManager.getLocalized("TP_ONLY_PLAYER"))
|
||||||
} else {
|
return false
|
||||||
sender.spigot().sendMessage(*ComponentBuilder("This command can only be executed by a player!")
|
}
|
||||||
.color(ChatColor.RED).create())
|
}
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,90 +1,103 @@
|
|||||||
package net.trivernis.chunkmaster.commands
|
package net.trivernis.chunkmaster.commands
|
||||||
|
|
||||||
import net.md_5.bungee.api.ChatColor
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
import net.md_5.bungee.api.chat.ComponentBuilder
|
import net.trivernis.chunkmaster.lib.ArgParser
|
||||||
import net.trivernis.chunkmaster.Chunkmaster
|
import net.trivernis.chunkmaster.lib.Subcommand
|
||||||
import net.trivernis.chunkmaster.lib.Subcommand
|
import org.bukkit.Server
|
||||||
import org.bukkit.Server
|
import org.bukkit.command.Command
|
||||||
import org.bukkit.command.Command
|
import org.bukkit.command.CommandExecutor
|
||||||
import org.bukkit.command.CommandExecutor
|
import org.bukkit.command.CommandSender
|
||||||
import org.bukkit.command.CommandSender
|
import org.bukkit.command.TabCompleter
|
||||||
import org.bukkit.command.TabCompleter
|
|
||||||
|
class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val server: Server) : CommandExecutor,
|
||||||
class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val server: Server) : CommandExecutor,
|
TabCompleter {
|
||||||
TabCompleter {
|
private val commands = HashMap<String, Subcommand>()
|
||||||
private val commands = HashMap<String, Subcommand>()
|
private val argParser = ArgParser()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
registerCommands()
|
registerCommands()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tab complete for commands
|
* Tab complete for commands
|
||||||
*/
|
*/
|
||||||
override fun onTabComplete(sender: CommandSender, command: Command, alias: String, args: Array<out String>):
|
override fun onTabComplete(sender: CommandSender, command: Command, alias: String, args: Array<out String>):
|
||||||
MutableList<String> {
|
MutableList<String> {
|
||||||
if (args.size == 1) {
|
if (args.size == 1) {
|
||||||
return commands.keys.filter { it.indexOf(args[0]) == 0 }.toMutableList()
|
return commands.keys.filter { it.indexOf(args[0]) == 0 }.toMutableList()
|
||||||
} else if (args.isNotEmpty()) {
|
} else if (args.isNotEmpty()) {
|
||||||
|
|
||||||
if (commands.containsKey(args[0])) {
|
if (commands.containsKey(args[0])) {
|
||||||
val commandEntry = commands[args[0]]
|
val commandEntry = commands[args[0]]
|
||||||
return commandEntry!!.onTabComplete(sender, command, alias, args.slice(1 until args.size))
|
return commandEntry!!.onTabComplete(sender, command, alias, args.slice(1 until args.size))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList<String>().toMutableList()
|
return emptyList<String>().toMutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /chunkmaster command to handle all commands
|
* /chunkmaster command to handle all commands
|
||||||
*/
|
*/
|
||||||
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
|
override fun onCommand(
|
||||||
if (args.isNotEmpty()) {
|
sender: CommandSender,
|
||||||
if (sender.hasPermission("chunkmaster.${args[0]}")) {
|
command: Command,
|
||||||
return if (commands.containsKey(args[0])) {
|
label: String,
|
||||||
commands[args[0]]!!.execute(sender, args.slice(1 until args.size))
|
bukkitArgs: Array<out String>
|
||||||
} else {
|
): Boolean {
|
||||||
sender.spigot().sendMessage(
|
val args = argParser.parseArguments(bukkitArgs.joinToString(" "))
|
||||||
*ComponentBuilder("Subcommand ").color(ChatColor.RED)
|
|
||||||
.append(args[0]).color(ChatColor.GREEN).append(" not found").color(ChatColor.RED).create()
|
if (args.isNotEmpty()) {
|
||||||
)
|
if (sender.hasPermission("chunkmaster.${args[0].toLowerCase()}")) {
|
||||||
false
|
return if (commands.containsKey(args[0])) {
|
||||||
}
|
commands[args[0]]!!.execute(sender, args.slice(1 until args.size))
|
||||||
} else {
|
} else {
|
||||||
sender.spigot().sendMessage(
|
sender.sendMessage(chunkmaster.langManager.getLocalized("SUBCOMMAND_NOT_FOUND", args[0]))
|
||||||
*ComponentBuilder("You do not have permission!")
|
false
|
||||||
.color(ChatColor.RED).create()
|
}
|
||||||
)
|
} else {
|
||||||
}
|
sender.sendMessage(chunkmaster.langManager.getLocalized("NO_PERMISSION"))
|
||||||
return true
|
}
|
||||||
} else {
|
return true
|
||||||
return false
|
} else {
|
||||||
}
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Registers all subcommands.
|
/**
|
||||||
*/
|
* Registers all subcommands.
|
||||||
private fun registerCommands() {
|
*/
|
||||||
val cmdGenerate = CmdGenerate(chunkmaster)
|
private fun registerCommands() {
|
||||||
commands[cmdGenerate.name] = cmdGenerate
|
val cmdGenerate = CmdGenerate(chunkmaster)
|
||||||
|
commands[cmdGenerate.name] = cmdGenerate
|
||||||
val cmdPause = CmdPause(chunkmaster)
|
|
||||||
commands[cmdPause.name] = cmdPause
|
val cmdPause = CmdPause(chunkmaster)
|
||||||
|
commands[cmdPause.name] = cmdPause
|
||||||
val cmdResume = CmdResume(chunkmaster)
|
|
||||||
commands[cmdResume.name] = cmdResume
|
val cmdResume = CmdResume(chunkmaster)
|
||||||
|
commands[cmdResume.name] = cmdResume
|
||||||
val cmdCancel = CmdCancel(chunkmaster)
|
|
||||||
commands[cmdCancel.name] = cmdCancel
|
val cmdCancel = CmdCancel(chunkmaster)
|
||||||
|
commands[cmdCancel.name] = cmdCancel
|
||||||
val cmdList = CmdList(chunkmaster)
|
|
||||||
commands[cmdList.name] = cmdList
|
val cmdList = CmdList(chunkmaster)
|
||||||
|
commands[cmdList.name] = cmdList
|
||||||
val cmdReload = CmdReload(chunkmaster)
|
|
||||||
commands[cmdReload.name] = cmdReload
|
val cmdReload = CmdReload(chunkmaster)
|
||||||
|
commands[cmdReload.name] = cmdReload
|
||||||
val cmdTpChunk = CmdTpChunk()
|
|
||||||
commands[cmdTpChunk.name] = cmdTpChunk
|
val cmdTpChunk = CmdTpChunk(chunkmaster)
|
||||||
}
|
commands[cmdTpChunk.name] = cmdTpChunk
|
||||||
|
|
||||||
|
val cmdSetCenter = CmdSetCenter(chunkmaster)
|
||||||
|
commands[cmdSetCenter.name] = cmdSetCenter
|
||||||
|
|
||||||
|
val cmdGetCenter = CmdGetCenter(chunkmaster)
|
||||||
|
commands[cmdGetCenter.name] = cmdGetCenter
|
||||||
|
|
||||||
|
val cmdStats = CmdStats(chunkmaster)
|
||||||
|
commands[cmdStats.name] = cmdStats
|
||||||
|
|
||||||
|
val cmdCompleted = CmdCompleted(chunkmaster)
|
||||||
|
commands[cmdCompleted.name] = cmdCompleted
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Better argument parser for command arguments
|
||||||
|
*/
|
||||||
|
class ArgParser {
|
||||||
|
private var input = ""
|
||||||
|
private var position = 0
|
||||||
|
private var currentChar = ' '
|
||||||
|
private var escaped = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses arguments from a string and respects quotes
|
||||||
|
*/
|
||||||
|
fun parseArguments(arguments: String): List<String> {
|
||||||
|
if (arguments.isEmpty()) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
input = arguments
|
||||||
|
position = 0
|
||||||
|
currentChar = input[position]
|
||||||
|
escaped = false
|
||||||
|
val args = ArrayList<String>()
|
||||||
|
var arg = ""
|
||||||
|
|
||||||
|
while (!endReached()) {
|
||||||
|
nextCharacter()
|
||||||
|
|
||||||
|
if (currentChar == '\\' && !escaped) {
|
||||||
|
escaped = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentChar.isWhitespace() && !escaped) {
|
||||||
|
if (arg.isNotBlank()) {
|
||||||
|
args.add(arg)
|
||||||
|
}
|
||||||
|
arg = ""
|
||||||
|
} else if (currentChar == '"' && !escaped) {
|
||||||
|
if (arg.isNotBlank()) {
|
||||||
|
args.add(arg)
|
||||||
|
}
|
||||||
|
arg = parseString()
|
||||||
|
if (arg.isNotBlank()) {
|
||||||
|
args.add(arg)
|
||||||
|
}
|
||||||
|
arg = ""
|
||||||
|
} else {
|
||||||
|
arg += currentChar
|
||||||
|
}
|
||||||
|
escaped = false
|
||||||
|
}
|
||||||
|
if (arg.isNotBlank()) {
|
||||||
|
args.add(arg)
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an enquoted string
|
||||||
|
*/
|
||||||
|
private fun parseString(): String {
|
||||||
|
var output = ""
|
||||||
|
|
||||||
|
while (!endReached()) {
|
||||||
|
nextCharacter()
|
||||||
|
|
||||||
|
if (currentChar == '\\') {
|
||||||
|
escaped = !escaped
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentChar == '"' && !escaped) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
output += currentChar
|
||||||
|
escaped = false
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun nextCharacter() {
|
||||||
|
if (!endReached()) {
|
||||||
|
currentChar = input[position++]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun endReached(): Boolean {
|
||||||
|
return position >= input.length
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
|
import java.io.*
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class LanguageManager(private val plugin: Chunkmaster) {
|
||||||
|
private val langProps = Properties()
|
||||||
|
private val languageFolder = "${plugin.dataFolder.absolutePath}/i18n"
|
||||||
|
private var langFileLoaded = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the default properties file and then the language specific ones.
|
||||||
|
* If no lang-specific file is found in the plugins directory under i18n an attempt is made to
|
||||||
|
* load the file from inside the jar in i18n.
|
||||||
|
*/
|
||||||
|
fun loadProperties() {
|
||||||
|
val language = plugin.config.getString("language")
|
||||||
|
val langFile = "$languageFolder/$language.i18n.properties"
|
||||||
|
val file = File(langFile)
|
||||||
|
val loader = Thread.currentThread().contextClassLoader
|
||||||
|
val defaultStream = this.javaClass.getResourceAsStream("/i18n/DEFAULT.i18n.properties")
|
||||||
|
|
||||||
|
if (defaultStream != null) {
|
||||||
|
langProps.load(getReaderForProperties(defaultStream))
|
||||||
|
defaultStream.close()
|
||||||
|
} else {
|
||||||
|
plugin.logger.severe("Couldn't load default language properties.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.exists()) {
|
||||||
|
try {
|
||||||
|
val inputStream = loader.getResourceAsStream(langFile)
|
||||||
|
if (inputStream != null) {
|
||||||
|
langProps.load(getReaderForProperties(inputStream))
|
||||||
|
langFileLoaded = true
|
||||||
|
inputStream.close()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
plugin.logger.warning("Language file $langFile could not be loaded!")
|
||||||
|
plugin.logger.fine(e.toString())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val inputStream = this.javaClass.getResourceAsStream("/i18n/$language.i18n.properties")
|
||||||
|
if (inputStream != null) {
|
||||||
|
langProps.load(getReaderForProperties(inputStream))
|
||||||
|
langFileLoaded = true
|
||||||
|
inputStream.close()
|
||||||
|
} else {
|
||||||
|
plugin.logger.warning("Language File $langFile could not be found!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a localized message with replacements
|
||||||
|
*/
|
||||||
|
fun getLocalized(key: String, vararg replacements: Any): String {
|
||||||
|
try {
|
||||||
|
val localizedString = langProps.getProperty(key)
|
||||||
|
return String.format(localizedString, *replacements)
|
||||||
|
} catch (e: NullPointerException) {
|
||||||
|
plugin.logger.severe("Failed to get localized entry for $key")
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a properties file as utf-8 and returns a string reader for the contents
|
||||||
|
*/
|
||||||
|
private fun getReaderForProperties(stream: InputStream): Reader {
|
||||||
|
return BufferedReader(InputStreamReader(stream, "UTF-8"))
|
||||||
|
}
|
||||||
|
}
|
@ -1,64 +0,0 @@
|
|||||||
package net.trivernis.chunkmaster.lib
|
|
||||||
|
|
||||||
import kotlin.math.abs
|
|
||||||
|
|
||||||
class Spiral(private val center: Pair<Int, Int>, start: Pair<Int, Int>) {
|
|
||||||
private var currentPos = start
|
|
||||||
private var direction = 0
|
|
||||||
var count = 0
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the next value in the spiral
|
|
||||||
*/
|
|
||||||
fun next(): Pair<Int, Int> {
|
|
||||||
if (count == 0 && currentPos != center) {
|
|
||||||
// simulate the spiral to get the correct direction
|
|
||||||
// TODO: Improve performance of this workaround (replace it with acutal stuff)
|
|
||||||
val simSpiral = Spiral(center, center)
|
|
||||||
while (simSpiral.next() != currentPos);
|
|
||||||
direction = simSpiral.direction
|
|
||||||
count = simSpiral.count
|
|
||||||
}
|
|
||||||
if (count == 1) { // because of the center behaviour
|
|
||||||
count ++
|
|
||||||
return currentPos
|
|
||||||
}
|
|
||||||
if (currentPos == center) { // the center has to be handled exclusively
|
|
||||||
currentPos = Pair(center.first, center.second + 1)
|
|
||||||
count ++
|
|
||||||
return center
|
|
||||||
} else {
|
|
||||||
val distances = getDistances(center, currentPos)
|
|
||||||
if (abs(distances.first) == abs(distances.second)) {
|
|
||||||
direction = (direction + 1)%5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
when(direction) {
|
|
||||||
0 -> {
|
|
||||||
currentPos = Pair(currentPos.first + 1, currentPos.second)
|
|
||||||
}
|
|
||||||
1 -> {
|
|
||||||
currentPos = Pair(currentPos.first, currentPos.second - 1)
|
|
||||||
}
|
|
||||||
2 -> {
|
|
||||||
currentPos = Pair(currentPos.first - 1, currentPos.second)
|
|
||||||
}
|
|
||||||
3 -> {
|
|
||||||
currentPos = Pair(currentPos.first, currentPos.second + 1)
|
|
||||||
}
|
|
||||||
4 -> {
|
|
||||||
currentPos = Pair(currentPos.first, currentPos.second + 1)
|
|
||||||
direction = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
count ++
|
|
||||||
return currentPos
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the distances between 2 coordinates
|
|
||||||
*/
|
|
||||||
private fun getDistances(pos1: Pair<Int, Int>, pos2: Pair<Int, Int>): Pair<Int, Int> {
|
|
||||||
return Pair(pos2.first - pos1.first, pos2.second - pos1.second)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
package net.trivernis.chunkmaster.lib
|
|
||||||
|
|
||||||
import net.trivernis.chunkmaster.Chunkmaster
|
|
||||||
import java.lang.Exception
|
|
||||||
import java.sql.Connection
|
|
||||||
|
|
||||||
class SqlUpdateManager(private val connnection: Connection, private val chunkmaster: Chunkmaster) {
|
|
||||||
private val tables = listOf(
|
|
||||||
Pair(
|
|
||||||
"generation_tasks",
|
|
||||||
listOf(
|
|
||||||
Pair("id", "integer PRIMARY KEY AUTOINCREMENT"),
|
|
||||||
Pair("center_x", "integer NOT NULL DEFAULT 0"),
|
|
||||||
Pair("center_z", "integer NOT NULL DEFAULT 0"),
|
|
||||||
Pair("last_x", "integer NOT NULL DEFAULT 0"),
|
|
||||||
Pair("last_z", "integer NOT NULL DEFAULT 0"),
|
|
||||||
Pair("world", "text UNIQUE NOT NULL DEFAULT 'world'"),
|
|
||||||
Pair("stop_after", "integer DEFAULT -1")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private val needUpdate = HashSet<Pair<String, Pair<String, String>>>()
|
|
||||||
private val needCreation = HashSet<String>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks which tables need an update or creation.
|
|
||||||
*/
|
|
||||||
fun checkUpdate() {
|
|
||||||
val meta = connnection.metaData
|
|
||||||
|
|
||||||
for (table in tables) {
|
|
||||||
val resTables = meta.getTables(null, null, table.first, null)
|
|
||||||
|
|
||||||
if (resTables.next()) { // table exists
|
|
||||||
for (column in table.second) {
|
|
||||||
val resColumn = meta.getColumns(null, null, table.first, column.first)
|
|
||||||
if (!resColumn.next()) {
|
|
||||||
needUpdate.add(Pair(table.first, column))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
needCreation.add(table.first)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates or updates tables that needed an update.
|
|
||||||
*/
|
|
||||||
fun performUpdate() {
|
|
||||||
for (table in needCreation) {
|
|
||||||
try {
|
|
||||||
var tableDef = "CREATE TABLE IF NOT EXISTS $table ("
|
|
||||||
|
|
||||||
for (column in tables.find{it.first == table}!!.second) {
|
|
||||||
tableDef += "${column.first} ${column.second},"
|
|
||||||
}
|
|
||||||
tableDef = tableDef.substringBeforeLast(",") + ");"
|
|
||||||
chunkmaster.logger.info("Creating table $table with definition $tableDef")
|
|
||||||
val stmt = connnection.prepareStatement(tableDef)
|
|
||||||
stmt.execute()
|
|
||||||
stmt.close()
|
|
||||||
} catch (err: Exception) {
|
|
||||||
chunkmaster.logger.severe("Error creating table $table.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (table in needUpdate) {
|
|
||||||
val updateSql = "ALTER TABLE ${table.first} ADD COLUMN ${table.second.first} ${table.second.second}"
|
|
||||||
try {
|
|
||||||
val stmt = connnection.prepareStatement(updateSql)
|
|
||||||
stmt.execute()
|
|
||||||
stmt.close()
|
|
||||||
chunkmaster.logger.info("Updated table ${table.first} with sql $updateSql")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
chunkmaster.logger.severe("Failed to update table ${table.first} with sql $updateSql")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,10 @@
|
|||||||
package net.trivernis.chunkmaster.lib
|
package net.trivernis.chunkmaster.lib
|
||||||
|
|
||||||
import org.bukkit.command.Command
|
import org.bukkit.command.Command
|
||||||
import org.bukkit.command.CommandSender
|
import org.bukkit.command.CommandSender
|
||||||
|
|
||||||
interface Subcommand {
|
interface Subcommand {
|
||||||
val name: String
|
val name: String
|
||||||
fun execute(sender: CommandSender, args: List<String>): Boolean
|
fun execute(sender: CommandSender, args: List<String>): Boolean
|
||||||
fun onTabComplete(sender: CommandSender, command: Command, alias: String, args: List<String>): MutableList<String>
|
fun onTabComplete(sender: CommandSender, command: Command, alias: String, args: List<String>): MutableList<String>
|
||||||
}
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.database
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.lib.generation.ChunkCoordinates
|
||||||
|
|
||||||
|
data class CompletedGenerationTask(
|
||||||
|
val id: Int,
|
||||||
|
val world: String,
|
||||||
|
val radius: Int,
|
||||||
|
val center: ChunkCoordinates,
|
||||||
|
val shape: String
|
||||||
|
)
|
@ -0,0 +1,80 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.database
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.lib.generation.ChunkCoordinates
|
||||||
|
import java.sql.ResultSet
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
|
class CompletedGenerationTasks(private val sqliteManager: SqliteManager) {
|
||||||
|
/**
|
||||||
|
* Returns the list of all completed tasks
|
||||||
|
*/
|
||||||
|
fun getCompletedTasks(): CompletableFuture<List<CompletedGenerationTask>> {
|
||||||
|
val completableFuture = CompletableFuture<List<CompletedGenerationTask>>()
|
||||||
|
|
||||||
|
sqliteManager.executeStatement("SELECT * FROM completed_generation_tasks", HashMap()) { res ->
|
||||||
|
val tasks = ArrayList<CompletedGenerationTask>()
|
||||||
|
|
||||||
|
while (res!!.next()) {
|
||||||
|
tasks.add(mapSqlResponseToWrapperObject(res))
|
||||||
|
}
|
||||||
|
completableFuture.complete(tasks)
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of completed tasks for a world
|
||||||
|
*/
|
||||||
|
fun getCompletedTasksForWorld(world: String): CompletableFuture<List<CompletedGenerationTask>> {
|
||||||
|
val completableFuture = CompletableFuture<List<CompletedGenerationTask>>()
|
||||||
|
|
||||||
|
sqliteManager.executeStatement(
|
||||||
|
"SELECT * FROM completed_generation_tasks WHERE world = ?",
|
||||||
|
hashMapOf(1 to world)
|
||||||
|
) { res ->
|
||||||
|
val tasks = ArrayList<CompletedGenerationTask>()
|
||||||
|
|
||||||
|
while (res!!.next()) {
|
||||||
|
tasks.add(mapSqlResponseToWrapperObject(res))
|
||||||
|
}
|
||||||
|
completableFuture.complete(tasks)
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mapSqlResponseToWrapperObject(res: ResultSet): CompletedGenerationTask {
|
||||||
|
val id = res.getInt("id")
|
||||||
|
val world = res.getString("world")
|
||||||
|
val center = ChunkCoordinates(res.getInt("center_x"), res.getInt("center_z"))
|
||||||
|
val radius = res.getInt("completed_radius")
|
||||||
|
val shape = res.getString("shape")
|
||||||
|
return CompletedGenerationTask(id, world, radius, center, shape)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a completed task
|
||||||
|
*/
|
||||||
|
fun addCompletedTask(
|
||||||
|
id: Int,
|
||||||
|
world: String,
|
||||||
|
radius: Int,
|
||||||
|
center: ChunkCoordinates,
|
||||||
|
shape: String
|
||||||
|
): CompletableFuture<Void> {
|
||||||
|
val completableFuture = CompletableFuture<Void>()
|
||||||
|
sqliteManager.executeStatement(
|
||||||
|
"INSERT INTO completed_generation_tasks (id, world, completed_radius, center_x, center_z, shape) VALUES (?, ?, ?, ?, ?, ?)",
|
||||||
|
hashMapOf(
|
||||||
|
1 to id,
|
||||||
|
2 to world,
|
||||||
|
3 to radius,
|
||||||
|
4 to center.x,
|
||||||
|
5 to center.z,
|
||||||
|
6 to shape,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
completableFuture.complete(null)
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.database
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.lib.generation.ChunkCoordinates
|
||||||
|
import net.trivernis.chunkmaster.lib.generation.TaskState
|
||||||
|
|
||||||
|
data class GenerationTaskData(
|
||||||
|
val id: Int,
|
||||||
|
val world: String,
|
||||||
|
val radius: Int,
|
||||||
|
val shape: String,
|
||||||
|
val state: TaskState,
|
||||||
|
val center: ChunkCoordinates,
|
||||||
|
val last: ChunkCoordinates
|
||||||
|
)
|
@ -0,0 +1,104 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.database
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.lib.generation.ChunkCoordinates
|
||||||
|
import net.trivernis.chunkmaster.lib.generation.TaskState
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
|
class GenerationTasks(private val sqliteManager: SqliteManager) {
|
||||||
|
/**
|
||||||
|
* Returns all stored generation tasks
|
||||||
|
*/
|
||||||
|
fun getGenerationTasks(): CompletableFuture<List<GenerationTaskData>> {
|
||||||
|
val completableFuture = CompletableFuture<List<GenerationTaskData>>()
|
||||||
|
|
||||||
|
sqliteManager.executeStatement("SELECT * FROM generation_tasks", HashMap()) { res ->
|
||||||
|
val tasks = ArrayList<GenerationTaskData>()
|
||||||
|
|
||||||
|
while (res!!.next()) {
|
||||||
|
val id = res.getInt("id")
|
||||||
|
val world = res.getString("world")
|
||||||
|
val center = ChunkCoordinates(res.getInt("center_x"), res.getInt("center_z"))
|
||||||
|
val last = ChunkCoordinates(res.getInt("last_x"), res.getInt("last_z"))
|
||||||
|
val radius = res.getInt("radius")
|
||||||
|
val shape = res.getString("shape")
|
||||||
|
val state = stringToState(res.getString("state"))
|
||||||
|
val taskData = GenerationTaskData(id, world, radius, shape, state, center, last)
|
||||||
|
if (tasks.find { it.id == id } == null) {
|
||||||
|
tasks.add(taskData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
completableFuture.complete(tasks)
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a generation task to the database
|
||||||
|
*/
|
||||||
|
fun addGenerationTask(world: String, center: ChunkCoordinates, radius: Int, shape: String): CompletableFuture<Int> {
|
||||||
|
val completableFuture = CompletableFuture<Int>()
|
||||||
|
sqliteManager.executeStatement(
|
||||||
|
"""
|
||||||
|
INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, radius, shape)
|
||||||
|
values (?, ?, ?, ?, ?, ?, ?)""",
|
||||||
|
hashMapOf(
|
||||||
|
1 to center.x,
|
||||||
|
2 to center.z,
|
||||||
|
3 to center.x,
|
||||||
|
4 to center.z,
|
||||||
|
5 to world,
|
||||||
|
6 to radius,
|
||||||
|
7 to shape
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
sqliteManager.executeStatement(
|
||||||
|
"""
|
||||||
|
SELECT id FROM generation_tasks ORDER BY id DESC LIMIT 1
|
||||||
|
""".trimIndent(), HashMap()
|
||||||
|
) {
|
||||||
|
it!!.next()
|
||||||
|
completableFuture.complete(it.getInt("id"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a generationTask from the database
|
||||||
|
*/
|
||||||
|
fun deleteGenerationTask(id: Int): CompletableFuture<Void> {
|
||||||
|
val completableFuture = CompletableFuture<Void>()
|
||||||
|
sqliteManager.executeStatement("DELETE FROM generation_tasks WHERE id = ?;", hashMapOf(1 to id)) {
|
||||||
|
completableFuture.complete(null)
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateGenerationTask(id: Int, last: ChunkCoordinates, state: TaskState): CompletableFuture<Void> {
|
||||||
|
val completableFuture = CompletableFuture<Void>()
|
||||||
|
sqliteManager.executeStatement(
|
||||||
|
"""
|
||||||
|
UPDATE generation_tasks SET last_x = ?, last_z = ?, state = ?
|
||||||
|
WHERE id = ?
|
||||||
|
""".trimIndent(),
|
||||||
|
hashMapOf(1 to last.x, 2 to last.z, 3 to state.toString(), 4 to id)
|
||||||
|
) {
|
||||||
|
completableFuture.complete(null)
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a string into a task state
|
||||||
|
*/
|
||||||
|
private fun stringToState(stringState: String): TaskState {
|
||||||
|
TaskState.valueOf(stringState)
|
||||||
|
return when (stringState) {
|
||||||
|
"GENERATING" -> TaskState.GENERATING
|
||||||
|
"VALIDATING" -> TaskState.VALIDATING
|
||||||
|
"PAUSING" -> TaskState.PAUSING
|
||||||
|
"CORRECTING" -> TaskState.CORRECTING
|
||||||
|
else -> TaskState.GENERATING
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.database
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.lib.generation.ChunkCoordinates
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
class PendingChunks(private val sqliteManager: SqliteManager) {
|
||||||
|
|
||||||
|
private val insertionCount = 300
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of pending chunks for a taskId
|
||||||
|
*/
|
||||||
|
fun getPendingChunks(taskId: Int): CompletableFuture<List<ChunkCoordinates>> {
|
||||||
|
val completableFuture = CompletableFuture<List<ChunkCoordinates>>()
|
||||||
|
sqliteManager.executeStatement("SELECT * FROM pending_chunks WHERE task_id = ?", hashMapOf(1 to taskId)) {
|
||||||
|
val pendingChunks = ArrayList<ChunkCoordinates>()
|
||||||
|
while (it!!.next()) {
|
||||||
|
pendingChunks.add(ChunkCoordinates(it.getInt("chunk_x"), it.getInt("chunk_z")))
|
||||||
|
}
|
||||||
|
completableFuture.complete(pendingChunks)
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all pending chunks of a task
|
||||||
|
*/
|
||||||
|
fun clearPendingChunks(taskId: Int): CompletableFuture<Void> {
|
||||||
|
val completableFuture = CompletableFuture<Void>()
|
||||||
|
sqliteManager.executeStatement("DELETE FROM pending_chunks WHERE task_id = ?", hashMapOf(1 to taskId)) {
|
||||||
|
completableFuture.complete(null)
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addPendingChunks(taskId: Int, pendingChunks: List<ChunkCoordinates>): CompletableFuture<Void> {
|
||||||
|
val futures = ArrayList<CompletableFuture<Void>>()
|
||||||
|
val statementCount = ceil(pendingChunks.size.toDouble() / insertionCount).toInt()
|
||||||
|
|
||||||
|
for (i in 0 until statementCount) {
|
||||||
|
futures.add(
|
||||||
|
insertPendingChunks(
|
||||||
|
taskId,
|
||||||
|
pendingChunks.subList(
|
||||||
|
i * insertionCount,
|
||||||
|
((i * insertionCount) + insertionCount).coerceAtMost(pendingChunks.size)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (futures.size > 0) {
|
||||||
|
return CompletableFuture.allOf(*futures.toTypedArray())
|
||||||
|
} else {
|
||||||
|
return CompletableFuture.supplyAsync { null }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds pending chunks for a taskid
|
||||||
|
*/
|
||||||
|
private fun insertPendingChunks(taskId: Int, pendingChunks: List<ChunkCoordinates>): CompletableFuture<Void> {
|
||||||
|
val completableFuture = CompletableFuture<Void>()
|
||||||
|
if (pendingChunks.isEmpty()) {
|
||||||
|
completableFuture.complete(null)
|
||||||
|
} else {
|
||||||
|
var sql = "INSERT INTO pending_chunks (task_id, chunk_x, chunk_z) VALUES"
|
||||||
|
var index = 1
|
||||||
|
val valueMap = HashMap<Int, Any>()
|
||||||
|
|
||||||
|
for (coordinates in pendingChunks) {
|
||||||
|
sql += "(?, ?, ?),"
|
||||||
|
valueMap[index++] = taskId
|
||||||
|
valueMap[index++] = coordinates.x
|
||||||
|
valueMap[index++] = coordinates.z
|
||||||
|
}
|
||||||
|
sqliteManager.executeStatement(sql.removeSuffix(","), valueMap) {
|
||||||
|
completableFuture.complete(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,201 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.database
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
|
import org.apache.commons.lang.exception.ExceptionUtils
|
||||||
|
import java.sql.Connection
|
||||||
|
import java.sql.DriverManager
|
||||||
|
import java.sql.ResultSet
|
||||||
|
|
||||||
|
class SqliteManager(private val chunkmaster: Chunkmaster) {
|
||||||
|
private val tables = listOf(
|
||||||
|
Pair(
|
||||||
|
"generation_tasks",
|
||||||
|
listOf(
|
||||||
|
Pair("id", "integer PRIMARY KEY AUTOINCREMENT"),
|
||||||
|
Pair("center_x", "integer NOT NULL DEFAULT 0"),
|
||||||
|
Pair("center_z", "integer NOT NULL DEFAULT 0"),
|
||||||
|
Pair("last_x", "integer NOT NULL DEFAULT 0"),
|
||||||
|
Pair("last_z", "integer NOT NULL DEFAULT 0"),
|
||||||
|
Pair("world", "text UNIQUE NOT NULL DEFAULT 'world'"),
|
||||||
|
Pair("radius", "integer DEFAULT -1"),
|
||||||
|
Pair("shape", "text NOT NULL DEFAULT 'square'"),
|
||||||
|
Pair("state", "text NOT NULL DEFAULT 'GENERATING'")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Pair(
|
||||||
|
"world_properties",
|
||||||
|
listOf(
|
||||||
|
Pair("name", "text PRIMARY KEY"),
|
||||||
|
Pair("center_x", "integer NOT NULL DEFAULT 0"),
|
||||||
|
Pair("center_z", "integer NOT NULL DEFAULT 0")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Pair(
|
||||||
|
"pending_chunks",
|
||||||
|
listOf(
|
||||||
|
Pair("id", "integer PRIMARY KEY AUTOINCREMENT"),
|
||||||
|
Pair("task_id", "integer NOT NULL"),
|
||||||
|
Pair("chunk_x", "integer NOT NULL"),
|
||||||
|
Pair("chunk_z", "integer NOT NULL")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Pair(
|
||||||
|
"completed_generation_tasks",
|
||||||
|
listOf(
|
||||||
|
Pair("id", "integer PRIMARY KEY"),
|
||||||
|
Pair("world", "text NOT NULL"),
|
||||||
|
Pair("completed_radius", "integer NOT NULL"),
|
||||||
|
Pair("center_x", "integer NOT NULL"),
|
||||||
|
Pair("center_z", "integer NOT NULL"),
|
||||||
|
Pair("shape", "text NOT NULL")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private val needUpdate = HashSet<Pair<String, Pair<String, String>>>()
|
||||||
|
private val needCreation = HashSet<String>()
|
||||||
|
private var connection: Connection? = null
|
||||||
|
private var activeTasks = 0
|
||||||
|
|
||||||
|
val worldProperties = WorldProperties(this)
|
||||||
|
val pendingChunks = PendingChunks(this)
|
||||||
|
val generationTasks = GenerationTasks(this)
|
||||||
|
val completedGenerationTasks = CompletedGenerationTasks(this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the connection to the database
|
||||||
|
*/
|
||||||
|
fun getConnection(): Connection? {
|
||||||
|
if (this.connection != null) {
|
||||||
|
return this.connection
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Class.forName("org.sqlite.JDBC")
|
||||||
|
this.connection = DriverManager.getConnection(
|
||||||
|
"jdbc:sqlite:${chunkmaster.dataFolder.absolutePath}/" +
|
||||||
|
"${chunkmaster.config.getString("database.filename")}"
|
||||||
|
)
|
||||||
|
return this.connection
|
||||||
|
} catch (e: Exception) {
|
||||||
|
chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("DATABASE_CONNECTION_ERROR"))
|
||||||
|
chunkmaster.logger.severe(e.message)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for and performs an update
|
||||||
|
*/
|
||||||
|
fun init() {
|
||||||
|
this.checkUpdate()
|
||||||
|
this.performUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks which tables need an update or creation.
|
||||||
|
*/
|
||||||
|
private fun checkUpdate() {
|
||||||
|
val meta = getConnection()!!.metaData
|
||||||
|
|
||||||
|
for (table in tables) {
|
||||||
|
val resTables = meta.getTables(null, null, table.first, null)
|
||||||
|
|
||||||
|
if (resTables.next()) { // table exists
|
||||||
|
for (column in table.second) {
|
||||||
|
val resColumn = meta.getColumns(null, null, table.first, column.first)
|
||||||
|
if (!resColumn.next()) {
|
||||||
|
needUpdate.add(Pair(table.first, column))
|
||||||
|
}
|
||||||
|
resColumn.close()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
needCreation.add(table.first)
|
||||||
|
}
|
||||||
|
resTables.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a sql statement on the database.
|
||||||
|
*/
|
||||||
|
fun executeStatement(sql: String, values: HashMap<Int, Any>, callback: ((ResultSet?) -> Unit)?) {
|
||||||
|
val connection = getConnection()
|
||||||
|
activeTasks++
|
||||||
|
if (connection != null) {
|
||||||
|
try {
|
||||||
|
//println("'$sql' with values $values")
|
||||||
|
val statement = connection.prepareStatement(sql)
|
||||||
|
for (parameterValue in values) {
|
||||||
|
statement.setObject(parameterValue.key, parameterValue.value)
|
||||||
|
}
|
||||||
|
statement.execute()
|
||||||
|
val res: ResultSet? = statement.resultSet
|
||||||
|
if (callback != null) {
|
||||||
|
callback(res)
|
||||||
|
}
|
||||||
|
statement.close()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("SQL_ERROR", e.toString()))
|
||||||
|
chunkmaster.logger.info(ExceptionUtils.getStackTrace(e))
|
||||||
|
} finally {
|
||||||
|
activeTasks--
|
||||||
|
if (activeTasks == 0) {
|
||||||
|
connection.close()
|
||||||
|
this.connection = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("NO_DATABASE_CONNECTION"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates or updates tables that needed an update.
|
||||||
|
*/
|
||||||
|
private fun performUpdate() {
|
||||||
|
for (table in needCreation) {
|
||||||
|
try {
|
||||||
|
var tableDef = "CREATE TABLE IF NOT EXISTS $table ("
|
||||||
|
|
||||||
|
for (column in tables.find { it.first == table }!!.second) {
|
||||||
|
tableDef += "${column.first} ${column.second},"
|
||||||
|
}
|
||||||
|
tableDef = tableDef.substringBeforeLast(",") + ");"
|
||||||
|
chunkmaster.logger.finest(
|
||||||
|
chunkmaster.langManager.getLocalized(
|
||||||
|
"CREATE_TABLE_DEFINITION",
|
||||||
|
table,
|
||||||
|
tableDef
|
||||||
|
)
|
||||||
|
)
|
||||||
|
executeStatement(tableDef, HashMap(), null)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("TABLE_CREATE_ERROR", table))
|
||||||
|
chunkmaster.logger.severe(e.message)
|
||||||
|
chunkmaster.logger.info(ExceptionUtils.getStackTrace(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (table in needUpdate) {
|
||||||
|
val updateSql = "ALTER TABLE ${table.first} ADD COLUMN ${table.second.first} ${table.second.second}"
|
||||||
|
try {
|
||||||
|
executeStatement(updateSql, HashMap(), null)
|
||||||
|
chunkmaster.logger.finest(
|
||||||
|
chunkmaster.langManager.getLocalized(
|
||||||
|
"UPDATE_TABLE_DEFINITION",
|
||||||
|
table.first,
|
||||||
|
updateSql
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
chunkmaster.logger.severe(
|
||||||
|
chunkmaster.langManager.getLocalized(
|
||||||
|
"UPDATE_TABLE_FAILED",
|
||||||
|
table.first,
|
||||||
|
updateSql
|
||||||
|
)
|
||||||
|
)
|
||||||
|
chunkmaster.logger.severe(e.message)
|
||||||
|
chunkmaster.logger.info(ExceptionUtils.getStackTrace(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.database
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
|
class WorldProperties(private val sqliteManager: SqliteManager) {
|
||||||
|
|
||||||
|
private val properties = HashMap<String, Pair<Int, Int>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the world center for one world
|
||||||
|
*/
|
||||||
|
fun getWorldCenter(worldName: String): CompletableFuture<Pair<Int, Int>?> {
|
||||||
|
val completableFuture = CompletableFuture<Pair<Int, Int>?>()
|
||||||
|
|
||||||
|
if (properties[worldName] != null) {
|
||||||
|
completableFuture.complete(properties[worldName])
|
||||||
|
} else {
|
||||||
|
sqliteManager.executeStatement("SELECT * FROM world_properties WHERE name = ?", hashMapOf(1 to worldName)) {
|
||||||
|
if (it != null && it.next()) {
|
||||||
|
completableFuture.complete(Pair(it.getInt("center_x"), it.getInt("center_z")))
|
||||||
|
} else {
|
||||||
|
completableFuture.complete(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the center of a world
|
||||||
|
*/
|
||||||
|
fun setWorldCenter(worldName: String, center: Pair<Int, Int>): CompletableFuture<Void> {
|
||||||
|
val completableFuture = CompletableFuture<Void>()
|
||||||
|
|
||||||
|
getWorldCenter(worldName).thenAccept {
|
||||||
|
if (it != null) {
|
||||||
|
updateWorldProperties(worldName, center).thenAccept { completableFuture.complete(null) }
|
||||||
|
} else {
|
||||||
|
insertWorldProperties(worldName, center).thenAccept { completableFuture.complete(null) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an entry in the world properties
|
||||||
|
*/
|
||||||
|
private fun updateWorldProperties(worldName: String, center: Pair<Int, Int>): CompletableFuture<Void> {
|
||||||
|
val completableFuture = CompletableFuture<Void>()
|
||||||
|
sqliteManager.executeStatement(
|
||||||
|
"UPDATE world_properties SET center_x = ?, center_z = ? WHERE name = ?",
|
||||||
|
hashMapOf(
|
||||||
|
1 to center.first,
|
||||||
|
2 to center.second,
|
||||||
|
3 to worldName
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
properties[worldName] = center
|
||||||
|
completableFuture.complete(null)
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts into the world properties
|
||||||
|
*/
|
||||||
|
private fun insertWorldProperties(worldName: String, center: Pair<Int, Int>): CompletableFuture<Void> {
|
||||||
|
val completableFuture = CompletableFuture<Void>()
|
||||||
|
sqliteManager.executeStatement(
|
||||||
|
"INSERT INTO world_properties (name, center_x, center_z) VALUES (?, ?, ?)",
|
||||||
|
hashMapOf(
|
||||||
|
1 to worldName,
|
||||||
|
2 to center.first,
|
||||||
|
3 to center.second
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
properties[worldName] = center
|
||||||
|
completableFuture.complete(null)
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.dynmap
|
||||||
|
|
||||||
|
import org.dynmap.DynmapAPI
|
||||||
|
|
||||||
|
class DynmapApiWrapper(private val dynmapAPI: DynmapAPI) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a marker set by name
|
||||||
|
*/
|
||||||
|
fun getMarkerSet(name: String): ExtendedMarkerSet? {
|
||||||
|
val set = dynmapAPI.markerAPI?.getMarkerSet(name)
|
||||||
|
return if (set != null) {
|
||||||
|
ExtendedMarkerSet(set)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCreateMarkerSet(id: String, name: String): ExtendedMarkerSet? {
|
||||||
|
var set = dynmapAPI.markerAPI?.getMarkerSet(id)
|
||||||
|
if (set == null) {
|
||||||
|
set = dynmapAPI.markerAPI?.createMarkerSet(id, name, null, true)
|
||||||
|
}
|
||||||
|
return if (set != null) {
|
||||||
|
ExtendedMarkerSet(set)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.dynmap
|
||||||
|
|
||||||
|
import org.bukkit.Location
|
||||||
|
import org.dynmap.markers.AreaMarker
|
||||||
|
import org.dynmap.markers.MarkerSet
|
||||||
|
import org.dynmap.markers.PolyLineMarker
|
||||||
|
|
||||||
|
class ExtendedMarkerSet(private val markerSet: MarkerSet) {
|
||||||
|
/**
|
||||||
|
* Creates or updates an area marker depending on if it exists
|
||||||
|
* @param id - the unique id of the area marker
|
||||||
|
* @param label - the label that is displayed when clicking on the marker
|
||||||
|
* @param l1 - the top left corner
|
||||||
|
* @param l2 - the bottom right corner
|
||||||
|
*/
|
||||||
|
fun creUpdateAreMarker(id: String, label: String, l1: Location, l2: Location, style: MarkerStyle?): AreaMarker {
|
||||||
|
var marker = markerSet.findAreaMarker(id)
|
||||||
|
|
||||||
|
if (marker != null) {
|
||||||
|
marker.setCornerLocations(
|
||||||
|
doubleArrayOf(l1.x, l2.x),
|
||||||
|
doubleArrayOf(l1.z, l2.z)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
marker = markerSet.createAreaMarker(
|
||||||
|
id,
|
||||||
|
label,
|
||||||
|
false,
|
||||||
|
l1.world.name,
|
||||||
|
doubleArrayOf(l1.x, l2.x),
|
||||||
|
doubleArrayOf(l1.z, l2.z),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (style != null) {
|
||||||
|
marker.boostFlag = style.boostFlag
|
||||||
|
if (style.lineStyle != null) {
|
||||||
|
marker.setLineStyle(style.lineStyle.weight, style.lineStyle.opacity, style.lineStyle.color)
|
||||||
|
}
|
||||||
|
if (style.fillStyle != null) {
|
||||||
|
marker.setFillStyle(style.fillStyle.opacity, style.fillStyle.color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return marker
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun creUpdatePolyLineMarker(
|
||||||
|
id: String,
|
||||||
|
label: String,
|
||||||
|
edges: List<Location>,
|
||||||
|
style: MarkerStyle?
|
||||||
|
): PolyLineMarker? {
|
||||||
|
var marker = markerSet.findPolyLineMarker(id)
|
||||||
|
val xList = edges.map { it.x }
|
||||||
|
val yList = edges.map { it.y }
|
||||||
|
val zList = edges.map { it.z }
|
||||||
|
if (marker != null) {
|
||||||
|
marker.setCornerLocations(xList.toDoubleArray(), yList.toDoubleArray(), zList.toDoubleArray())
|
||||||
|
} else {
|
||||||
|
marker = markerSet.createPolyLineMarker(
|
||||||
|
id,
|
||||||
|
label,
|
||||||
|
false,
|
||||||
|
edges.first().world.name,
|
||||||
|
xList.toDoubleArray(),
|
||||||
|
yList.toDoubleArray(),
|
||||||
|
zList.toDoubleArray(),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (style != null) {
|
||||||
|
if (style.lineStyle != null) {
|
||||||
|
marker.setLineStyle(style.lineStyle.weight, style.lineStyle.opacity, style.lineStyle.color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return marker
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the area marker for an id
|
||||||
|
* @param id - the id of the marker
|
||||||
|
*/
|
||||||
|
fun findAreaMarker(id: String): AreaMarker? {
|
||||||
|
return markerSet.findAreaMarker(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the polylinemarker for an id
|
||||||
|
*/
|
||||||
|
fun findPolyLineMarker(id: String): PolyLineMarker? {
|
||||||
|
return markerSet.findPolyLineMarker(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes an area marker
|
||||||
|
* @param id - the id of the marker
|
||||||
|
*/
|
||||||
|
fun deleteAreaMarker(id: String) {
|
||||||
|
val marker = this.findAreaMarker(id)
|
||||||
|
marker?.deleteMarker()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deletePolyLineMarker(id: String) {
|
||||||
|
val marker = this.findPolyLineMarker(id)
|
||||||
|
marker?.deleteMarker()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.dynmap
|
||||||
|
|
||||||
|
data class FillStyle(val opacity: Double, val color: Int)
|
@ -0,0 +1,3 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.dynmap
|
||||||
|
|
||||||
|
data class LineStyle(val weight: Int, val opacity: Double, val color: Int)
|
@ -0,0 +1,10 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.dynmap
|
||||||
|
|
||||||
|
import org.dynmap.markers.MarkerIcon
|
||||||
|
|
||||||
|
data class MarkerStyle(
|
||||||
|
val icon: MarkerIcon?,
|
||||||
|
val lineStyle: LineStyle?,
|
||||||
|
val fillStyle: FillStyle?,
|
||||||
|
val boostFlag: Boolean = false
|
||||||
|
)
|
@ -1,10 +1,14 @@
|
|||||||
package net.trivernis.chunkmaster.lib.generation
|
package net.trivernis.chunkmaster.lib.generation
|
||||||
|
|
||||||
import org.bukkit.Location
|
import org.bukkit.Location
|
||||||
import org.bukkit.World
|
import org.bukkit.World
|
||||||
|
|
||||||
class ChunkCoordinates(val x: Int, val z: Int) {
|
class ChunkCoordinates(val x: Int, val z: Int) {
|
||||||
fun getCenterLocation(world: World): Location {
|
fun getCenterLocation(world: World): Location {
|
||||||
return Location(world, ((x*16) + 8).toDouble(), 1.0, ((x*16) + 8).toDouble())
|
return Location(world, ((x * 16) + 8).toDouble(), 1.0, ((z * 16) + 8).toDouble())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "($x, $z)"
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.generation
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
|
import org.bukkit.Chunk
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
|
||||||
|
class ChunkUnloader(private val plugin: Chunkmaster) : Runnable {
|
||||||
|
private val maxLoadedChunks = plugin.config.getInt("generation.max-loaded-chunks")
|
||||||
|
private val lock = ReentrantReadWriteLock()
|
||||||
|
private var unloadingQueue = Vector<Chunk>(maxLoadedChunks)
|
||||||
|
val isFull: Boolean
|
||||||
|
get() {
|
||||||
|
return pendingSize == maxLoadedChunks
|
||||||
|
}
|
||||||
|
|
||||||
|
val pendingSize: Int
|
||||||
|
get() {
|
||||||
|
lock.readLock().lock()
|
||||||
|
val size = unloadingQueue.size
|
||||||
|
lock.readLock().unlock()
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unloads all chunks in the unloading queue with each run
|
||||||
|
*/
|
||||||
|
override fun run() {
|
||||||
|
lock.writeLock().lock()
|
||||||
|
try {
|
||||||
|
val chunkToUnload = unloadingQueue.toHashSet()
|
||||||
|
|
||||||
|
for (chunk in chunkToUnload) {
|
||||||
|
try {
|
||||||
|
chunk.unload(true)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
plugin.logger.severe(e.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unloadingQueue.clear()
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a chunk to unload to the queue
|
||||||
|
*/
|
||||||
|
fun add(chunk: Chunk) {
|
||||||
|
lock.writeLock().lockInterruptibly()
|
||||||
|
try {
|
||||||
|
unloadingQueue.add(chunk)
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.generation
|
||||||
|
|
||||||
|
import io.papermc.lib.PaperLib
|
||||||
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
|
import net.trivernis.chunkmaster.lib.shapes.Shape
|
||||||
|
import org.bukkit.World
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue
|
||||||
|
|
||||||
|
class DefaultGenerationTask(
|
||||||
|
private val plugin: Chunkmaster,
|
||||||
|
unloader: ChunkUnloader,
|
||||||
|
world: World,
|
||||||
|
startChunk: ChunkCoordinates,
|
||||||
|
override val radius: Int = -1,
|
||||||
|
shape: Shape,
|
||||||
|
missingChunks: HashSet<ChunkCoordinates>,
|
||||||
|
state: TaskState
|
||||||
|
) : GenerationTask(plugin, world, unloader, startChunk, shape, missingChunks, state) {
|
||||||
|
|
||||||
|
private val maxPendingChunks = plugin.config.getInt("generation.max-pending-chunks")
|
||||||
|
val pendingChunks = ArrayBlockingQueue<PendingChunkEntry>(maxPendingChunks)
|
||||||
|
|
||||||
|
override var count = 0
|
||||||
|
override var endReached: Boolean = false
|
||||||
|
|
||||||
|
init {
|
||||||
|
updateGenerationAreaMarker()
|
||||||
|
count = shape.count
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the generation task. Every Iteration the next chunks will be generated if
|
||||||
|
* they haven't been generated already
|
||||||
|
* After a configured number of chunks chunks have been generated, they will all be unloaded and saved.
|
||||||
|
*/
|
||||||
|
override fun generate() {
|
||||||
|
generateMissing()
|
||||||
|
seekGenerated()
|
||||||
|
generateUntilBorder()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates that all chunks have been generated or generates missing ones
|
||||||
|
*/
|
||||||
|
override fun validate() {
|
||||||
|
this.shape.reset()
|
||||||
|
val missedChunks = HashSet<ChunkCoordinates>()
|
||||||
|
|
||||||
|
while (!cancelRun && !borderReached()) {
|
||||||
|
val chunkCoordinates = nextChunkCoordinates
|
||||||
|
triggerDynmapRender(chunkCoordinates)
|
||||||
|
if (!PaperLib.isChunkGenerated(world, chunkCoordinates.x, chunkCoordinates.z)) {
|
||||||
|
missedChunks.add(chunkCoordinates)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.missingChunks.addAll(missedChunks)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates chunks that are missing
|
||||||
|
*/
|
||||||
|
override fun generateMissing() {
|
||||||
|
val missing = this.missingChunks.toHashSet()
|
||||||
|
this.count = 0
|
||||||
|
|
||||||
|
while (missing.size > 0 && !cancelRun) {
|
||||||
|
if (plugin.mspt < msptThreshold && !unloader.isFull) {
|
||||||
|
val chunk = missing.first()
|
||||||
|
missing.remove(chunk)
|
||||||
|
this.requestGeneration(chunk)
|
||||||
|
this.count++
|
||||||
|
} else {
|
||||||
|
Thread.sleep(50L)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!cancelRun) {
|
||||||
|
this.joinPending()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seeks until it encounters a chunk that hasn't been generated yet
|
||||||
|
*/
|
||||||
|
private fun seekGenerated() {
|
||||||
|
do {
|
||||||
|
lastChunkCoords = nextChunkCoordinates
|
||||||
|
count = shape.count
|
||||||
|
} while (PaperLib.isChunkGenerated(world, lastChunkCoords.x, lastChunkCoords.z) && !borderReached())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the world until it encounters the worlds border
|
||||||
|
*/
|
||||||
|
private fun generateUntilBorder() {
|
||||||
|
var chunkCoordinates: ChunkCoordinates
|
||||||
|
|
||||||
|
while (!cancelRun && !borderReached()) {
|
||||||
|
if (plugin.mspt < msptThreshold && !unloader.isFull) {
|
||||||
|
chunkCoordinates = nextChunkCoordinates
|
||||||
|
requestGeneration(chunkCoordinates)
|
||||||
|
|
||||||
|
lastChunkCoords = chunkCoordinates
|
||||||
|
count = shape.count
|
||||||
|
} else {
|
||||||
|
Thread.sleep(50L)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!cancelRun) {
|
||||||
|
joinPending()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun joinPending() {
|
||||||
|
while (!this.pendingChunks.isEmpty()) {
|
||||||
|
Thread.sleep(msptThreshold)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the generation of a chunk
|
||||||
|
*/
|
||||||
|
private fun requestGeneration(chunkCoordinates: ChunkCoordinates) {
|
||||||
|
if (!PaperLib.isChunkGenerated(world, chunkCoordinates.x, chunkCoordinates.z) || PaperLib.isSpigot()) {
|
||||||
|
val pendingChunkEntry = PendingChunkEntry(
|
||||||
|
chunkCoordinates,
|
||||||
|
PaperLib.getChunkAtAsync(world, chunkCoordinates.x, chunkCoordinates.z, true)
|
||||||
|
)
|
||||||
|
this.pendingChunks.put(pendingChunkEntry)
|
||||||
|
pendingChunkEntry.chunk.thenAccept {
|
||||||
|
this.unloader.add(it)
|
||||||
|
this.pendingChunks.remove(pendingChunkEntry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels the generation task.
|
||||||
|
* This unloads all chunks that were generated but not unloaded yet.
|
||||||
|
*/
|
||||||
|
override fun cancel() {
|
||||||
|
this.cancelRun = true
|
||||||
|
this.pendingChunks.forEach { it.chunk.cancel(false) }
|
||||||
|
updateGenerationAreaMarker(true)
|
||||||
|
}
|
||||||
|
}
|
@ -1,267 +1,383 @@
|
|||||||
package net.trivernis.chunkmaster.lib.generation
|
package net.trivernis.chunkmaster.lib.generation
|
||||||
|
|
||||||
import io.papermc.lib.PaperLib
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
import net.trivernis.chunkmaster.Chunkmaster
|
import net.trivernis.chunkmaster.lib.generation.taskentry.PausedTaskEntry
|
||||||
import org.bukkit.Chunk
|
import net.trivernis.chunkmaster.lib.generation.taskentry.RunningTaskEntry
|
||||||
import org.bukkit.Server
|
import net.trivernis.chunkmaster.lib.generation.taskentry.TaskEntry
|
||||||
import org.bukkit.World
|
import net.trivernis.chunkmaster.lib.shapes.Circle
|
||||||
import java.lang.Exception
|
import net.trivernis.chunkmaster.lib.shapes.Square
|
||||||
import java.lang.NullPointerException
|
import org.bukkit.Server
|
||||||
|
import org.bukkit.World
|
||||||
class GenerationManager(private val chunkmaster: Chunkmaster, private val server: Server) {
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
val tasks: HashSet<RunningTaskEntry> = HashSet()
|
class GenerationManager(private val chunkmaster: Chunkmaster, private val server: Server) {
|
||||||
val pausedTasks: HashSet<PausedTaskEntry> = HashSet()
|
|
||||||
val allTasks: HashSet<TaskEntry>
|
val tasks: HashSet<RunningTaskEntry> = HashSet()
|
||||||
get() {
|
val pausedTasks: HashSet<PausedTaskEntry> = HashSet()
|
||||||
val all = HashSet<TaskEntry>()
|
val worldProperties = chunkmaster.sqliteManager.worldProperties
|
||||||
all.addAll(pausedTasks)
|
private val pendingChunksTable = chunkmaster.sqliteManager.pendingChunks
|
||||||
all.addAll(tasks)
|
private val generationTasks = chunkmaster.sqliteManager.generationTasks
|
||||||
return all
|
private val completedGenerationTasks = chunkmaster.sqliteManager.completedGenerationTasks
|
||||||
}
|
|
||||||
var paused = false
|
private val unloadingPeriod: Long
|
||||||
private set
|
get() {
|
||||||
|
return chunkmaster.config.getLong("generation.unloading-period")
|
||||||
/**
|
}
|
||||||
* Adds a generation task
|
private val pauseOnPlayerCount: Int
|
||||||
*/
|
get() {
|
||||||
fun addTask(world: World, stopAfter: Int = -1): Int {
|
return chunkmaster.config.getInt("generation.pause-on-player-count")
|
||||||
val foundTask = allTasks.find { it.generationTask.world == world }
|
}
|
||||||
if (foundTask == null) {
|
private val autostart: Boolean
|
||||||
val centerChunk = ChunkCoordinates(world.spawnLocation.chunk.x, world.spawnLocation.chunk.z)
|
get() {
|
||||||
val generationTask = createGenerationTask(world, centerChunk, centerChunk, stopAfter)
|
return chunkmaster.config.getBoolean("generation.autostart")
|
||||||
|
}
|
||||||
val insertStatement = chunkmaster.sqliteConnection.prepareStatement(
|
|
||||||
"""
|
val loadedChunkCount: Int
|
||||||
INSERT INTO generation_tasks (center_x, center_z, last_x, last_z, world, stop_after)
|
get() {
|
||||||
values (?, ?, ?, ?, ?, ?)
|
return unloader.pendingSize
|
||||||
"""
|
}
|
||||||
)
|
private val unloader = ChunkUnloader(chunkmaster)
|
||||||
insertStatement.setInt(1, centerChunk.x)
|
|
||||||
insertStatement.setInt(2, centerChunk.z)
|
|
||||||
insertStatement.setInt(3, centerChunk.x)
|
val allTasks: HashSet<TaskEntry>
|
||||||
insertStatement.setInt(4, centerChunk.z)
|
get() {
|
||||||
insertStatement.setString(5, world.name)
|
if (this.tasks.isEmpty() && this.pausedTasks.isEmpty()) {
|
||||||
insertStatement.setInt(6, stopAfter)
|
this.startAll()
|
||||||
insertStatement.execute()
|
if (server.onlinePlayers.size >= pauseOnPlayerCount) {
|
||||||
|
this.pauseAll()
|
||||||
val getIdStatement = chunkmaster.sqliteConnection.prepareStatement(
|
}
|
||||||
"""
|
}
|
||||||
SELECT id FROM generation_tasks ORDER BY id DESC LIMIT 1
|
val all = HashSet<TaskEntry>()
|
||||||
""".trimIndent()
|
all.addAll(pausedTasks)
|
||||||
)
|
all.addAll(tasks)
|
||||||
getIdStatement.execute()
|
return all
|
||||||
val result = getIdStatement.resultSet
|
}
|
||||||
result.next()
|
var paused = false
|
||||||
val id: Int = result.getInt("id")
|
private set
|
||||||
|
|
||||||
insertStatement.close()
|
/**
|
||||||
getIdStatement.close()
|
* Adds a generation task
|
||||||
|
*/
|
||||||
generationTask.onEndReached {
|
fun addTask(world: World, radius: Int = -1, shape: String = "square"): Int {
|
||||||
chunkmaster.logger.info("Task #${id} finished after ${generationTask.count} chunks.")
|
val foundTask = allTasks.find { it.generationTask.world == world }
|
||||||
removeTask(id)
|
|
||||||
}
|
if (foundTask == null) {
|
||||||
|
val center = worldProperties.getWorldCenter(world.name).join()
|
||||||
if (!paused) {
|
|
||||||
val task = server.scheduler.runTaskTimer(
|
val centerChunk = if (center == null) {
|
||||||
chunkmaster, generationTask, 200, // 10 sec delay
|
ChunkCoordinates(world.spawnLocation.chunk.x, world.spawnLocation.chunk.z)
|
||||||
chunkmaster.config.getLong("generation.period")
|
} else {
|
||||||
)
|
ChunkCoordinates(center.first, center.second)
|
||||||
tasks.add(RunningTaskEntry(id, task, generationTask))
|
}
|
||||||
} else {
|
val generationTask = createGenerationTask(world, centerChunk, centerChunk, radius, shape, null)
|
||||||
pausedTasks.add(PausedTaskEntry(id, generationTask))
|
val id = generationTasks.addGenerationTask(world.name, centerChunk, radius, shape).join()
|
||||||
}
|
|
||||||
|
generationTask.onEndReached {
|
||||||
return id
|
chunkmaster.logger.info(chunkmaster.langManager.getLocalized("TASK_FINISHED", id, it.count))
|
||||||
} else {
|
removeTask(id)
|
||||||
return foundTask.id
|
}
|
||||||
}
|
|
||||||
}
|
if (!paused) {
|
||||||
|
val taskEntry = RunningTaskEntry(
|
||||||
/**
|
id,
|
||||||
* Resumes a generation task
|
generationTask
|
||||||
*/
|
)
|
||||||
private fun resumeTask(
|
taskEntry.start()
|
||||||
world: World,
|
tasks.add(taskEntry)
|
||||||
center: ChunkCoordinates,
|
} else {
|
||||||
last: ChunkCoordinates,
|
pausedTasks.add(
|
||||||
id: Int,
|
PausedTaskEntry(
|
||||||
stopAfter: Int = -1
|
id,
|
||||||
) {
|
generationTask
|
||||||
if (!paused) {
|
)
|
||||||
chunkmaster.logger.info("Resuming chunk generation task for world \"${world.name}\"")
|
)
|
||||||
val generationTask = createGenerationTask(world, center, last, stopAfter)
|
}
|
||||||
val task = server.scheduler.runTaskTimer(
|
|
||||||
chunkmaster, generationTask, 200, // 10 sec delay
|
return id
|
||||||
chunkmaster.config.getLong("generation.period")
|
} else {
|
||||||
)
|
return foundTask.id
|
||||||
tasks.add(RunningTaskEntry(id, task, generationTask))
|
}
|
||||||
generationTask.onEndReached {
|
}
|
||||||
chunkmaster.logger.info("Task #${id} finished after ${generationTask.count} chunks.")
|
|
||||||
removeTask(id)
|
/**
|
||||||
}
|
* Resumes a generation task
|
||||||
}
|
*/
|
||||||
}
|
private fun resumeTask(
|
||||||
|
world: World,
|
||||||
/**
|
center: ChunkCoordinates,
|
||||||
* Stops a running generation task.
|
last: ChunkCoordinates,
|
||||||
*/
|
id: Int,
|
||||||
fun removeTask(id: Int): Boolean {
|
radius: Int = -1,
|
||||||
val taskEntry: TaskEntry? = if (this.paused) {
|
shape: String = "square",
|
||||||
this.pausedTasks.find { it.id == id }
|
pendingChunks: List<ChunkCoordinates>?
|
||||||
} else {
|
) {
|
||||||
this.tasks.find { it.id == id }
|
if (!paused) {
|
||||||
}
|
chunkmaster.logger.info(chunkmaster.langManager.getLocalized("RESUME_FOR_WORLD", world.name))
|
||||||
if (taskEntry != null) {
|
val generationTask = createGenerationTask(world, center, last, radius, shape, pendingChunks)
|
||||||
taskEntry.cancel()
|
val taskEntry = RunningTaskEntry(
|
||||||
val deleteTask = chunkmaster.sqliteConnection.prepareStatement(
|
id,
|
||||||
"""
|
generationTask
|
||||||
DELETE FROM generation_tasks WHERE id = ?;
|
)
|
||||||
""".trimIndent()
|
taskEntry.start()
|
||||||
)
|
tasks.add(taskEntry)
|
||||||
deleteTask.setInt(1, taskEntry.id)
|
generationTask.onEndReached {
|
||||||
deleteTask.execute()
|
chunkmaster.logger.info(chunkmaster.langManager.getLocalized("TASK_FINISHED", id, generationTask.count))
|
||||||
deleteTask.close()
|
removeTask(id)
|
||||||
|
}
|
||||||
if (taskEntry is RunningTaskEntry) {
|
}
|
||||||
if (taskEntry.task.isCancelled) {
|
}
|
||||||
tasks.remove(taskEntry)
|
|
||||||
}
|
/**
|
||||||
} else if (taskEntry is PausedTaskEntry) {
|
* Stops a running generation task.
|
||||||
pausedTasks.remove(taskEntry)
|
*/
|
||||||
}
|
fun removeTask(id: Int): Boolean {
|
||||||
return true
|
val taskEntry: TaskEntry? = if (this.paused) {
|
||||||
}
|
this.pausedTasks.find { it.id == id }
|
||||||
return false
|
} else {
|
||||||
}
|
this.tasks.find { it.id == id }
|
||||||
|
}
|
||||||
/**
|
try {
|
||||||
* Init
|
if (taskEntry != null) {
|
||||||
* Loads tasks from the database and resumes them
|
if (taskEntry.generationTask.isRunning && taskEntry is RunningTaskEntry) {
|
||||||
*/
|
taskEntry.cancel(chunkmaster.config.getLong("mspt-pause-threshold"))
|
||||||
fun init() {
|
}
|
||||||
chunkmaster.logger.info("Creating task to load chunk generation Tasks later...")
|
generationTasks.deleteGenerationTask(id)
|
||||||
server.scheduler.runTaskTimer(chunkmaster, Runnable {
|
completedGenerationTasks.addCompletedTask(
|
||||||
saveProgress() // save progress every 30 seconds
|
id,
|
||||||
}, 600, 600)
|
taskEntry.generationTask.world.name,
|
||||||
server.scheduler.runTaskLater(chunkmaster, Runnable {
|
taskEntry.generationTask.shape.currentRadius(),
|
||||||
if (server.onlinePlayers.isEmpty()) {
|
taskEntry.generationTask.startChunk,
|
||||||
startAll() // run startAll after 10 seconds if empty
|
taskEntry.generationTask.shape.javaClass.simpleName
|
||||||
}
|
)
|
||||||
}, 600)
|
pendingChunksTable.clearPendingChunks(id)
|
||||||
}
|
|
||||||
|
if (taskEntry is RunningTaskEntry) {
|
||||||
/**
|
tasks.remove(taskEntry)
|
||||||
* Stops all generation tasks
|
} else if (taskEntry is PausedTaskEntry) {
|
||||||
*/
|
pausedTasks.remove(taskEntry)
|
||||||
fun stopAll() {
|
}
|
||||||
saveProgress()
|
return true
|
||||||
val removalSet = HashSet<RunningTaskEntry>()
|
}
|
||||||
for (task in tasks) {
|
} catch (e: Exception) {
|
||||||
task.generationTask.cancel()
|
chunkmaster.logger.severe(e.toString())
|
||||||
task.task.cancel()
|
}
|
||||||
if (task.task.isCancelled) {
|
return false
|
||||||
removalSet.add(task)
|
}
|
||||||
}
|
|
||||||
chunkmaster.logger.info("Canceled task #${task.id}")
|
/**
|
||||||
}
|
* Init
|
||||||
tasks.removeAll(removalSet)
|
* Loads tasks from the database and resumes them
|
||||||
}
|
*/
|
||||||
|
fun init() {
|
||||||
/**
|
chunkmaster.logger.info(chunkmaster.langManager.getLocalized("CREATE_DELAYED_LOAD"))
|
||||||
* Starts all generation tasks.
|
server.scheduler.runTaskTimer(chunkmaster, Runnable {
|
||||||
*/
|
saveProgress() // save progress every 30 seconds
|
||||||
fun startAll() {
|
}, 600, 600)
|
||||||
val savedTasksStatement = chunkmaster.sqliteConnection.prepareStatement("SELECT * FROM generation_tasks")
|
server.scheduler.runTaskLater(chunkmaster, Runnable {
|
||||||
savedTasksStatement.execute()
|
this.startAll()
|
||||||
val res = savedTasksStatement.resultSet
|
if (server.onlinePlayers.count() >= pauseOnPlayerCount || !autostart) {
|
||||||
while (res.next()) {
|
if (!autostart) {
|
||||||
try {
|
chunkmaster.logger.info(chunkmaster.langManager.getLocalized("NO_AUTOSTART"))
|
||||||
val id = res.getInt("id")
|
}
|
||||||
val world = server.getWorld(res.getString("world"))
|
this.pauseAll()
|
||||||
val center = ChunkCoordinates(res.getInt("center_x"), res.getInt("center_z"))
|
}
|
||||||
val last = ChunkCoordinates(res.getInt("last_x"), res.getInt("last_z"))
|
}, 20)
|
||||||
val stopAfter = res.getInt("stop_after")
|
server.scheduler.runTaskTimer(chunkmaster, unloader, unloadingPeriod, unloadingPeriod)
|
||||||
if (this.tasks.find { it.id == id } == null) {
|
}
|
||||||
resumeTask(world!!, center, last, id, stopAfter)
|
|
||||||
}
|
/**
|
||||||
} catch (error: NullPointerException) {
|
* Stops all generation tasks
|
||||||
chunkmaster.logger.severe("Failed to load Task ${res.getInt("id")}.")
|
*/
|
||||||
}
|
fun stopAll() {
|
||||||
}
|
val removalSet = HashSet<RunningTaskEntry>()
|
||||||
savedTasksStatement.close()
|
for (task in tasks) {
|
||||||
if (tasks.isNotEmpty()) {
|
val id = task.id
|
||||||
chunkmaster.logger.info("${tasks.size} saved tasks loaded.")
|
chunkmaster.logger.info(chunkmaster.langManager.getLocalized("SAVING_TASK_PROGRESS", task.id))
|
||||||
}
|
saveProgressToDatabase(task.generationTask, id).join()
|
||||||
}
|
if (!task.cancel(chunkmaster.config.getLong("mspt-pause-threshold"))) {
|
||||||
|
chunkmaster.logger.warning(chunkmaster.langManager.getLocalized("CANCEL_FAIL", task.id))
|
||||||
/**
|
}
|
||||||
* Pauses all tasks
|
removalSet.add(task)
|
||||||
*/
|
|
||||||
fun pauseAll() {
|
chunkmaster.logger.info(chunkmaster.langManager.getLocalized("TASK_CANCELLED", task.id))
|
||||||
paused = true
|
}
|
||||||
for (task in tasks) {
|
tasks.removeAll(removalSet)
|
||||||
pausedTasks.add(PausedTaskEntry(task.id, task.generationTask))
|
if (unloader.pendingSize > 0) {
|
||||||
}
|
chunkmaster.logger.info(chunkmaster.langManager.getLocalized("SAVING_CHUNKS", unloader.pendingSize))
|
||||||
stopAll()
|
unloader.run()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Resumes all tasks
|
/**
|
||||||
*/
|
* Starts all generation tasks.
|
||||||
fun resumeAll() {
|
*/
|
||||||
paused = false
|
fun startAll() {
|
||||||
pausedTasks.clear()
|
generationTasks.getGenerationTasks().thenAccept { tasks ->
|
||||||
startAll()
|
for (task in tasks) {
|
||||||
}
|
val world = server.getWorld(task.world)
|
||||||
|
if (world != null) {
|
||||||
/**
|
pendingChunksTable.getPendingChunks(task.id).thenAccept {
|
||||||
* Saves the task progress
|
resumeTask(world, task.center, task.last, task.id, task.radius, task.shape, it)
|
||||||
*/
|
}
|
||||||
private fun saveProgress() {
|
} else {
|
||||||
for (task in tasks) {
|
chunkmaster.logger.severe(chunkmaster.langManager.getLocalized("TASK_LOAD_FAILED", task.id))
|
||||||
try {
|
}
|
||||||
val genTask = task.generationTask
|
}
|
||||||
chunkmaster.logger.info(
|
}
|
||||||
"""Task #${task.id} running for "${genTask.world.name}".
|
|
||||||
|Progress ${task.generationTask.count} chunks
|
if (tasks.isNotEmpty()) {
|
||||||
|${if (task.generationTask.stopAfter > 0) "(${"%.2f".format((task.generationTask.count.toDouble() /
|
chunkmaster.logger.info(chunkmaster.langManager.getLocalized("TASK_LOAD_SUCCESS", tasks.size))
|
||||||
task.generationTask.stopAfter.toDouble()) * 100)}%)" else ""}.
|
}
|
||||||
| Speed: ${"%.1f".format(task.generationSpeed)} chunks/sec,
|
}
|
||||||
|Last Chunk: ${genTask.lastChunk.x}, ${genTask.lastChunk.z}""".trimMargin("|").replace('\n', ' ')
|
|
||||||
)
|
/**
|
||||||
val updateStatement = chunkmaster.sqliteConnection.prepareStatement(
|
* Pauses all tasks
|
||||||
"""
|
*/
|
||||||
UPDATE generation_tasks SET last_x = ?, last_z = ?
|
fun pauseAll() {
|
||||||
WHERE id = ?
|
paused = true
|
||||||
""".trimIndent()
|
for (task in tasks) {
|
||||||
)
|
pausedTasks.add(
|
||||||
updateStatement.setInt(1, genTask.lastChunk.x)
|
PausedTaskEntry(
|
||||||
updateStatement.setInt(2, genTask.lastChunk.z)
|
task.id,
|
||||||
updateStatement.setInt(3, task.id)
|
task.generationTask
|
||||||
updateStatement.execute()
|
)
|
||||||
updateStatement.close()
|
)
|
||||||
} catch (error: Exception) {
|
}
|
||||||
chunkmaster.logger.warning("Exception when saving task progress ${error.message}")
|
stopAll()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
/**
|
||||||
|
* Resumes all tasks
|
||||||
/**
|
*/
|
||||||
* Creates a new generation task. This method is used to create a task depending
|
fun resumeAll() {
|
||||||
* on the server type (Paper/Spigot).
|
paused = false
|
||||||
*/
|
pausedTasks.clear()
|
||||||
private fun createGenerationTask(
|
startAll()
|
||||||
world: World,
|
}
|
||||||
center: ChunkCoordinates,
|
|
||||||
start: ChunkCoordinates,
|
/**
|
||||||
stopAfter: Int
|
* Saves the task progress
|
||||||
): GenerationTask {
|
*/
|
||||||
return if (PaperLib.isPaper()) {
|
private fun saveProgress() {
|
||||||
GenerationTaskPaper(chunkmaster, world, center, start, stopAfter)
|
for (task in tasks) {
|
||||||
} else {
|
try {
|
||||||
GenerationTaskSpigot(chunkmaster, world, center, start, stopAfter)
|
if (task.generationTask.state == TaskState.CORRECTING) {
|
||||||
}
|
reportCorrectionProgress(task)
|
||||||
}
|
} else {
|
||||||
|
reportGenerationProgress(task)
|
||||||
|
}
|
||||||
|
saveProgressToDatabase(task.generationTask, task.id)
|
||||||
|
} catch (error: Exception) {
|
||||||
|
chunkmaster.logger.warning(chunkmaster.langManager.getLocalized("TASK_SAVE_FAILED", error.toString()))
|
||||||
|
error.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports the progress for correcting tasks
|
||||||
|
*/
|
||||||
|
private fun reportCorrectionProgress(task: RunningTaskEntry) {
|
||||||
|
val genTask = task.generationTask
|
||||||
|
val progress = if (genTask.missingChunks.size > 0) {
|
||||||
|
"(${(genTask.count / genTask.missingChunks.size) * 100}%)"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
chunkmaster.logger.info(
|
||||||
|
chunkmaster.langManager.getLocalized(
|
||||||
|
"TASK_PERIODIC_REPORT_CORRECTING",
|
||||||
|
task.id,
|
||||||
|
genTask.world.name,
|
||||||
|
genTask.count,
|
||||||
|
progress
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports the progress of the chunk generation
|
||||||
|
*/
|
||||||
|
private fun reportGenerationProgress(task: RunningTaskEntry) {
|
||||||
|
val genTask = task.generationTask
|
||||||
|
val (speed, chunkSpeed) = task.generationSpeed
|
||||||
|
val progress =
|
||||||
|
genTask.shape.progress(if (genTask.radius < 0) (genTask.world.worldBorder.size / 32).toInt() else null)
|
||||||
|
val percentage =
|
||||||
|
"(${"%.2f".format(progress * 100)}%)"
|
||||||
|
|
||||||
|
val eta = if (speed!! > 0) {
|
||||||
|
val remaining = 1 - progress
|
||||||
|
val etaSeconds = remaining / speed
|
||||||
|
val hours: Int = (etaSeconds / 3600).toInt()
|
||||||
|
val minutes: Int = ((etaSeconds % 3600) / 60).toInt()
|
||||||
|
val seconds: Int = (etaSeconds % 60).toInt()
|
||||||
|
", ETA: %dh %dmin %ds".format(hours, minutes, seconds)
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
chunkmaster.logger.info(
|
||||||
|
chunkmaster.langManager.getLocalized(
|
||||||
|
"TASK_PERIODIC_REPORT",
|
||||||
|
task.id,
|
||||||
|
genTask.world.name,
|
||||||
|
genTask.state.toString(),
|
||||||
|
genTask.count,
|
||||||
|
percentage,
|
||||||
|
eta,
|
||||||
|
chunkSpeed!!,
|
||||||
|
genTask.lastChunkCoords.x,
|
||||||
|
genTask.lastChunkCoords.z
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the generation progress to the database
|
||||||
|
*/
|
||||||
|
private fun saveProgressToDatabase(generationTask: GenerationTask, id: Int): CompletableFuture<Void> {
|
||||||
|
val completableFuture = CompletableFuture<Void>()
|
||||||
|
generationTasks.updateGenerationTask(id, generationTask.lastChunkCoords, generationTask.state).thenAccept {
|
||||||
|
pendingChunksTable.clearPendingChunks(id).thenAccept {
|
||||||
|
if (generationTask is DefaultGenerationTask) {
|
||||||
|
if (generationTask.pendingChunks.size > 0) {
|
||||||
|
pendingChunksTable.addPendingChunks(id, generationTask.pendingChunks.map { it.coordinates })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pendingChunksTable.addPendingChunks(id, generationTask.missingChunks.toList()).thenAccept {
|
||||||
|
completableFuture.complete(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new generation task. This method is used to create a task depending
|
||||||
|
* on the server type (Paper/Spigot).
|
||||||
|
*/
|
||||||
|
private fun createGenerationTask(
|
||||||
|
world: World,
|
||||||
|
center: ChunkCoordinates,
|
||||||
|
start: ChunkCoordinates,
|
||||||
|
radius: Int,
|
||||||
|
shapeName: String,
|
||||||
|
pendingChunks: List<ChunkCoordinates>?
|
||||||
|
): GenerationTask {
|
||||||
|
val shape = when (shapeName) {
|
||||||
|
"circle" -> Circle(Pair(center.x, center.z), Pair(start.x, start.z), radius)
|
||||||
|
"square" -> Square(Pair(center.x, center.z), Pair(start.x, start.z), radius)
|
||||||
|
else -> Square(Pair(center.x, center.z), Pair(start.x, start.z), radius)
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefaultGenerationTask(
|
||||||
|
chunkmaster,
|
||||||
|
unloader,
|
||||||
|
world,
|
||||||
|
start,
|
||||||
|
radius,
|
||||||
|
shape, pendingChunks?.toHashSet() ?: HashSet(),
|
||||||
|
TaskState.GENERATING
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,64 +1,144 @@
|
|||||||
package net.trivernis.chunkmaster.lib.generation
|
package net.trivernis.chunkmaster.lib.generation
|
||||||
|
|
||||||
import net.trivernis.chunkmaster.Chunkmaster
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
import net.trivernis.chunkmaster.lib.Spiral
|
import net.trivernis.chunkmaster.lib.dynmap.*
|
||||||
import org.bukkit.Chunk
|
import net.trivernis.chunkmaster.lib.shapes.Shape
|
||||||
import org.bukkit.World
|
import org.bukkit.World
|
||||||
|
import kotlin.math.ceil
|
||||||
/**
|
|
||||||
* Interface for generation tasks.
|
/**
|
||||||
*/
|
* Interface for generation tasks.
|
||||||
abstract class GenerationTask(plugin: Chunkmaster, centerChunk: ChunkCoordinates, startChunk: ChunkCoordinates) :
|
*/
|
||||||
Runnable {
|
abstract class GenerationTask(
|
||||||
|
plugin: Chunkmaster,
|
||||||
abstract val stopAfter: Int
|
val world: World,
|
||||||
abstract val world: World
|
protected val unloader: ChunkUnloader,
|
||||||
abstract val count: Int
|
val startChunk: ChunkCoordinates,
|
||||||
abstract val endReached: Boolean
|
val shape: Shape,
|
||||||
|
val missingChunks: HashSet<ChunkCoordinates>,
|
||||||
protected val spiral: Spiral =
|
var state: TaskState
|
||||||
Spiral(Pair(centerChunk.x, centerChunk.z), Pair(startChunk.x, startChunk.z))
|
) :
|
||||||
protected val loadedChunks: HashSet<Chunk> = HashSet()
|
Runnable {
|
||||||
protected var lastChunkCoords = ChunkCoordinates(startChunk.x, startChunk.z)
|
|
||||||
protected val chunkSkips = plugin.config.getInt("generation.chunk-skips-per-step")
|
abstract val radius: Int
|
||||||
protected val msptThreshold = plugin.config.getLong("generation.mspt-pause-threshold")
|
abstract var count: Int
|
||||||
protected val maxLoadedChunks = plugin.config.getInt("generation.max-loaded-chunks")
|
abstract var endReached: Boolean
|
||||||
protected val chunksPerStep = plugin.config.getInt("generation.chunks-per-step")
|
var isRunning: Boolean = false
|
||||||
|
|
||||||
protected var endReachedCallback: (() -> Unit)? = null
|
var lastChunkCoords = ChunkCoordinates(startChunk.x, startChunk.z)
|
||||||
private set
|
protected set
|
||||||
|
protected val msptThreshold = plugin.config.getLong("generation.mspt-pause-threshold")
|
||||||
abstract override fun run()
|
protected var cancelRun: Boolean = false
|
||||||
abstract fun cancel()
|
|
||||||
|
private var endReachedCallback: ((GenerationTask) -> Unit)? = null
|
||||||
val nextChunkCoordinates: ChunkCoordinates
|
|
||||||
get() {
|
private val dynmapIntegration = plugin.config.getBoolean("dynmap")
|
||||||
val nextChunkCoords = spiral.next()
|
private val dynmap = plugin.dynmapApi
|
||||||
return ChunkCoordinates(nextChunkCoords.first, nextChunkCoords.second)
|
private val markerSet: ExtendedMarkerSet? = if (dynmap != null) {
|
||||||
}
|
DynmapApiWrapper(dynmap).getCreateMarkerSet("chunkmaster", "Chunkmaster")
|
||||||
|
} else {
|
||||||
val lastChunk: Chunk
|
null
|
||||||
get() {
|
}
|
||||||
return world.getChunkAt(lastChunkCoords.x, lastChunkCoords.z)
|
private val markerAreaStyle = MarkerStyle(null, LineStyle(2, 1.0, 0x0022FF), FillStyle(.0, 0))
|
||||||
}
|
private val markerAreaId = "chunkmaster_genarea_${world.name}"
|
||||||
|
private val markerAreaName = "Chunkmaster Generation Area (${ceil(shape.total()).toInt()} chunks)"
|
||||||
val nextChunk: Chunk
|
private val ignoreWorldborder = plugin.config.getBoolean("generation.ignore-worldborder")
|
||||||
get() {
|
|
||||||
val next = nextChunkCoordinates
|
abstract fun generate()
|
||||||
return world.getChunkAt(next.x, next.z)
|
abstract fun validate()
|
||||||
}
|
abstract fun generateMissing()
|
||||||
|
abstract fun cancel()
|
||||||
/**
|
|
||||||
* Checks if the World border or the maximum chunk setting for the task is reached.
|
override fun run() {
|
||||||
*/
|
isRunning = true
|
||||||
protected fun borderReached(): Boolean {
|
try {
|
||||||
return !world.worldBorder.isInside(lastChunkCoords.getCenterLocation(world)) || (stopAfter in 1..count)
|
when (state) {
|
||||||
}
|
TaskState.GENERATING -> {
|
||||||
|
this.generate()
|
||||||
/**
|
if (!cancelRun) {
|
||||||
* Registers end reached callback
|
this.state = TaskState.VALIDATING
|
||||||
*/
|
this.validate()
|
||||||
fun onEndReached(cb: () -> Unit) {
|
}
|
||||||
endReachedCallback = cb
|
if (!cancelRun) {
|
||||||
}
|
this.state = TaskState.CORRECTING
|
||||||
|
this.generateMissing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TaskState.VALIDATING -> {
|
||||||
|
this.validate()
|
||||||
|
if (!cancelRun) {
|
||||||
|
this.state = TaskState.CORRECTING
|
||||||
|
this.generateMissing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TaskState.CORRECTING -> {
|
||||||
|
this.generateMissing()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!cancelRun && this.borderReached()) {
|
||||||
|
this.setEndReached()
|
||||||
|
}
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
}
|
||||||
|
isRunning = false
|
||||||
|
}
|
||||||
|
|
||||||
|
val nextChunkCoordinates: ChunkCoordinates
|
||||||
|
get() {
|
||||||
|
val nextChunkCoords = shape.next()
|
||||||
|
return ChunkCoordinates(nextChunkCoords.first, nextChunkCoords.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the World border or the maximum chunk setting for the task is reached.
|
||||||
|
*/
|
||||||
|
protected fun borderReached(): Boolean {
|
||||||
|
return (!world.worldBorder.isInside(lastChunkCoords.getCenterLocation(world)) && !ignoreWorldborder)
|
||||||
|
|| shape.endReached()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the dynmap marker for the generation radius
|
||||||
|
*/
|
||||||
|
protected fun updateGenerationAreaMarker(clear: Boolean = false) {
|
||||||
|
if (clear) {
|
||||||
|
markerSet?.deletePolyLineMarker(markerAreaId)
|
||||||
|
} else if (dynmapIntegration && radius > 0) {
|
||||||
|
markerSet?.creUpdatePolyLineMarker(
|
||||||
|
markerAreaId,
|
||||||
|
markerAreaName,
|
||||||
|
this.shape.getShapeEdgeLocations()
|
||||||
|
.map { ChunkCoordinates(it.first, it.second).getCenterLocation(this.world) },
|
||||||
|
markerAreaStyle
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun triggerDynmapRender(chunkCoordinates: ChunkCoordinates) {
|
||||||
|
if (dynmapIntegration) {
|
||||||
|
dynmap?.triggerRenderOfVolume(
|
||||||
|
world.getBlockAt(chunkCoordinates.x * 16, 0, chunkCoordinates.z * 16).location,
|
||||||
|
world.getBlockAt((chunkCoordinates.x * 16) + 16, 255, (chunkCoordinates.z * 16) + 16).location
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the invocation of the end reached callback and additional logic
|
||||||
|
*/
|
||||||
|
private fun setEndReached() {
|
||||||
|
endReached = true
|
||||||
|
count = shape.count
|
||||||
|
updateGenerationAreaMarker(true)
|
||||||
|
endReachedCallback?.invoke(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers end reached callback
|
||||||
|
*/
|
||||||
|
fun onEndReached(cb: (GenerationTask) -> Unit) {
|
||||||
|
endReachedCallback = cb
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,117 +0,0 @@
|
|||||||
package net.trivernis.chunkmaster.lib.generation
|
|
||||||
|
|
||||||
import net.trivernis.chunkmaster.Chunkmaster
|
|
||||||
import org.bukkit.Chunk
|
|
||||||
import org.bukkit.World
|
|
||||||
import java.util.concurrent.CompletableFuture
|
|
||||||
import io.papermc.lib.PaperLib
|
|
||||||
|
|
||||||
class GenerationTaskPaper(
|
|
||||||
private val plugin: Chunkmaster, override val world: World,
|
|
||||||
centerChunk: ChunkCoordinates, private val startChunk: ChunkCoordinates,
|
|
||||||
override val stopAfter: Int = -1
|
|
||||||
) : GenerationTask(plugin, centerChunk, startChunk) {
|
|
||||||
|
|
||||||
private val maxPendingChunks = plugin.config.getInt("generation.max-pending-chunks")
|
|
||||||
|
|
||||||
private val pendingChunks = HashSet<CompletableFuture<Chunk>>()
|
|
||||||
|
|
||||||
override var count = 0
|
|
||||||
private set
|
|
||||||
override var endReached: Boolean = false
|
|
||||||
private set
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs the generation task. Every Iteration the next chunk will be generated if
|
|
||||||
* it hasn't been generated already.
|
|
||||||
* After 10 chunks have been generated, they will all be unloaded and saved.
|
|
||||||
*/
|
|
||||||
override fun run() {
|
|
||||||
if (plugin.mspt < msptThreshold) { // pause when tps < 2
|
|
||||||
if (loadedChunks.size > maxLoadedChunks) {
|
|
||||||
for (chunk in loadedChunks) {
|
|
||||||
if (chunk.isLoaded) {
|
|
||||||
chunk.unload(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loadedChunks.clear()
|
|
||||||
} else if (pendingChunks.size < maxPendingChunks) { // if more than 10 chunks are pending, wait.
|
|
||||||
if (borderReached()) {
|
|
||||||
endReached = true
|
|
||||||
endReachedCallback?.invoke()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var chunk = nextChunkCoordinates
|
|
||||||
for (i in 1 until chunkSkips) {
|
|
||||||
if (PaperLib.isChunkGenerated(world, chunk.x, chunk.z)) {
|
|
||||||
chunk = nextChunkCoordinates
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PaperLib.isChunkGenerated(world, chunk.x, chunk.z)) {
|
|
||||||
for (i in 0 until minOf(chunksPerStep, (stopAfter - count) - 1)) {
|
|
||||||
if (!PaperLib.isChunkGenerated(world, chunk.x, chunk.z)) {
|
|
||||||
pendingChunks.add(PaperLib.getChunkAtAsync(world, chunk.x, chunk.z, true))
|
|
||||||
}
|
|
||||||
chunk = nextChunkCoordinates
|
|
||||||
}
|
|
||||||
if (!PaperLib.isChunkGenerated(world, chunk.x, chunk.z)) {
|
|
||||||
pendingChunks.add(PaperLib.getChunkAtAsync(world, chunk.x, chunk.z, true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastChunkCoords = chunk
|
|
||||||
count = spiral.count // set the count to the more accurate spiral count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkChunksLoaded()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels the generation task.
|
|
||||||
* This unloads all chunks that were generated but not unloaded yet.
|
|
||||||
*/
|
|
||||||
override fun cancel() {
|
|
||||||
unloadAllChunks()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels all pending chunks and unloads all loaded chunks.
|
|
||||||
*/
|
|
||||||
fun unloadAllChunks() {
|
|
||||||
for (pendingChunk in pendingChunks) {
|
|
||||||
if (pendingChunk.isDone) {
|
|
||||||
loadedChunks.add(pendingChunk.get())
|
|
||||||
} else {
|
|
||||||
pendingChunk.cancel(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pendingChunks.clear()
|
|
||||||
if (loadedChunks.isNotEmpty()) {
|
|
||||||
lastChunkCoords = ChunkCoordinates(loadedChunks.last().x, loadedChunks.last().z)
|
|
||||||
}
|
|
||||||
for (chunk in loadedChunks) {
|
|
||||||
if (chunk.isLoaded) {
|
|
||||||
chunk.unload(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if some chunks have been loaded and adds them to the loaded chunk set.
|
|
||||||
*/
|
|
||||||
private fun checkChunksLoaded() {
|
|
||||||
val completedEntrys = HashSet<CompletableFuture<Chunk>>()
|
|
||||||
for (pendingChunk in pendingChunks) {
|
|
||||||
if (pendingChunk.isDone) {
|
|
||||||
completedEntrys.add(pendingChunk)
|
|
||||||
loadedChunks.add(pendingChunk.get())
|
|
||||||
} else if (pendingChunk.isCompletedExceptionally || pendingChunk.isCancelled) {
|
|
||||||
completedEntrys.add(pendingChunk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pendingChunks.removeAll(completedEntrys)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
package net.trivernis.chunkmaster.lib.generation
|
|
||||||
|
|
||||||
import net.trivernis.chunkmaster.Chunkmaster
|
|
||||||
import org.bukkit.Chunk
|
|
||||||
import org.bukkit.World
|
|
||||||
|
|
||||||
class GenerationTaskSpigot(
|
|
||||||
private val plugin: Chunkmaster, override val world: World,
|
|
||||||
centerChunk: ChunkCoordinates, private val startChunk: ChunkCoordinates,
|
|
||||||
override val stopAfter: Int = -1
|
|
||||||
) : GenerationTask(plugin, centerChunk, startChunk) {
|
|
||||||
|
|
||||||
|
|
||||||
override var count = 0
|
|
||||||
private set
|
|
||||||
override var endReached: Boolean = false
|
|
||||||
private set
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs the generation task. Every Iteration the next chunk will be generated if
|
|
||||||
* it hasn't been generated already.
|
|
||||||
* After 10 chunks have been generated, they will all be unloaded and saved.
|
|
||||||
*/
|
|
||||||
override fun run() {
|
|
||||||
if (plugin.mspt < msptThreshold) { // pause when tps < 2
|
|
||||||
if (loadedChunks.size > maxLoadedChunks) {
|
|
||||||
for (chunk in loadedChunks) {
|
|
||||||
if (chunk.isLoaded) {
|
|
||||||
chunk.unload(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loadedChunks.clear()
|
|
||||||
} else {
|
|
||||||
if (borderReached()) {
|
|
||||||
endReached = true
|
|
||||||
endReachedCallback?.invoke()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var chunk = nextChunkCoordinates
|
|
||||||
|
|
||||||
if (!world.isChunkGenerated(chunk.x, chunk.z)) {
|
|
||||||
for (i in 0 until minOf(chunksPerStep, stopAfter - count)) {
|
|
||||||
val chunkInstance = world.getChunkAt(chunk.x, chunk.z)
|
|
||||||
chunkInstance.load(true)
|
|
||||||
loadedChunks.add(chunkInstance)
|
|
||||||
chunk = nextChunkCoordinates
|
|
||||||
}
|
|
||||||
val chunkInstance = world.getChunkAt(chunk.x, chunk.z)
|
|
||||||
chunkInstance.load(true)
|
|
||||||
loadedChunks.add(chunkInstance)
|
|
||||||
}
|
|
||||||
lastChunkCoords = chunk
|
|
||||||
count = spiral.count // set the count to the more accurate spiral count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels the generation task.
|
|
||||||
* This unloads all chunks that were generated but not unloaded yet.
|
|
||||||
*/
|
|
||||||
override fun cancel() {
|
|
||||||
for (chunk in loadedChunks) {
|
|
||||||
if (chunk.isLoaded) {
|
|
||||||
chunk.unload(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package net.trivernis.chunkmaster.lib.generation
|
|
||||||
|
|
||||||
class PausedTaskEntry(
|
|
||||||
override val id: Int,
|
|
||||||
override val generationTask: GenerationTask
|
|
||||||
) : TaskEntry {
|
|
||||||
override fun cancel() {
|
|
||||||
generationTask.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,9 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.generation
|
||||||
|
|
||||||
|
import org.bukkit.Chunk
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
|
class PendingChunkEntry(val coordinates: ChunkCoordinates, val chunk: CompletableFuture<Chunk>) {
|
||||||
|
val isDone: Boolean
|
||||||
|
get() = chunk.isDone
|
||||||
|
}
|
@ -1,37 +0,0 @@
|
|||||||
package net.trivernis.chunkmaster.lib.generation
|
|
||||||
|
|
||||||
import org.bukkit.scheduler.BukkitTask
|
|
||||||
|
|
||||||
class RunningTaskEntry(
|
|
||||||
override val id: Int,
|
|
||||||
val task: BukkitTask,
|
|
||||||
override val generationTask: GenerationTask
|
|
||||||
) : TaskEntry {
|
|
||||||
|
|
||||||
private var lastProgress: Pair<Long, Int>? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the generation Speed
|
|
||||||
*/
|
|
||||||
val generationSpeed: Double?
|
|
||||||
get() {
|
|
||||||
var generationSpeed: Double? = null
|
|
||||||
if (lastProgress != null) {
|
|
||||||
val chunkDiff = generationTask.count - lastProgress!!.second
|
|
||||||
val timeDiff = (System.currentTimeMillis() - lastProgress!!.first).toDouble()/1000
|
|
||||||
generationSpeed = chunkDiff.toDouble()/timeDiff
|
|
||||||
}
|
|
||||||
lastProgress = Pair(System.currentTimeMillis(), generationTask.count)
|
|
||||||
return generationSpeed
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
lastProgress = Pair(System.currentTimeMillis(), generationTask.count)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun cancel() {
|
|
||||||
task.cancel()
|
|
||||||
generationTask.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package net.trivernis.chunkmaster.lib.generation
|
|
||||||
|
|
||||||
import org.bukkit.scheduler.BukkitTask
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic task entry
|
|
||||||
*/
|
|
||||||
interface TaskEntry {
|
|
||||||
val id: Int
|
|
||||||
val generationTask: GenerationTask
|
|
||||||
|
|
||||||
fun cancel()
|
|
||||||
}
|
|
@ -0,0 +1,24 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.generation
|
||||||
|
|
||||||
|
enum class TaskState {
|
||||||
|
GENERATING {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "GENERATING"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
VALIDATING {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "VALIDATING"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CORRECTING {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "CORRECTING"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PAUSING {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "PAUSING"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.generation.taskentry
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.lib.generation.GenerationTask
|
||||||
|
|
||||||
|
class PausedTaskEntry(
|
||||||
|
override val id: Int,
|
||||||
|
override val generationTask: GenerationTask
|
||||||
|
) : TaskEntry
|
@ -0,0 +1,71 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.generation.taskentry
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.lib.generation.GenerationTask
|
||||||
|
|
||||||
|
class RunningTaskEntry(
|
||||||
|
override val id: Int,
|
||||||
|
override val generationTask: GenerationTask
|
||||||
|
) : TaskEntry {
|
||||||
|
|
||||||
|
private var lastProgress: Pair<Long, Double>? = null
|
||||||
|
private var lastChunkCount: Pair<Long, Int>? = null
|
||||||
|
private var thread = Thread(generationTask)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the generation Speed
|
||||||
|
*/
|
||||||
|
val generationSpeed: Pair<Double?, Double?>
|
||||||
|
get() {
|
||||||
|
var generationSpeed: Double? = null
|
||||||
|
var chunkGenerationSpeed: Double? = null
|
||||||
|
val progress =
|
||||||
|
generationTask.shape.progress(if (generationTask.radius < 0) (generationTask.world.worldBorder.size / 32).toInt() else null)
|
||||||
|
if (lastProgress != null) {
|
||||||
|
val progressDiff = progress - lastProgress!!.second
|
||||||
|
val timeDiff = (System.currentTimeMillis() - lastProgress!!.first).toDouble() / 1000
|
||||||
|
generationSpeed = progressDiff / timeDiff
|
||||||
|
}
|
||||||
|
if (lastChunkCount != null) {
|
||||||
|
val chunkDiff = generationTask.count - lastChunkCount!!.second
|
||||||
|
val timeDiff = (System.currentTimeMillis() - lastChunkCount!!.first).toDouble() / 1000
|
||||||
|
chunkGenerationSpeed = chunkDiff / timeDiff
|
||||||
|
}
|
||||||
|
lastProgress = Pair(System.currentTimeMillis(), progress)
|
||||||
|
lastChunkCount = Pair(System.currentTimeMillis(), generationTask.count)
|
||||||
|
return Pair(generationSpeed, chunkGenerationSpeed)
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
lastProgress = Pair(System.currentTimeMillis(), generationTask.shape.progress(null))
|
||||||
|
lastChunkCount = Pair(System.currentTimeMillis(), generationTask.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
thread.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel(timeout: Long): Boolean {
|
||||||
|
if (generationTask.isRunning) {
|
||||||
|
generationTask.cancel()
|
||||||
|
thread.interrupt()
|
||||||
|
}
|
||||||
|
return try {
|
||||||
|
joinThread(timeout)
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun joinThread(timeout: Long): Boolean {
|
||||||
|
var threadStopped = false
|
||||||
|
|
||||||
|
for (i in 0..100) {
|
||||||
|
if (!thread.isAlive || !generationTask.isRunning) {
|
||||||
|
threadStopped = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
Thread.sleep(timeout / 100)
|
||||||
|
}
|
||||||
|
return threadStopped
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.generation.taskentry
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.lib.generation.GenerationTask
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic task entry
|
||||||
|
*/
|
||||||
|
interface TaskEntry {
|
||||||
|
val id: Int
|
||||||
|
val generationTask: GenerationTask
|
||||||
|
}
|
@ -0,0 +1,148 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.shapes
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
import kotlin.collections.HashSet
|
||||||
|
import kotlin.math.PI
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
class Circle(center: Pair<Int, Int>, start: Pair<Int, Int>, radius: Int) : Shape(center, start, radius) {
|
||||||
|
private var r = 0
|
||||||
|
private var coords = Stack<Pair<Int, Int>>()
|
||||||
|
private var previousCoords = HashSet<Pair<Int, Int>>()
|
||||||
|
|
||||||
|
override fun endReached(): Boolean {
|
||||||
|
if ((radius + 1) in 1..r) return true
|
||||||
|
return radius > 0 && coords.isEmpty() && r >= radius
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun total(): Double {
|
||||||
|
return (PI * radius.toFloat().pow(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun progress(maxRadius: Int?): Double {
|
||||||
|
// TODO: Radius inner progress
|
||||||
|
return if (maxRadius != null) {
|
||||||
|
(count / (PI * maxRadius.toFloat().pow(2))).coerceAtMost(1.0)
|
||||||
|
} else {
|
||||||
|
(count / (PI * radius.toFloat().pow(2))).coerceAtMost(1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun currentRadius(): Int {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the edge locations of the shape to be used
|
||||||
|
* with dynmap markers
|
||||||
|
*/
|
||||||
|
override fun getShapeEdgeLocations(): List<Pair<Int, Int>> {
|
||||||
|
val locations = this.getCircleCoordinates(this.radius)
|
||||||
|
locations.add(locations.first())
|
||||||
|
return locations.map { Pair(it.first + center.first, it.second + center.second) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next coordinate of the circle until the end is reached
|
||||||
|
*/
|
||||||
|
override fun next(): Pair<Int, Int> {
|
||||||
|
if (endReached()) {
|
||||||
|
return currentPos
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0 && currentPos != center) {
|
||||||
|
val tmpCircle = Circle(center, center, radius)
|
||||||
|
while (tmpCircle.next() != currentPos && !tmpCircle.endReached());
|
||||||
|
this.count = tmpCircle.count
|
||||||
|
this.r = tmpCircle.r
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
count++
|
||||||
|
return center
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coords.isEmpty()) {
|
||||||
|
r++
|
||||||
|
val tmpCoords = HashSet<Pair<Int, Int>>()
|
||||||
|
tmpCoords.addAll(getCircleCoordinates((r * 2) - 1).map { Pair(it.first / 2, it.second / 2) })
|
||||||
|
tmpCoords.addAll(getCircleCoordinates(r))
|
||||||
|
tmpCoords.removeAll(previousCoords)
|
||||||
|
previousCoords.clear()
|
||||||
|
coords.addAll(tmpCoords)
|
||||||
|
previousCoords.addAll(tmpCoords)
|
||||||
|
}
|
||||||
|
|
||||||
|
count++
|
||||||
|
val coord = coords.pop()
|
||||||
|
currentPos = Pair(coord.first + center.first, coord.second + center.second)
|
||||||
|
return currentPos
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the int coordinates for a circle
|
||||||
|
* Some coordinates might already be present in the list
|
||||||
|
* @param r - the radius
|
||||||
|
*/
|
||||||
|
private fun getCircleCoordinates(r: Int): Vector<Pair<Int, Int>> {
|
||||||
|
val coords = Vector<Pair<Int, Int>>()
|
||||||
|
val segCoords = getSegment(r)
|
||||||
|
coords.addAll(segCoords.reversed())
|
||||||
|
|
||||||
|
for (step in 1..7) {
|
||||||
|
val tmpSeg = Vector<Pair<Int, Int>>()
|
||||||
|
|
||||||
|
for (pos in segCoords) {
|
||||||
|
val coord = when (step) {
|
||||||
|
1 -> Pair(pos.first, -pos.second)
|
||||||
|
2 -> Pair(pos.second, -pos.first)
|
||||||
|
3 -> Pair(-pos.second, -pos.first)
|
||||||
|
4 -> Pair(-pos.first, -pos.second)
|
||||||
|
5 -> Pair(-pos.first, pos.second)
|
||||||
|
6 -> Pair(-pos.second, pos.first)
|
||||||
|
7 -> Pair(pos.second, pos.first)
|
||||||
|
else -> pos
|
||||||
|
}
|
||||||
|
if (coord !in coords) {
|
||||||
|
tmpSeg.add(coord)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (step % 2 == 0) {
|
||||||
|
coords.addAll(tmpSeg.reversed())
|
||||||
|
} else {
|
||||||
|
coords.addAll(tmpSeg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return coords
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the int coordinates for a circles segment
|
||||||
|
* @param r - the radius
|
||||||
|
*/
|
||||||
|
private fun getSegment(r: Int): ArrayList<Pair<Int, Int>> {
|
||||||
|
var d = -r
|
||||||
|
var x = r
|
||||||
|
var y = 0
|
||||||
|
val coords = ArrayList<Pair<Int, Int>>()
|
||||||
|
while (y <= x) {
|
||||||
|
coords.add(Pair(x, y))
|
||||||
|
d += 2 * y + 1
|
||||||
|
y += 1
|
||||||
|
if (d > 0) {
|
||||||
|
x -= 1
|
||||||
|
d -= 2 * x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return coords
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reset() {
|
||||||
|
this.r = 0
|
||||||
|
this.currentPos = center
|
||||||
|
this.previousCoords.clear()
|
||||||
|
this.count = 0
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.shapes
|
||||||
|
|
||||||
|
abstract class Shape(protected val center: Pair<Int, Int>, start: Pair<Int, Int>, radius: Int) {
|
||||||
|
protected var currentPos = start
|
||||||
|
protected var radius = radius
|
||||||
|
private set
|
||||||
|
var count = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next value
|
||||||
|
*/
|
||||||
|
abstract fun next(): Pair<Int, Int>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the shape can provide a next value
|
||||||
|
*/
|
||||||
|
abstract fun endReached(): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the progress of the shape
|
||||||
|
*/
|
||||||
|
abstract fun progress(maxRadius: Int?): Double
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total number of chunks to generate
|
||||||
|
*/
|
||||||
|
abstract fun total(): Double
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current radius
|
||||||
|
*/
|
||||||
|
abstract fun currentRadius(): Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns a poly marker for the shape
|
||||||
|
*/
|
||||||
|
abstract fun getShapeEdgeLocations(): List<Pair<Int, Int>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the shape to its center start position
|
||||||
|
*/
|
||||||
|
abstract fun reset()
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.shapes
|
||||||
|
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
class Square(center: Pair<Int, Int>, start: Pair<Int, Int>, radius: Int) : Shape(center, start, radius) {
|
||||||
|
private var direction = 0
|
||||||
|
|
||||||
|
override fun endReached(): Boolean {
|
||||||
|
val distances = getDistances(center, currentPos)
|
||||||
|
return radius > 0 && ((direction == 3
|
||||||
|
&& abs(distances.first) == abs(distances.second)
|
||||||
|
&& abs(distances.first) == radius)
|
||||||
|
|| (distances.first > radius || distances.second > radius))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun total(): Double {
|
||||||
|
return (radius * 2).toDouble().pow(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun progress(maxRadius: Int?): Double {
|
||||||
|
return if (maxRadius != null) {
|
||||||
|
(count / (maxRadius * 2).toDouble().pow(2)).coerceAtMost(1.0)
|
||||||
|
} else {
|
||||||
|
(count / (radius * 2).toDouble().pow(2)).coerceAtMost(1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun currentRadius(): Int {
|
||||||
|
val distances = getDistances(center, currentPos)
|
||||||
|
return distances.first.coerceAtLeast(distances.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next value in the spiral
|
||||||
|
*/
|
||||||
|
override fun next(): Pair<Int, Int> {
|
||||||
|
if (endReached()) {
|
||||||
|
return currentPos
|
||||||
|
}
|
||||||
|
if (count == 0 && currentPos != center) {
|
||||||
|
// simulate the spiral to get the correct direction and count
|
||||||
|
val simSpiral = Square(center, center, radius)
|
||||||
|
while (simSpiral.next() != currentPos && !simSpiral.endReached());
|
||||||
|
direction = simSpiral.direction
|
||||||
|
count = simSpiral.count
|
||||||
|
}
|
||||||
|
if (count == 1) { // because of the center behaviour
|
||||||
|
count++
|
||||||
|
return currentPos
|
||||||
|
}
|
||||||
|
if (currentPos == center) { // the center has to be handled exclusively
|
||||||
|
currentPos = Pair(center.first, center.second + 1)
|
||||||
|
count++
|
||||||
|
return center
|
||||||
|
} else {
|
||||||
|
val distances = getDistances(center, currentPos)
|
||||||
|
if (abs(distances.first) == abs(distances.second)) {
|
||||||
|
direction = (direction + 1) % 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
when (direction) {
|
||||||
|
0 -> {
|
||||||
|
currentPos = Pair(currentPos.first + 1, currentPos.second)
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
currentPos = Pair(currentPos.first, currentPos.second - 1)
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
currentPos = Pair(currentPos.first - 1, currentPos.second)
|
||||||
|
}
|
||||||
|
3 -> {
|
||||||
|
currentPos = Pair(currentPos.first, currentPos.second + 1)
|
||||||
|
}
|
||||||
|
4 -> {
|
||||||
|
currentPos = Pair(currentPos.first, currentPos.second + 1)
|
||||||
|
direction = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
return currentPos
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the edges to be used with dynmap markers
|
||||||
|
*/
|
||||||
|
override fun getShapeEdgeLocations(): List<Pair<Int, Int>> {
|
||||||
|
val a = Pair(this.radius + center.first, this.radius + center.second)
|
||||||
|
val b = Pair(this.radius + center.first, -this.radius + center.second)
|
||||||
|
val c = Pair(-this.radius + center.first, -this.radius + center.second)
|
||||||
|
val d = Pair(-this.radius + center.first, this.radius + center.second)
|
||||||
|
return listOf(a, b, c, d, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the distances between 2 coordinates
|
||||||
|
*/
|
||||||
|
private fun getDistances(pos1: Pair<Int, Int>, pos2: Pair<Int, Int>): Pair<Int, Int> {
|
||||||
|
return Pair(pos2.first - pos1.first, pos2.second - pos1.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the shape to its starting parameters
|
||||||
|
*/
|
||||||
|
override fun reset() {
|
||||||
|
this.currentPos = center
|
||||||
|
this.count = 0
|
||||||
|
this.direction = 0
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
RESUME_FOR_WORLD = Resuming chunk generation task for world '%s'...
|
||||||
|
TASK_FINISHED = Task #%d finished after %d chunks.
|
||||||
|
TASK_CANCELLED = Cancelled task #%s.
|
||||||
|
TASK_LOAD_FAILED = §cFailed to load task #%d.
|
||||||
|
TASK_LOAD_SUCCESS = %d saved tasks loaded.
|
||||||
|
TASK_NOT_FOUND = §cTask %s not found!
|
||||||
|
CREATE_DELAYED_LOAD = Creating task to load chunk generation Tasks later...
|
||||||
|
TASK_PERIODIC_REPORT = Task #%d running for '%s'. State: %s. Progress: %d chunks %s %s, Speed: %.1f ch/s, Last Chunk: %d, %d
|
||||||
|
TASK_PERIODIC_REPORT_CORRECTING = Task #%d generating missing chunks for '%s'. Progress: %d chunks %s
|
||||||
|
TASK_SAVE_FAILED = §cException when saving tasks: %s
|
||||||
|
|
||||||
|
WORLD_NAME_REQUIRED = §cYou need to provide a world name!
|
||||||
|
WORLD_NOT_FOUND = §cWorld §2%s §cnot found!
|
||||||
|
TASK_ALREADY_EXISTS = §cA task for '%s' already exists!
|
||||||
|
TASK_CREATION_SUCCESS = §9Generation Task for world §2%s§9 §2%s§9 and shape %s successfully created!
|
||||||
|
TASK_UNIT_WORLDBORDER = until world border
|
||||||
|
TASK_UNIT_RADIUS = with a radius of %s
|
||||||
|
TASK_ID_REQUIRED = §cYou need to provide a task id!
|
||||||
|
INVALID_ARGUMENT = §cInvalid argument at %s: %s!
|
||||||
|
|
||||||
|
PAUSED_TASKS_HEADER = Currently Paused Generation Tasks
|
||||||
|
TASKS_ENTRY = - §9#%d§r - §2%s§r - §2%s§r - §2%s chunks %s§r
|
||||||
|
RUNNING_TASKS_HEADER = Currently Running Generation Tasks
|
||||||
|
NO_GENERATION_TASKS = There are no generation tasks.
|
||||||
|
|
||||||
|
COMPLETED_TASKS_HEADER = §nCompleted Generation Tasks§r
|
||||||
|
COMPLETED_WORLD_HEADER = §l%s§r
|
||||||
|
COMPLETED_TASK_ENTRY = - §9#%d§r: §2%d§r chunks radius from center §2(%d, %d)§r with shape §2%s§r
|
||||||
|
|
||||||
|
PAUSE_SUCCESS = §9Paused all generation tasks.
|
||||||
|
ALREADY_PAUSED = §cThe generation process is already paused!
|
||||||
|
|
||||||
|
RESUME_SUCCESS = §9Resumed all generation Tasks.
|
||||||
|
NOT_PAUSED = §cThe generation process is not paused!
|
||||||
|
|
||||||
|
CONFIG_RELOADING = Reloading the config file...
|
||||||
|
CONFIG_RELOADED = §2The config file has been reloaded!
|
||||||
|
|
||||||
|
TELEPORTED = §9You have been teleported to chunk §2%s, %s
|
||||||
|
TP_ONLY_PLAYER = §cThis command can only be executed by a player!
|
||||||
|
|
||||||
|
NO_PERMISSION = §cYou do not have the permission for this command!
|
||||||
|
SUBCOMMAND_NOT_FOUND = §cSubcommand §2%s §cnot found!
|
||||||
|
|
||||||
|
STOPPING_ALL_TASKS = Stopping all generation tasks...
|
||||||
|
SAVING_TASK_PROGRESS = Saving the progress of task #%s to the database...
|
||||||
|
DB_INIT = Initializing database...
|
||||||
|
DB_INIT_FINISHED = Database fully initialized.
|
||||||
|
DB_INIT_EROR = Failed to init database: %s.
|
||||||
|
|
||||||
|
DATABASE_CONNECTION_ERROR = §cCould not get the database connection!
|
||||||
|
SQL_ERROR = §cAn eror occured on sql %s!
|
||||||
|
NO_DATABASE_CONNECTION = §cCould not execute sql: No database connection.
|
||||||
|
CREATE_TABLE_DEFINITION = Created table %s with definition %s.
|
||||||
|
TABLE_CREATE_ERROR = §cError when creation table %s.
|
||||||
|
UPDATE_TABLE_DEFINITION = Updated table %s with sql %s.
|
||||||
|
UPDATE_TABLE_FAILED = Failed to update table %s with sql %s.
|
||||||
|
|
||||||
|
TOO_FEW_ARGUMENTS = §cYou did not provide enough arguments.
|
||||||
|
COORD_INVALID = §cThe provided coordinate ('%s', '%s') is invalid!
|
||||||
|
CENTER_UPDATED = §9The center for world §2%s §9has been set to §2(%s, %s)§9.
|
||||||
|
CENTER_INFO = §9The center for world §2%s §9is §2(%s, %s)§9.
|
||||||
|
|
||||||
|
PLUGIN_DETECTED = Detected %s version %s
|
||||||
|
|
||||||
|
RESUME_PLAYER_LEAVE = The player count is smaller than the configured pause value. Resuming generation...
|
||||||
|
PAUSE_PLAYER_JOIN = The player count has reached the pause value. Pausing generation...
|
||||||
|
PAUSE_MANUALLY = Generation was manually paused. Not resuming automatically.
|
||||||
|
|
||||||
|
STATS_HEADER = §nStats§r
|
||||||
|
STATS_SERVER = §lServer Stats§r
|
||||||
|
STATS_SERVER_VERSION = - Server Version: §2%s§r
|
||||||
|
STATS_PLUGIN_VERSION = - Plugin Version: §2%s§r
|
||||||
|
STATS_MEMORY = - Memory (u/a): §2%d mb / %d mb = (%.2f percent)§r
|
||||||
|
STATS_CORES = - Cores: §2%d§r
|
||||||
|
|
||||||
|
STATS_WORLD_NAME = §l%s§r
|
||||||
|
STATS_ENTITY_COUNT = - §2%d§r Entities
|
||||||
|
STATS_LOADED_CHUNKS = - §2%d§r Loaded Chunks
|
||||||
|
STATS_PLUGIN_LOADED_CHUNKS = - §2%d§r Chunks Loaded by Chunkmaster
|
||||||
|
STATS_GENERATING = - Generating: §2%s§r
|
||||||
|
|
||||||
|
SAVING_CHUNKS = Saving %d loaded chunks...
|
||||||
|
CANCEL_FAIL = Failed to cancel task #%d in the given timeout!
|
||||||
|
NO_AUTOSTART = Autostart set to §2false§r. Pausing...
|
@ -0,0 +1,86 @@
|
|||||||
|
RESUME_FOR_WORLD = Setze das Chunk-Generieren für Welt '%s' fort...
|
||||||
|
TASK_FINISHED = Aufgabe #%d wurde nach %d chunks beendet.
|
||||||
|
TASK_CANCELLED = Aufgabe #%s wurde abgebrochen.
|
||||||
|
TASK_LOAD_FAILED = §cAufgabe #%d konnte nicht geladen werden.
|
||||||
|
TASK_LOAD_SUCCESS = %d gespeicherte Aufgaben wurden geladen.
|
||||||
|
TASK_NOT_FOUND = §cAufgabe %s konnte nicht gefunden werden!
|
||||||
|
CREATE_DELAYED_LOAD = Erstelle einen Bukkit-Task zum verzögerten Laden von Aufgaben...
|
||||||
|
TASK_PERIODIC_REPORT = Aufgabe #%d für Welt '%s'. Status: %s. Fortschritt: %d chunks %s %s, Geschwindigkeit: %.1f ch/s, Letzer Chunk: %d, %d
|
||||||
|
TASK_PERIODIC_REPORT_CORRECTING = Aufgabe #%d generiert fehlende Chunks für Welt '%s'. Fortschritt: %d chunks %s
|
||||||
|
TASK_SAVE_FAILED = §cFehler beim Speichern der Aufgaben: %s
|
||||||
|
|
||||||
|
WORLD_NAME_REQUIRED = §cDu musst einen Weltennamen angeben!
|
||||||
|
WORLD_NOT_FOUND = §c Die Welt §2%s §cwurde nicht gefunden!
|
||||||
|
TASK_ALREADY_EXISTS = §cEs existiert bereits eine Aufgabe für §2%s§c!
|
||||||
|
TASK_CREATION_SUCCESS = §9Generierungs-Aufgabe §2%s§9 §2%s§9 in der Form %s wurde erfolgreich erstellt!
|
||||||
|
TASK_UNIT_WORLDBORDER = bis zur World-Border
|
||||||
|
TASK_UNIT_RADIUS = mit einem Radius von %s
|
||||||
|
TASK_ID_REQUIRED = §cDu musst eine Aufgaben-Id angeben!
|
||||||
|
INVALID_ARGUMENT = §cFalscher Parameter an Stelle %s: %s!
|
||||||
|
|
||||||
|
PAUSED_TASKS_HEADER = §lPausierte Generierungsaufgaben§r
|
||||||
|
|
||||||
|
RUNNING_TASKS_HEADER = §lLaufende Generierungsaufgaben§r
|
||||||
|
NO_GENERATION_TASKS = Es gibt keine Aufgaben.
|
||||||
|
|
||||||
|
PAUSE_SUCCESS = §9Alle Aufgaben wurden pausiert.
|
||||||
|
ALREADY_PAUSED = §cDas Generieren ist bereits pausiert.
|
||||||
|
|
||||||
|
RESUME_SUCCESS = §9Alle Aufgaben wurden fortgesetzt.
|
||||||
|
NOT_PAUSED = §cEs gibt keine pausierten Aufgaben!
|
||||||
|
|
||||||
|
CONFIG_RELOADING = Die Konfigurationsdatei wird neu eingelesen...
|
||||||
|
CONFIG_RELOADED = §2Die Konfigurationsdatei wurde neu geladen!
|
||||||
|
|
||||||
|
TELEPORTED = §9Du wurdest zum Chunk §2%s, %s §9teleportiert
|
||||||
|
TP_ONLY_PLAYER = §cDieser Befehl kann nur von einem Spieler ausgeführt werden.
|
||||||
|
|
||||||
|
NO_PERMISSION = §cDu hast nicht die Rechte für diesen Befehl!
|
||||||
|
SUBCOMMAND_NOT_FOUND = §cUnteraktion §2%s §cwurde nicht gefunden!
|
||||||
|
|
||||||
|
STOPPING_ALL_TASKS = Stoppt alle Aufgaben...
|
||||||
|
SAVING_TASK_PROGRESS = Speichert den Fortschritt der Aufgabe #%s in der Datenbank...
|
||||||
|
DB_INIT = Initialisiere Datenbank...
|
||||||
|
DB_INIT_FINISHED = Die Datenbank wurde initialisiert.
|
||||||
|
DB_INIT_EROR = Fehler beim Initalisieren der Datenbank: %s.
|
||||||
|
|
||||||
|
DATABASE_CONNECTION_ERROR = §cDie Datenbankverbindung konnte nicht erzeugt werden.
|
||||||
|
SQL_ERROR = §cEin Fehler trat mit sql %s auf!
|
||||||
|
NO_DATABASE_CONNECTION = §cSql konnte nicht ausgeführt werden: Keine Datenbankverbindung.
|
||||||
|
CREATE_TABLE_DEFINITION = Tabelle %s mit Definition %s wurde erstellt.
|
||||||
|
TABLE_CREATE_ERROR = §cFehler beim erstellen der Tabelle %s.
|
||||||
|
UPDATE_TABLE_DEFINITION = Tabelle %s wurde mit sql %s geupdated.
|
||||||
|
UPDATE_TABLE_FAILED = Fehler beim Updaten der Tabelle %s mit sql %s.
|
||||||
|
|
||||||
|
TOO_FEW_ARGUMENTS = §cDu hast nicht genug Parameter angegeben.
|
||||||
|
COORD_INVALID = §cDie Koordinate ('%s', '%s') ist ungültig!
|
||||||
|
CENTER_UPDATED = §9Die Mitte der Welt §2%s §9wurde auf §2(%s, %s)§9 gesetzt.
|
||||||
|
CENTER_INFO = §9Die Mitte der Welt §2%s §9ist §2(%s, %s)§9.
|
||||||
|
|
||||||
|
PLUGIN_DETECTED = Plugin %s in der Version %s gefunden!
|
||||||
|
|
||||||
|
RESUME_PLAYER_LEAVE = Die Anzahl der Spieler hat den festgelegen Wert zum Pausieren unterschritten. Setze Generieren fort...
|
||||||
|
PAUSE_PLAYER_JOIN = Die Anzahl der Spieler hat den festgelegten Wert zum Pausieren erreicht. Pausiere...
|
||||||
|
PAUSE_MANUALLY = Das Generieren wurde manuell pausiert und wird deswegen nicht automatisch fortgesetzt.
|
||||||
|
|
||||||
|
STATS_HEADER = §nStatistiken§r
|
||||||
|
STATS_SERVER = §lServer Statistik§r
|
||||||
|
STATS_SERVER_VERSION = - Server Version: §2%s§r
|
||||||
|
STATS_PLUGIN_VERSION = - Plugin Version: §2%s§r
|
||||||
|
STATS_MEMORY = - Arbeitsspeicher (u/a): §2%d mb / %d mb = (%.2f percent)§r
|
||||||
|
STATS_CORES = - Kerne: §2%d§r
|
||||||
|
|
||||||
|
STATS_WORLD_NAME = §l%s§r
|
||||||
|
STATS_ENTITY_COUNT = - §2%d§r Entities
|
||||||
|
STATS_LOADED_CHUNKS = - §2%d§r Geladene Chunks
|
||||||
|
STATS_PLUGIN_LOADED_CHUNKS = - §2%d§r von Chunkmaster geladene Chunks
|
||||||
|
|
||||||
|
SAVING_CHUNKS = Speichere %d geladene Chunks...
|
||||||
|
CANCEL_FAIL = Konnte Aufgabe #%d nicht im angegebenen Timeout stoppen!
|
||||||
|
NO_AUTOSTART = Autostart ist auf §2false§r gesetzt. Pausiere...
|
||||||
|
|
||||||
|
COMPLETED_TASKS_HEADER = §nAbgeschlossene Aufgaben§r
|
||||||
|
COMPLETED_WORLD_HEADER = §l%s§r
|
||||||
|
COMPLETED_TASK_ENTRY = - §9#%d§r: §2%d§r Chunks Radius von der Mitte §2(%d, %d)§r aus in der Form §2%s§r
|
||||||
|
|
||||||
|
STATS_GENERATING = - Generiert: §2%s§r
|
@ -0,0 +1,84 @@
|
|||||||
|
RESUME_FOR_WORLD = Resuming chunk generation task for world '%s'...
|
||||||
|
TASK_FINISHED = Task #%d finished after %d chunks.
|
||||||
|
TASK_CANCELLED = Cancelled task #%s.
|
||||||
|
TASK_LOAD_FAILED = §cFailed to load task #%d.
|
||||||
|
TASK_LOAD_SUCCESS = %d saved tasks loaded.
|
||||||
|
TASK_NOT_FOUND = §cTask %s not found!
|
||||||
|
CREATE_DELAYED_LOAD = Creating task to load chunk generation Tasks later...
|
||||||
|
TASK_PERIODIC_REPORT = Task #%d running for '%s'. State: %s. Progress: %d chunks %s %s, Speed: %.1f ch/s, Last Chunk: %d, %d
|
||||||
|
TASK_SAVE_FAILED = §cException when saving tasks: %s
|
||||||
|
|
||||||
|
WORLD_NAME_REQUIRED = §cYou need to provide a world name!
|
||||||
|
WORLD_NOT_FOUND = §cWorld §2%s §cnot found!
|
||||||
|
TASK_ALREADY_EXISTS = §cA task for '%s' already exists!
|
||||||
|
TASK_CREATION_SUCCESS = §9Generation Task for world §2%s§9 §2%s§9 and shape %s successfully created!
|
||||||
|
TASK_UNIT_WORLDBORDER = until world border
|
||||||
|
TASK_UNIT_RADIUS = with a radius of %s
|
||||||
|
TASK_ID_REQUIRED = §cYou need to provide a task id!
|
||||||
|
INVALID_ARGUMENT = §cInvalid argument at %s: %s!
|
||||||
|
|
||||||
|
PAUSED_TASKS_HEADER = Currently Paused Generation Tasks
|
||||||
|
RUNNING_TASKS_HEADER = Currently Running Generation Tasks
|
||||||
|
NO_GENERATION_TASKS = There are no generation tasks.
|
||||||
|
|
||||||
|
PAUSE_SUCCESS = §9Paused all generation tasks.
|
||||||
|
ALREADY_PAUSED = §cThe generation process is already paused!
|
||||||
|
|
||||||
|
RESUME_SUCCESS = §9Resumed all generation Tasks.
|
||||||
|
NOT_PAUSED = §cThe generation process is not paused!
|
||||||
|
|
||||||
|
CONFIG_RELOADING = Reloading the config file...
|
||||||
|
CONFIG_RELOADED = §2The config file has been reloaded!
|
||||||
|
|
||||||
|
TELEPORTED = §9You have been teleported to chunk §2%s, %s
|
||||||
|
TP_ONLY_PLAYER = §cThis command can only be executed by a player!
|
||||||
|
|
||||||
|
NO_PERMISSION = §cYou do not have the permission for this command!
|
||||||
|
SUBCOMMAND_NOT_FOUND = §cSubcommand §2%s §cnot found!
|
||||||
|
|
||||||
|
STOPPING_ALL_TASKS = Stopping all generation tasks...
|
||||||
|
SAVING_TASK_PROGRESS = Saving the progress of task #%s to the database...
|
||||||
|
DB_INIT = Initializing database...
|
||||||
|
DB_INIT_FINISHED = Database fully initialized.
|
||||||
|
DB_INIT_EROR = Failed to init database: %s.
|
||||||
|
|
||||||
|
DATABASE_CONNECTION_ERROR = §cCould not get the database connection!
|
||||||
|
SQL_ERROR = §cAn eror occured on sql %s!
|
||||||
|
NO_DATABASE_CONNECTION = §cCould not execute sql: No database connection.
|
||||||
|
CREATE_TABLE_DEFINITION = Created table %s with definition %s.
|
||||||
|
TABLE_CREATE_ERROR = §cError when creation table %s.
|
||||||
|
UPDATE_TABLE_DEFINITION = Updated table %s with sql %s.
|
||||||
|
UPDATE_TABLE_FAILED = Failed to update table %s with sql %s.
|
||||||
|
|
||||||
|
TOO_FEW_ARGUMENTS = §cYou did not provide enough arguments.
|
||||||
|
COORD_INVALID = §cThe provided coordinate ('%s', '%s') is invalid!
|
||||||
|
CENTER_UPDATED = §9The center for world §2%s §9has been set to §2(%s, %s)§9.
|
||||||
|
CENTER_INFO = §9The center for world §2%s §9is §2(%s, %s)§9.
|
||||||
|
|
||||||
|
PLUGIN_DETECTED = Detected %s version %s
|
||||||
|
|
||||||
|
RESUME_PLAYER_LEAVE = The player count is smaller than the configured pause value. Resuming generation...
|
||||||
|
PAUSE_PLAYER_JOIN = The player count has reached the pause value. Pausing generation...
|
||||||
|
PAUSE_MANUALLY = Generation was manually paused. Not resuming automatically.
|
||||||
|
|
||||||
|
STATS_HEADER = §nStats§r
|
||||||
|
STATS_SERVER = §lServer Stats§r
|
||||||
|
STATS_SERVER_VERSION = - Server Version: §2%s§r
|
||||||
|
STATS_PLUGIN_VERSION = - Plugin Version: §2%s§r
|
||||||
|
STATS_MEMORY = - Memory (u/a): §2%d mb / %d mb = (%.2f percent)§r
|
||||||
|
STATS_CORES = - Cores: §2%d§r
|
||||||
|
|
||||||
|
STATS_WORLD_NAME = §l%s§r
|
||||||
|
STATS_ENTITY_COUNT = - §2%d§r Entities
|
||||||
|
STATS_LOADED_CHUNKS = - §2%d§r Loaded Chunks
|
||||||
|
STATS_PLUGIN_LOADED_CHUNKS = - §2%d§r Chunks Loaded by Chunkmaster
|
||||||
|
|
||||||
|
SAVING_CHUNKS = Saving %d loaded chunks...
|
||||||
|
CANCEL_FAIL = Failed to cancel task #%d in the given timeout!
|
||||||
|
NO_AUTOSTART = Autostart set to §2false§r. Pausing...
|
||||||
|
|
||||||
|
COMPLETED_TASKS_HEADER = §nCompleted Generation Tasks§r
|
||||||
|
COMPLETED_WORLD_HEADER = §l%s§r
|
||||||
|
COMPLETED_TASK_ENTRY = - §9#%d§r: §2%d§r chunks radius from center §2(%d, %d)§r with shape §2%s§r
|
||||||
|
|
||||||
|
STATS_GENERATING = - Generating: §2%s§r
|
@ -0,0 +1,85 @@
|
|||||||
|
RESUME_FOR_WORLD = Resumiendo tarea de generación de chunks para el mundo '%s'...
|
||||||
|
TASK_FINISHED = Tarea #%d terminada después de %d chunks.
|
||||||
|
TASK_CANCELLED = Tarea #%s cancelada.
|
||||||
|
TASK_LOAD_FAILED = §cFallo al cargar la tarea #%d.
|
||||||
|
TASK_LOAD_SUCCESS = %d tareas guardadas cargadas.
|
||||||
|
TASK_NOT_FOUND = §c¡Tarea %s no encontrada!
|
||||||
|
CREATE_DELAYED_LOAD = Creando tarea Bukkit para cargar la generación de chunks...
|
||||||
|
TASK_PERIODIC_REPORT = Tarea #%d en ejecución para '%s'. Estado: %s. Progreso: %d Chunks %s %s, Velocidad: %.1f ch/s, Último Chunk: %d, %d
|
||||||
|
TASK_PERIODIC_REPORT_CORRECTING = Tarea #%d gerenando chunks perdidos para '%s'. Progreso: %d Chunks %s
|
||||||
|
TASK_SAVE_FAILED = §cExcepción al guardar las tareas: %s
|
||||||
|
|
||||||
|
WORLD_NAME_REQUIRED = §c¡Tienes que proporcionar un nombre del mundo!
|
||||||
|
WORLD_NOT_FOUND = §c¡Mundo §2%s §cno encontrado!
|
||||||
|
TASK_ALREADY_EXISTS = §c¡Una tarea para '%s' ya existe!
|
||||||
|
TASK_CREATION_SUCCESS = §9¡Tarea de generación para el mundo §2%s§9 §2%s§9 y forma %s creada con éxito!
|
||||||
|
TASK_UNIT_WORLDBORDER = hasta el borde del mundo
|
||||||
|
TASK_UNIT_RADIUS = con un radius de %s
|
||||||
|
TASK_ID_REQUIRED = §c¡Necesitas proporcionar una id de tarea!
|
||||||
|
INVALID_ARGUMENT = §c¡Argumento invalido en %s: %s!
|
||||||
|
|
||||||
|
PAUSED_TASKS_HEADER = Tareas de generación actualmente en pausa
|
||||||
|
TASKS_ENTRY = - §9#%d§r - §2%s§r - §2%s§r - §2%s Chunks %s§r
|
||||||
|
RUNNING_TASKS_HEADER = Tareas de generación en curso
|
||||||
|
NO_GENERATION_TASKS = No hay tareas de generación.
|
||||||
|
|
||||||
|
COMPLETED_TASKS_HEADER = §nTareas de generación completadas§r
|
||||||
|
COMPLETED_WORLD_HEADER = §l%s§r
|
||||||
|
COMPLETED_TASK_ENTRY = - §9#%d§r: §2%d§r radio de chunks desde el centro §2(%d, %d)§r con forma §2%s§r
|
||||||
|
|
||||||
|
PAUSE_SUCCESS = §9Tareas de generación pausadas
|
||||||
|
ALREADY_PAUSED = §cEl progreso de generación ya está en pausa.
|
||||||
|
|
||||||
|
RESUME_SUCCESS = §9Tareas de generación renaudadas
|
||||||
|
NOT_PAUSED = §cEl progreso de generación no está en pausa.
|
||||||
|
|
||||||
|
CONFIG_RELOADING = Recargando el archivo de configuración...
|
||||||
|
CONFIG_RELOADED = §2El archivo de configuración ha sido recargado!
|
||||||
|
|
||||||
|
TELEPORTED = §9Has sido teletransportado al chunk §2%s, %s
|
||||||
|
TP_ONLY_PLAYER = §c¡Este comando solo puede ser ejecutado por un jugador!
|
||||||
|
|
||||||
|
NO_PERMISSION = §c¡No tienes permiso para este comando!
|
||||||
|
SUBCOMMAND_NOT_FOUND = §c¡Subcomando §2%s §cno encontrado!
|
||||||
|
|
||||||
|
STOPPING_ALL_TASKS = Deteniendo todas las tareas de generación...
|
||||||
|
SAVING_TASK_PROGRESS = Guardando el progreso de la tarea #%s en la base de datos...
|
||||||
|
DB_INIT = Iniciando la base de datos...
|
||||||
|
DB_INIT_FINISHED = Base de datos ha sido inicializada.
|
||||||
|
DB_INIT_EROR = Error al inicializar la base de datos: %s.
|
||||||
|
|
||||||
|
DATABASE_CONNECTION_ERROR = §c¡No se ha podido establecer la conexión con la base de datos!
|
||||||
|
SQL_ERROR = §cSe ha producido un error en sql %s!
|
||||||
|
NO_DATABASE_CONNECTION = §cNo se ha podido ejecutar sql: No hay conexión con la base de datos.
|
||||||
|
CREATE_TABLE_DEFINITION = Tabla %s creada con definición %s.
|
||||||
|
TABLE_CREATE_ERROR = §cSe ha producido un error al crear la tabla %s.
|
||||||
|
UPDATE_TABLE_DEFINITION = Tabla %s actualizada con sql %s.
|
||||||
|
UPDATE_TABLE_FAILED = Se ha producido un error al actualizar la tabla %s con sql %s.
|
||||||
|
|
||||||
|
TOO_FEW_ARGUMENTS = §cNo has aportado suficientes argumentos.
|
||||||
|
COORD_INVALID = §cLas coordenadas proporcionadas ('%s', '%s') son incorrectas!
|
||||||
|
CENTER_UPDATED = §9El centro del mundo §2%s §9se ha actualizado a §2(%s, %s)§9.
|
||||||
|
CENTER_INFO = §9El centro del mundo §2%s §9es §2(%s, %s)§9.
|
||||||
|
|
||||||
|
PLUGIN_DETECTED = ¡Plugin %s con versión %s detectada!
|
||||||
|
|
||||||
|
RESUME_PLAYER_LEAVE = El número de jugadores es menor que el configurado como valor de pausa. Reanudando generación...
|
||||||
|
PAUSE_PLAYER_JOIN = El número de jugadores es igual que el configurado como valor de pausa. Pausando generación...
|
||||||
|
PAUSE_MANUALLY = Generación se pausó manualmente. No se resumirá automáticamente.
|
||||||
|
|
||||||
|
STATS_HEADER = §nEstadísticas§r
|
||||||
|
STATS_SERVER = §lEstadísticas del Servidor§r
|
||||||
|
STATS_SERVER_VERSION = - Versión del Servidor: §2%s§r
|
||||||
|
STATS_PLUGIN_VERSION = - Versión del Plugin: §2%s§r
|
||||||
|
STATS_MEMORY = - Memoria (u/a): §2%d mb / %d mb = (%.2f porcentaje)§r
|
||||||
|
STATS_CORES = - Cores: §2%d§r
|
||||||
|
|
||||||
|
STATS_WORLD_NAME = §l%s§r
|
||||||
|
STATS_ENTITY_COUNT = - §2%d§r Entidades
|
||||||
|
STATS_LOADED_CHUNKS = - §2%d§r Chunks cargados
|
||||||
|
STATS_PLUGIN_LOADED_CHUNKS = - §2%d§r Chunks cargados por Chunkmaster
|
||||||
|
STATS_GENERATING = - Generando: §2%s§r
|
||||||
|
|
||||||
|
SAVING_CHUNKS = Guardando %d chunks guardados...
|
||||||
|
CANCEL_FAIL = Fallo en la cancelación de la tarea #%d en el tiempo de espera configurado!
|
||||||
|
NO_AUTOSTART = Autoarranque configurado a §2false§r. Pausando...
|
@ -0,0 +1,75 @@
|
|||||||
|
RESUME_FOR_WORLD = Reprise de la tâche de génération pour le monde '%s'...
|
||||||
|
TASK_FINISHED = Tâche #%d terminée après %d chunks.
|
||||||
|
TASK_CANCELED = Tâche #%s annulée.
|
||||||
|
TASK_LOAD_FAILED = §cImpossible de charger la tâche #%d.
|
||||||
|
TASK_LOAD_SUCCESS = %d tâches chargées avec succès.
|
||||||
|
TASK_NOT_FOUND = §cTâche %s introuvable!
|
||||||
|
CREATE_DELAYED_LOAD = Création de la tâche pour la génération du terrain plus tard...
|
||||||
|
TASK_PERIODIC_REPORT = Tâche #%d en cours pour '%s'. État: %s. Progression: %d chunks %s %s, Vitesse: %.1f ch/s, Dernier Chunk: %d, %d
|
||||||
|
TASK_SAVE_FAILED = §cErreur lors de la sauvegarde de la tâche: %s
|
||||||
|
|
||||||
|
WORLD_NAME_REQUIRED = §cVous devez renseigner le nom d'un monde!
|
||||||
|
WORLD_NOT_FOUND = §cMonde §2%s §cintrouvable!
|
||||||
|
TASK_ALREADY_EXISTS = §cUne tâche pour '%s' existe déjà!
|
||||||
|
TASK_CREATION_SUCCESS = §9Tâche de génération pour le monde §2%s§9 §2%s§9 avec une forme %s créée avec succès!
|
||||||
|
TASK_UNIT_WORLDBORDER = jusqu'à la limite du monde
|
||||||
|
TASK_UNIT_RADIUS = avec un rayon de %s
|
||||||
|
TASK_ID_REQUIRED = §cVous devez renseigner l'id de la tache!
|
||||||
|
INVALID_ARGUMENT = §cArgument invalide à %s: %s!
|
||||||
|
|
||||||
|
PAUSED_TASKS_HEADER = Tâches de génération en pause
|
||||||
|
TASKS_ENTRY = - §9#%d§r - §2%s§r - §2%s§r - §2%s chunks %s§r
|
||||||
|
RUNNING_TASKS_HEADER = Tâches de génération en cours
|
||||||
|
NO_GENERATION_TASKS = Il n'y a aucune tâche de génération.
|
||||||
|
|
||||||
|
PAUSE_SUCCESS = §9Mise en pause de toutes les tâches de génération.
|
||||||
|
ALREADY_PAUSED = §cLa génération est déjà en pause!
|
||||||
|
|
||||||
|
RESUME_SUCCESS = §9Reprise de toutes les tâches de génération.
|
||||||
|
NOT_PAUSED = §cLa génération n'est pas en pause!
|
||||||
|
|
||||||
|
CONFIG_RELOADING = Rechargement du fichier de configuration...
|
||||||
|
CONFIG_RELOADED = §2La configuration a été rechargé!
|
||||||
|
|
||||||
|
TELEPORTED = §9Vous avez été téléporté au chunk §2%s, %s
|
||||||
|
TP_ONLY_PLAYER = §cCette commande ne peut être exécutée que par un joueur!
|
||||||
|
|
||||||
|
NO_PERMISSION = §cVous n'avez pas la permission d'exécuter cette commande!
|
||||||
|
SUBCOMMAND_NOT_FOUND = §cSSous-commande §2%s §cintrouvable!
|
||||||
|
|
||||||
|
STOPPING_ALL_TASKS = Arrêts de toutes les tâches de génération...
|
||||||
|
SAVING_TASK_PROGRESS = Sauvegarde de la progression de la tâche #%s dans la base de données...
|
||||||
|
DB_INIT = Initialisation de la base de données...
|
||||||
|
DB_INIT_FINISHED = Base de données initialisée.
|
||||||
|
DB_INIT_EROR = Erreur lors de l'initialisation de la base de données: %s.
|
||||||
|
|
||||||
|
DATABASE_CONNECTION_ERROR = §cConnexion impossible à la base de données!
|
||||||
|
SQL_ERROR = §cErreur lors de l'exécution SQL %s!
|
||||||
|
NO_DATABASE_CONNECTION = §cImpossible d'exécuter une requête SQL: Pas de connexion à la base de données.
|
||||||
|
CREATE_TABLE_DEFINITION = Table créée %s avec pour définition %s.
|
||||||
|
TABLE_CREATE_ERROR = §cErreur lors de la création de la table %s.
|
||||||
|
UPDATE_TABLE_DEFINITION = Mise à jour de la table %s par la requête SQL %s.
|
||||||
|
UPDATE_TABLE_FAILED = Impossible de mettre à jour la table %s par la requête SQL %s.
|
||||||
|
|
||||||
|
TOO_FEW_ARGUMENTS = §cIl manques des arguments pour exécuter la commande.
|
||||||
|
COORD_INVALID = §cLes coordonnées renseignées ('%s', '%s') sont invalides!
|
||||||
|
CENTER_UPDATED = §9Le centre du monde §2%s §9a été défini en §2(%s, %s)§9.
|
||||||
|
CENTER_INFO = §9Le centre du monde §2%s §9est §2(%s, %s)§9.
|
||||||
|
|
||||||
|
PLUGIN_DETECTED = Version de %s détectée %s
|
||||||
|
|
||||||
|
RESUME_PLAYER_LEAVE = Le nombre de joueurs est plus bas que la limite configurée. Reprise de la génération...
|
||||||
|
PAUSE_PLAYER_JOIN = Le nombre de joueurs a atteint la limite configurée. Arrêt de la génération...
|
||||||
|
PAUSE_MANUALLY = La génération a été manuellement arrêtée. Celle-ci ne reprendra pas automatiquement.
|
||||||
|
|
||||||
|
STATS_HEADER = §nStatistiques§r
|
||||||
|
STATS_SERVER = §lStatistiques serveur§r
|
||||||
|
STATS_SERVER_VERSION = - Version du serveur: §2%s§r
|
||||||
|
STATS_PLUGIN_VERSION = - Version du plugin: §2%s§r
|
||||||
|
STATS_MEMORY = - Mémoire vive (u/a): §2%d mb / %d mb = (%.2f pourcent)§r
|
||||||
|
STATS_CORES = - Coeurs processeur: §2%d§r
|
||||||
|
|
||||||
|
STATS_WORLD_NAME = §l%s§r
|
||||||
|
STATS_ENTITY_COUNT = - §2%d§r Entités
|
||||||
|
STATS_LOADED_CHUNKS = - §2%d§r Chunks chargés
|
||||||
|
STATS_PLUGIN_LOADED_CHUNKS = - §2%d§r Chunks chargés par Chunkmaster
|
@ -1,58 +1,83 @@
|
|||||||
main: net.trivernis.chunkmaster.Chunkmaster
|
main: net.trivernis.chunkmaster.Chunkmaster
|
||||||
name: Chunkmaster
|
name: Chunkmaster
|
||||||
version: '0.12-beta'
|
version: $$PLUGIN_VERSION$$
|
||||||
description: Chunk commands plugin.
|
description: Automated world pregeneration.
|
||||||
author: Trivernis
|
author: Trivernis
|
||||||
website: trivernis.net
|
website: trivernis.net
|
||||||
api-version: '1.14'
|
api-version: '1.14'
|
||||||
commands:
|
database: true
|
||||||
chunkmaster:
|
softdepend:
|
||||||
description: Main command
|
- dynmap
|
||||||
permission: chunkmaster.chunkmaster
|
commands:
|
||||||
usage: |
|
chunkmaster:
|
||||||
/<command> generate [<world>, <chunk-count>] - generates chunks starting from the spawn until the chunk-count is reached
|
description: Main command
|
||||||
/<command> cancel <task-id> - cancels the generation task with the task-id
|
permission: chunkmaster.chunkmaster
|
||||||
/<command> list - lists all running and paused generation tasks
|
usage: |
|
||||||
/<command> pause - pauses all generation tasks
|
/<command> generate [<world>] <radius> [<shape>] - generates chunks starting from the spawn until the chunk-count is reached
|
||||||
/<command> resume - resumes all generation tasks
|
/<command> cancel <task-id|world> - cancels the generation task with the task-id
|
||||||
/<command> reload - reloads the configuration and restarts all tasks
|
/<command> list - lists all running and paused generation tasks
|
||||||
/<command> tpchunk <chunkX> <chunkZ> - teleports you to the chunk with the given chunk coordinates
|
/<command> pause - pauses all generation tasks
|
||||||
aliases:
|
/<command> resume - resumes all generation tasks
|
||||||
- chm
|
/<command> reload - reloads the configuration and restarts all tasks
|
||||||
- chunkm
|
/<command> tpchunk <chunkX> <chunkZ> - teleports you to the chunk with the given chunk coordinates
|
||||||
- cmaster
|
/<command> setCenter [[<world>] <chunkX> <chunkZ>]] - sets the center chunk of the world
|
||||||
permissions:
|
/<command> getCenter [<world>] - returns the center chunk of the world
|
||||||
cunkmaster.generate:
|
/<command> stats [<world>] - returns some chunk stats for the world or the whole server
|
||||||
description: Allows the generate subcommand.
|
/<command> completed - lists all completed tasks for all worlds
|
||||||
default: op
|
aliases:
|
||||||
chunkmaster.list:
|
- chm
|
||||||
description: Allows the list subcommand.
|
- chunkm
|
||||||
default: op
|
- cmaster
|
||||||
chunkmaster.cancel:
|
permissions:
|
||||||
description: Allows the remove subcommand.
|
chunkmaster.generate:
|
||||||
default: op
|
description: Allows the generate subcommand.
|
||||||
chunkmaster.pause:
|
default: op
|
||||||
description: Allows the pause subcommand.
|
chunkmaster.list:
|
||||||
default: op
|
description: Allows the list subcommand.
|
||||||
chunkmaster.resume:
|
default: op
|
||||||
description: Allows the resume subcommand.
|
chunkmaster.cancel:
|
||||||
default: op
|
description: Allows the cancel subcommand.
|
||||||
chunkmaster.reload:
|
default: op
|
||||||
description: Allows the reload subcommand.
|
chunkmaster.pause:
|
||||||
default: op
|
description: Allows the pause subcommand.
|
||||||
chunkmaster.tpchunk:
|
default: op
|
||||||
description: Allows the tpchunk subcommand.
|
chunkmaster.resume:
|
||||||
default: op
|
description: Allows the resume subcommand.
|
||||||
chunkmaster.chunkmaster:
|
default: op
|
||||||
description: Allows Chunkmaster commands.
|
chunkmaster.reload:
|
||||||
default: op
|
description: Allows the reload subcommand.
|
||||||
chunkmaster.*:
|
default: op
|
||||||
description: Wildcard permission
|
chunkmaster.tpchunk:
|
||||||
default: op
|
description: Allows the tpchunk subcommand.
|
||||||
children:
|
default: op
|
||||||
- chunkmaster.generate
|
chunkmaster.setcenter:
|
||||||
- chunkmaster.listgentasks
|
description: Allows the setCenter subcommand.
|
||||||
- chunkmaster.removegentask
|
default: op
|
||||||
- chunkmaster.pausegentasks
|
chunkmaster.getcenter:
|
||||||
- chunkmaster.resumegentasks
|
description: Allows the getCenter subcommand.
|
||||||
|
default: op
|
||||||
|
chunkmaster.stats:
|
||||||
|
description: Allows the stats subcommand.
|
||||||
|
deault: op
|
||||||
|
chunkmaster.completed:
|
||||||
|
description: Allows the completed subcommand.
|
||||||
|
default: op
|
||||||
|
chunkmaster.chunkmaster:
|
||||||
|
description: Allows Chunkmaster commands.
|
||||||
|
default: op
|
||||||
|
chunkmaster.*:
|
||||||
|
description: Wildcard permission
|
||||||
|
default: op
|
||||||
|
children:
|
||||||
|
- chunkmaster.generate
|
||||||
|
- chunkmaster.list
|
||||||
|
- chunkmaster.cancel
|
||||||
|
- chunkmaster.pause
|
||||||
|
- chunkmaster.resume
|
||||||
|
- chunkmaster.completed
|
||||||
|
- chunkmaster.tpchunk
|
||||||
|
- chunkmaster.reload
|
||||||
|
- chunkmaster.setcenter
|
||||||
|
- chunkmaster.getcenter
|
||||||
|
- chunkmaster.stats
|
||||||
- chunkmaster.chunkmaster
|
- chunkmaster.chunkmaster
|
@ -0,0 +1,60 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib
|
||||||
|
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class ArgParserTest {
|
||||||
|
var argParser = ArgParser()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it parses arguments`() {
|
||||||
|
argParser.parseArguments("first second third forth").shouldBe(listOf("first", "second", "third", "forth"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it handles escaped sequences`() {
|
||||||
|
argParser.parseArguments("first second\\ pt2 third").shouldBe(listOf("first", "second pt2", "third"))
|
||||||
|
argParser.parseArguments("first \"second\\\" part 2\" third")
|
||||||
|
.shouldBe(listOf("first", "second\" part 2", "third"))
|
||||||
|
argParser.parseArguments("first \\\\second third").shouldBe(listOf("first", "\\second", "third"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it parses quoted arguments as one argument`() {
|
||||||
|
argParser.parseArguments("first \"second with space\" third")
|
||||||
|
.shouldBe(listOf("first", "second with space", "third"))
|
||||||
|
argParser.parseArguments("\"first\" \"second\" \"third\"").shouldBe(listOf("first", "second", "third"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it parses single arguments`() {
|
||||||
|
argParser.parseArguments("one").shouldBe(listOf("one"))
|
||||||
|
argParser.parseArguments("\"one\"").shouldBe(listOf("one"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it parses no arguments`() {
|
||||||
|
argParser.parseArguments("").shouldBe(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it parses just whitespace as no arguments`() {
|
||||||
|
argParser.parseArguments(" ").shouldBe(emptyList())
|
||||||
|
argParser.parseArguments("\t\t").shouldBe(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it parses arguments with weird whitespace`() {
|
||||||
|
argParser.parseArguments(" first second \t third \n forth ")
|
||||||
|
.shouldBe(listOf("first", "second", "third", "forth"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it deals predictable with malformed input`() {
|
||||||
|
argParser.parseArguments("first \"second third fourth").shouldBe(listOf("first", "second third fourth"))
|
||||||
|
argParser.parseArguments("\"first second \"third\" fourth")
|
||||||
|
.shouldBe(listOf("first second ", "third", " fourth"))
|
||||||
|
argParser.parseArguments("first second third fourth\"").shouldBe(listOf("first", "second", "third", "fourth"))
|
||||||
|
argParser.parseArguments("\"").shouldBe(emptyList())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib
|
||||||
|
|
||||||
|
import io.kotest.matchers.string.shouldNotBeEmpty
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class LanguageManagerTest {
|
||||||
|
private var langManager: LanguageManager
|
||||||
|
|
||||||
|
init {
|
||||||
|
val plugin = mockk<Chunkmaster>()
|
||||||
|
val config = mockk<FileConfiguration>()
|
||||||
|
|
||||||
|
every { plugin.dataFolder } returns createTempDir()
|
||||||
|
every { plugin.config } returns config
|
||||||
|
every { config.getString("language") } returns "en"
|
||||||
|
|
||||||
|
langManager = LanguageManager(plugin)
|
||||||
|
langManager.loadProperties()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it returns localized for a key`() {
|
||||||
|
langManager.getLocalized("NOT_PAUSED").shouldNotBeEmpty()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.shapes
|
||||||
|
|
||||||
|
import io.kotest.matchers.booleans.shouldBeTrue
|
||||||
|
import io.kotest.matchers.collections.shouldContainAll
|
||||||
|
import io.kotest.matchers.doubles.shouldBeBetween
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
|
||||||
|
class CircleTest {
|
||||||
|
private val circle = Circle(center = Pair(0, 0), radius = 2, start = Pair(0, 0))
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun init() {
|
||||||
|
circle.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it generates coordinates`() {
|
||||||
|
circle.next().shouldBe(Pair(0, 0))
|
||||||
|
circle.next().shouldBe(Pair(-1, -1))
|
||||||
|
circle.next().shouldBe(Pair(1, 0))
|
||||||
|
circle.next().shouldBe(Pair(-1, 0))
|
||||||
|
circle.next().shouldBe(Pair(1, -1))
|
||||||
|
circle.next().shouldBe(Pair(-1, 1))
|
||||||
|
circle.next().shouldBe(Pair(0, 1))
|
||||||
|
circle.next().shouldBe(Pair(0, -1))
|
||||||
|
circle.next().shouldBe(Pair(1, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it reports when reaching the end`() {
|
||||||
|
for (i in 1..25) {
|
||||||
|
circle.next()
|
||||||
|
}
|
||||||
|
circle.endReached().shouldBeTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it reports the radius`() {
|
||||||
|
for (i in 1..9) {
|
||||||
|
circle.next()
|
||||||
|
}
|
||||||
|
circle.currentRadius().shouldBe(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it returns the right edges`() {
|
||||||
|
circle.getShapeEdgeLocations().shouldContainAll(
|
||||||
|
listOf(
|
||||||
|
Pair(2, -1),
|
||||||
|
Pair(2, 0),
|
||||||
|
Pair(2, 1),
|
||||||
|
Pair(1, 2),
|
||||||
|
Pair(0, 2),
|
||||||
|
Pair(-1, 2),
|
||||||
|
Pair(-2, 1),
|
||||||
|
Pair(-2, 0),
|
||||||
|
Pair(-2, -1),
|
||||||
|
Pair(-1, -2),
|
||||||
|
Pair(0, -2),
|
||||||
|
Pair(1, -2),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it returns the progress`() {
|
||||||
|
circle.progress(2).shouldBe(0)
|
||||||
|
for (i in 1..7) {
|
||||||
|
circle.next()
|
||||||
|
}
|
||||||
|
circle.progress(2).shouldBeBetween(.5, .8, .0)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.shapes
|
||||||
|
|
||||||
|
import io.kotest.matchers.booleans.shouldBeTrue
|
||||||
|
import io.kotest.matchers.collections.shouldContainAll
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
|
||||||
|
class SquareTest {
|
||||||
|
|
||||||
|
private val square = Square(center = Pair(0, 0), radius = 2, start = Pair(0, 0))
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun init() {
|
||||||
|
square.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it generates coordinates`() {
|
||||||
|
square.next().shouldBe(Pair(0, 0))
|
||||||
|
square.next().shouldBe(Pair(0, 1))
|
||||||
|
square.next().shouldBe(Pair(1, 1))
|
||||||
|
square.next().shouldBe(Pair(1, 0))
|
||||||
|
square.next().shouldBe(Pair(1, -1))
|
||||||
|
square.next().shouldBe(Pair(0, -1))
|
||||||
|
square.next().shouldBe(Pair(-1, -1))
|
||||||
|
square.next().shouldBe(Pair(-1, 0))
|
||||||
|
square.next().shouldBe(Pair(-1, 1))
|
||||||
|
square.next().shouldBe(Pair(-1, 2))
|
||||||
|
square.next().shouldBe(Pair(0, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it reports when reaching the end`() {
|
||||||
|
for (i in 1..25) {
|
||||||
|
square.next()
|
||||||
|
}
|
||||||
|
square.endReached().shouldBeTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it reports the radius`() {
|
||||||
|
for (i in 1..9) {
|
||||||
|
square.next()
|
||||||
|
}
|
||||||
|
square.currentRadius().shouldBe(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it returns the right edges`() {
|
||||||
|
square.getShapeEdgeLocations().shouldContainAll(listOf(Pair(2, 2), Pair(-2, 2), Pair(2, -2), Pair(-2, -2)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it returns the progress`() {
|
||||||
|
square.progress(2).shouldBe(0)
|
||||||
|
for (i in 1..8) {
|
||||||
|
square.next()
|
||||||
|
}
|
||||||
|
square.progress(2).shouldBe(0.5)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue