mirror of
https://github.com/BentoBoxWorld/Limits.git
synced 2024-11-16 15:45:20 +01:00
Compare commits
248 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
52c12e2a7d | ||
|
deda533db1 | ||
|
1d73ce8a23 | ||
|
d1030ca172 | ||
|
59ea22ce47 | ||
|
a72acac16f | ||
|
2d6d768b14 | ||
|
1bca34560f | ||
|
ae13fb2cf5 | ||
|
c880bb300e | ||
|
3c2500c125 | ||
|
abcfa539fc | ||
|
6c8de4424a | ||
|
463f28478f | ||
|
28576ed2fd | ||
|
5199505f96 | ||
|
9651c5dac9 | ||
|
74a9223a52 | ||
|
ab1e5fe23b | ||
|
7144683661 | ||
|
ecc017318d | ||
|
ddad296227 | ||
|
5d0420e0b5 | ||
|
fcea94d09c | ||
|
f805c98b26 | ||
|
69c5a852ea | ||
|
ecb5b3bfe3 | ||
|
c0eae7f986 | ||
|
412553b408 | ||
|
266823f9e7 | ||
|
8e601a1357 | ||
|
1430abafc0 | ||
|
fc4c652d5b | ||
|
a63d980580 | ||
|
5d908dff37 | ||
|
c03583de5f | ||
|
c7be909aed | ||
|
701e758a75 | ||
|
c1c656739f | ||
|
5b74acb323 | ||
|
68cc0b8436 | ||
|
cf9159b308 | ||
|
09f25d4811 | ||
|
574af3294e | ||
|
ac42be3e06 | ||
|
70d7c0e180 | ||
|
ba442a789f | ||
|
88c8343036 | ||
|
66cab9e74d | ||
|
53167b4fa0 | ||
|
9d1933ed4c | ||
|
dd436834b1 | ||
|
e9b66c16f1 | ||
|
2daff776ff | ||
|
3123e8d146 | ||
|
31ea783858 | ||
|
12b1e264d3 | ||
|
35988f680a | ||
|
79573eae88 | ||
|
e43d85d487 | ||
|
b02d3e42c4 | ||
|
22d25c0181 | ||
|
ec7f0e4314 | ||
|
30c25ee3bc | ||
|
11f3aa500c | ||
|
95c3e82fdb | ||
|
e472fddec2 | ||
|
a167bc220b | ||
|
fba0981d61 | ||
|
592f0413b3 | ||
|
78c07f0101 | ||
|
37e4c10964 | ||
|
2bb48e0139 | ||
|
9f4c9315d6 | ||
|
79bf0f00c3 | ||
|
5f0102ee10 | ||
|
e317f27869 | ||
|
923ce3ad17 | ||
|
eb5ceba4c7 | ||
|
902b597b11 | ||
|
50347bb922 | ||
|
f08c45822d | ||
|
6b9262efc7 | ||
|
e2670a49e0 | ||
|
40fdf283b1 | ||
|
6dedd50db5 | ||
|
19373fa9b3 | ||
|
674614eb22 | ||
|
c0d965daa6 | ||
|
2ea2c4526e | ||
|
0db1cfc2b8 | ||
|
397e6d281e | ||
|
89af2ae996 | ||
|
3a42692909 | ||
|
894fc1347c | ||
|
e53f11ea19 | ||
|
f408d9c00b | ||
|
f9ce3ede64 | ||
|
2f01344068 | ||
|
7da342bec5 | ||
|
65c7e6e8d0 | ||
|
ee577e210a | ||
|
9ffe0c740a | ||
|
843aa89f6c | ||
|
39c630b92a | ||
|
4323a1a53a | ||
|
2b9b3dacf8 | ||
|
2d92663cd4 | ||
|
a35816cc80 | ||
|
9763dc1227 | ||
|
f6b3e3b8db | ||
|
5137c24c91 | ||
|
e16d1f1893 | ||
|
6b770d083e | ||
|
eeafa239c4 | ||
|
68f696a46c | ||
|
995ac6191f | ||
|
d87d194c65 | ||
|
8dd35fabcf | ||
|
e17245b86f | ||
|
f0727586a3 | ||
|
69ded8fc3e | ||
|
9e7a52c4d9 | ||
|
3b6e912bae | ||
|
97006d28c1 | ||
|
989ebcba7b | ||
|
a1c75e1192 | ||
|
fdd827a297 | ||
|
bc905adccb | ||
|
ed2af07f07 | ||
|
f726492eae | ||
|
26d357fd42 | ||
|
cf5be3ef30 | ||
|
be3554bf8c | ||
|
782150f0f4 | ||
|
388f973aee | ||
|
5fd461ddea | ||
|
b459f8bdb5 | ||
|
baa73b76a5 | ||
|
d7bff1df7d | ||
|
1a7d2b8f3e | ||
|
e8dffc76f5 | ||
|
fe0e935e14 | ||
|
360ba0b2de | ||
|
d038269f8c | ||
|
78e02b6196 | ||
|
7582aa93d7 | ||
|
886c6c5fa5 | ||
|
8d56a4078a | ||
|
6e9d83c348 | ||
|
f635c07ead | ||
|
d8c8102010 | ||
|
343d7bbbde | ||
|
4cff598aee | ||
|
51408751a6 | ||
|
0248c398f6 | ||
|
38aa99b2c7 | ||
|
d6f12c29dc | ||
|
3f1465d6ef | ||
|
14c38869df | ||
|
328a1a13f7 | ||
|
945aae0d8b | ||
|
b71700895e | ||
|
390814bedb | ||
|
05a8a0ebbe | ||
|
76c582ba1d | ||
|
ad1f060d76 | ||
|
be2c6480b1 | ||
|
db233b5ec5 | ||
|
f7c44c7c21 | ||
|
ebb3acd599 | ||
|
f94a4ac0bb | ||
|
0a193ebd27 | ||
|
21986d3f2d | ||
|
f7d379f42d | ||
|
4c5fdd6390 | ||
|
b16332da30 | ||
|
d0d950818d | ||
|
62caf1aa47 | ||
|
7a04c8af41 | ||
|
c7b310ffd8 | ||
|
0992a4fc52 | ||
|
6a2768e572 | ||
|
0ae58db8a9 | ||
|
f525ae80a5 | ||
|
e7d6d799f1 | ||
|
d3bd125603 | ||
|
dc4efb2a0c | ||
|
a730daf6a8 | ||
|
5e9a4326ba | ||
|
f353de9cc8 | ||
|
c286a65803 | ||
|
8ce7c3e631 | ||
|
514f2158cc | ||
|
4f994b3883 | ||
|
368443a413 | ||
|
90b5a65c62 | ||
|
fbf13bd99e | ||
|
dee2367ad6 | ||
|
81e4dbefc5 | ||
|
19565e90ff | ||
|
c0c9e16b6c | ||
|
b9870a4fef | ||
|
0f7f61c179 | ||
|
88879f5638 | ||
|
eb3efc03b4 | ||
|
bc6c7610eb | ||
|
4f92611df0 | ||
|
d0e7ec0d68 | ||
|
062574f0c5 | ||
|
3b367407ae | ||
|
6c097468ff | ||
|
0c107f2bab | ||
|
33d16d16e2 | ||
|
1870a309f7 | ||
|
07101c72dc | ||
|
b8682960cf | ||
|
d3a5c23cf0 | ||
|
2cac6dd3f9 | ||
|
336ede6d53 | ||
|
71018c08d4 | ||
|
b35a738557 | ||
|
989143d218 | ||
|
088f853f0a | ||
|
64b25b0ca6 | ||
|
2da76cddd8 | ||
|
a316eaabc6 | ||
|
b9ea6ecddd | ||
|
598ccf383c | ||
|
503b4827a9 | ||
|
e86230915a | ||
|
dafc710c48 | ||
|
d2b1a0527d | ||
|
4ca3455d5b | ||
|
2329e2c61e | ||
|
ad186958cc | ||
|
caf766403c | ||
|
8a01cd3976 | ||
|
e11d6825f3 | ||
|
3d0e1b0922 | ||
|
eb065d321e | ||
|
54cb8b3a10 | ||
|
cc6781bf44 | ||
|
346f2b8ed8 | ||
|
f25c027a2b | ||
|
f77c48218a | ||
|
625bbe3a9f | ||
|
e0c208e049 |
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,33 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Description**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**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.
|
|
||||||
|
|
||||||
**Server Information:**
|
|
||||||
|
|
||||||
[Please complete the following information:]
|
|
||||||
- Database being used (YAML, JSON, MySQL, MongoDB): []
|
|
||||||
- OS: [e.g. iOS]
|
|
||||||
- Java Version: [e.g. Java 8]
|
|
||||||
- BentoBox version: [e.g. 1.7.2.21]
|
|
||||||
- Addons installed? [Do '/bentobox version' and copy/paste from the console]
|
|
||||||
- Other plugins? [Do '/plugins' and copy/paste from the console]
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**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.
|
|
38
.github/workflows/build.yml
vendored
Normal file
38
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
name: Build
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||||
|
- name: Set up JDK 17
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'adopt'
|
||||||
|
java-version: 17
|
||||||
|
- name: Cache SonarCloud packages
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ~/.sonar/cache
|
||||||
|
key: ${{ runner.os }}-sonar
|
||||||
|
restore-keys: ${{ runner.os }}-sonar
|
||||||
|
- name: Cache Maven packages
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ~/.m2
|
||||||
|
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||||
|
restore-keys: ${{ runner.os }}-m2
|
||||||
|
- name: Build and analyze
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||||
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
|
run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
22
.travis.yml
22
.travis.yml
@ -1,22 +0,0 @@
|
|||||||
language: java
|
|
||||||
sudo: false
|
|
||||||
addons:
|
|
||||||
sonarcloud:
|
|
||||||
organization: "bentobox-world"
|
|
||||||
|
|
||||||
jdk:
|
|
||||||
- openjdk8
|
|
||||||
- openjdk11
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- jdk: openjdk11
|
|
||||||
|
|
||||||
script:
|
|
||||||
# the following command line builds the project, runs the tests with coverage and then execute the SonarCloud analysis
|
|
||||||
- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install sonar:sonar -Dsonar.projectKey=BentoBoxWorld_Limits
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- '$HOME/.m2/repository'
|
|
||||||
- '$HOME/.sonar/cache'
|
|
119
pom.xml
119
pom.xml
@ -39,13 +39,9 @@
|
|||||||
</issueManagement>
|
</issueManagement>
|
||||||
|
|
||||||
<distributionManagement>
|
<distributionManagement>
|
||||||
<snapshotRepository>
|
|
||||||
<id>codemc-snapshots</id>
|
|
||||||
<url>https://repo.codemc.org/repository/maven-snapshots</url>
|
|
||||||
</snapshotRepository>
|
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-releases</id>
|
<id>bentoboxworld</id>
|
||||||
<url>https://repo.codemc.org/repository/maven-releases</url>
|
<url>https://repo.codemc.org/repository/bentoboxworld/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</distributionManagement>
|
</distributionManagement>
|
||||||
|
|
||||||
@ -53,18 +49,21 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<java.version>1.8</java.version>
|
<java.version>17</java.version>
|
||||||
<!-- Non-minecraft related dependencies -->
|
<!-- Non-minecraft related dependencies -->
|
||||||
<powermock.version>2.0.2</powermock.version>
|
<powermock.version>2.0.9</powermock.version>
|
||||||
<!-- More visible way how to change dependency versions -->
|
<!-- More visible way how to change dependency versions -->
|
||||||
<spigot.version>1.15.2-R0.1-SNAPSHOT</spigot.version>
|
<spigot.version>1.21.3-R0.1-SNAPSHOT</spigot.version>
|
||||||
<bentobox.version>1.12.0</bentobox.version>
|
<bentobox.version>2.7.1-SNAPSHOT</bentobox.version>
|
||||||
<!-- Revision variable removes warning about dynamic version -->
|
<!-- Revision variable removes warning about dynamic version -->
|
||||||
<revision>${build.version}-SNAPSHOT</revision>
|
<revision>${build.version}-SNAPSHOT</revision>
|
||||||
<!-- Do not change unless you want different name for local builds. -->
|
<!-- Do not change unless you want different name for local builds. -->
|
||||||
<build.number>-LOCAL</build.number>
|
<build.number>-LOCAL</build.number>
|
||||||
<!-- This allows to change between versions. -->
|
<!-- This allows to change between versions. -->
|
||||||
<build.version>1.12.1</build.version>
|
<build.version>1.26.0</build.version>
|
||||||
|
<sonar.projectKey>BentoBoxWorld_Limits</sonar.projectKey>
|
||||||
|
<sonar.organization>bentobox-world</sonar.organization>
|
||||||
|
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<!-- Profiles will allow to automatically change build version. -->
|
<!-- Profiles will allow to automatically change build version. -->
|
||||||
@ -107,30 +106,6 @@
|
|||||||
<build.number></build.number>
|
<build.number></build.number>
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
<profile>
|
|
||||||
<id>sonar</id>
|
|
||||||
<properties>
|
|
||||||
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
|
|
||||||
<sonar.organization>bentobox-world</sonar.organization>
|
|
||||||
</properties>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.sonarsource.scanner.maven</groupId>
|
|
||||||
<artifactId>sonar-maven-plugin</artifactId>
|
|
||||||
<version>3.6.0.1398</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<phase>verify</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>sonar</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
</profiles>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
@ -139,12 +114,12 @@
|
|||||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
|
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc</id>
|
<id>bentoboxworld</id>
|
||||||
<url>https://repo.codemc.org/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.org/repository/bentoboxworld/</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-repo</id>
|
<id>codemc</id>
|
||||||
<url>https://repo.codemc.org/repository/maven-public/</url>
|
<url>https://repo.codemc.org/repository/maven-snapshots/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
@ -160,7 +135,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mockito</groupId>
|
<groupId>org.mockito</groupId>
|
||||||
<artifactId>mockito-core</artifactId>
|
<artifactId>mockito-core</artifactId>
|
||||||
<version>3.0.0</version>
|
<version>3.11.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -217,19 +192,42 @@
|
|||||||
<artifactId>maven-resources-plugin</artifactId>
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
<version>3.1.0</version>
|
<version>3.1.0</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<version>3.7.0</version>
|
|
||||||
<configuration>
|
|
||||||
<source>${java.version}</source>
|
|
||||||
<target>${java.version}</target>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>2.22.0</version>
|
<version>3.0.0-M5</version>
|
||||||
|
<configuration>
|
||||||
|
<argLine>
|
||||||
|
${argLine}
|
||||||
|
--add-opens java.base/java.lang=ALL-UNNAMED
|
||||||
|
--add-opens java.base/java.math=ALL-UNNAMED
|
||||||
|
--add-opens java.base/java.io=ALL-UNNAMED
|
||||||
|
--add-opens java.base/java.util=ALL-UNNAMED
|
||||||
|
--add-opens
|
||||||
|
java.base/java.util.stream=ALL-UNNAMED
|
||||||
|
--add-opens java.base/java.text=ALL-UNNAMED
|
||||||
|
--add-opens
|
||||||
|
java.base/java.util.regex=ALL-UNNAMED
|
||||||
|
--add-opens
|
||||||
|
java.base/java.nio.channels.spi=ALL-UNNAMED
|
||||||
|
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
|
||||||
|
--add-opens java.base/java.net=ALL-UNNAMED
|
||||||
|
--add-opens
|
||||||
|
java.base/java.util.concurrent=ALL-UNNAMED
|
||||||
|
--add-opens java.base/sun.nio.fs=ALL-UNNAMED
|
||||||
|
--add-opens java.base/sun.nio.cs=ALL-UNNAMED
|
||||||
|
--add-opens java.base/java.nio.file=ALL-UNNAMED
|
||||||
|
--add-opens
|
||||||
|
java.base/java.nio.charset=ALL-UNNAMED
|
||||||
|
--add-opens
|
||||||
|
java.base/java.lang.reflect=ALL-UNNAMED
|
||||||
|
--add-opens
|
||||||
|
java.logging/java.util.logging=ALL-UNNAMED
|
||||||
|
--add-opens java.base/java.lang.ref=ALL-UNNAMED
|
||||||
|
--add-opens java.base/java.util.jar=ALL-UNNAMED
|
||||||
|
--add-opens java.base/java.util.zip=ALL-UNNAMED
|
||||||
|
</argLine>
|
||||||
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
@ -249,30 +247,45 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.jacoco</groupId>
|
<groupId>org.jacoco</groupId>
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
<version>0.8.3</version>
|
<version>0.8.10</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<append>true</append>
|
<append>true</append>
|
||||||
<excludes>
|
<excludes>
|
||||||
<!-- This is required to prevent Jacoco from adding
|
<!-- This is required to prevent Jacoco from adding
|
||||||
synthetic fields to a JavaBean class (causes errors in testing) -->
|
synthetic fields to a JavaBean class (causes errors in testing) -->
|
||||||
<exclude>**/*Names*</exclude>
|
<exclude>**/*Names*</exclude>
|
||||||
|
<!-- Prevents the Material is too large to mock error -->
|
||||||
|
<exclude>org/bukkit/Material*</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>pre-unit-test</id>
|
<id>prepare-agent</id>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>prepare-agent</goal>
|
<goal>prepare-agent</goal>
|
||||||
</goals>
|
</goals>
|
||||||
</execution>
|
</execution>
|
||||||
<execution>
|
<execution>
|
||||||
<id>post-unit-test</id>
|
<id>report</id>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>report</goal>
|
<goal>report</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<formats>
|
||||||
|
<format>XML</format>
|
||||||
|
</formats>
|
||||||
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.7.0</version>
|
||||||
|
<configuration>
|
||||||
|
<release>${java.version}</release>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
72
src/main/java/world/bentobox/limits/EntityGroup.java
Normal file
72
src/main/java/world/bentobox/limits/EntityGroup.java
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package world.bentobox.limits;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A named class representing a group of entities and their limits
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class EntityGroup {
|
||||||
|
private final String name;
|
||||||
|
private final Set<EntityType> types;
|
||||||
|
private final int limit;
|
||||||
|
private final Material icon;
|
||||||
|
|
||||||
|
public EntityGroup(String name, Set<EntityType> types, int limit, Material icon) {
|
||||||
|
this.name = name;
|
||||||
|
this.types = types;
|
||||||
|
this.limit = limit;
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(EntityType type) {
|
||||||
|
return types.contains(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<EntityType> getTypes() {
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLimit() {
|
||||||
|
return limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hash = 7;
|
||||||
|
hash = 83 * hash + Objects.hashCode(this.name);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
final EntityGroup other = (EntityGroup) obj;
|
||||||
|
return Objects.equals(this.name, other.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the icon
|
||||||
|
*/
|
||||||
|
public Material getIcon() {
|
||||||
|
return Objects.requireNonNullElse(icon, Material.BARRIER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "EntityGroup [name=" + name + ", types=" + types + ", limit=" + limit + ", icon=" + icon + "]";
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,26 @@
|
|||||||
package world.bentobox.limits;
|
package world.bentobox.limits;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.Registry;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
import world.bentobox.bentobox.api.addons.Addon;
|
import world.bentobox.bentobox.api.addons.Addon;
|
||||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||||
import world.bentobox.limits.commands.AdminCommand;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.limits.commands.PlayerCommand;
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.limits.commands.admin.AdminCommand;
|
||||||
|
import world.bentobox.limits.commands.player.PlayerCommand;
|
||||||
import world.bentobox.limits.listeners.BlockLimitsListener;
|
import world.bentobox.limits.listeners.BlockLimitsListener;
|
||||||
import world.bentobox.limits.listeners.EntityLimitListener;
|
import world.bentobox.limits.listeners.EntityLimitListener;
|
||||||
import world.bentobox.limits.listeners.JoinListener;
|
import world.bentobox.limits.listeners.JoinListener;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,9 +30,11 @@ import world.bentobox.limits.listeners.JoinListener;
|
|||||||
*/
|
*/
|
||||||
public class Limits extends Addon {
|
public class Limits extends Addon {
|
||||||
|
|
||||||
|
private static final String LIMIT_NOT_SET = "Limit not set";
|
||||||
private Settings settings;
|
private Settings settings;
|
||||||
private List<GameModeAddon> gameModes;
|
private List<GameModeAddon> gameModes = new ArrayList<>();
|
||||||
private BlockLimitsListener blockLimitListener;
|
private BlockLimitsListener blockLimitListener;
|
||||||
|
private JoinListener joinListener;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable(){
|
public void onDisable(){
|
||||||
@ -47,13 +58,15 @@ public class Limits extends Addon {
|
|||||||
// Register commands
|
// Register commands
|
||||||
gm.getAdminCommand().ifPresent(a -> new AdminCommand(this, a));
|
gm.getAdminCommand().ifPresent(a -> new AdminCommand(this, a));
|
||||||
gm.getPlayerCommand().ifPresent(a -> new PlayerCommand(this, a));
|
gm.getPlayerCommand().ifPresent(a -> new PlayerCommand(this, a));
|
||||||
|
registerPlaceholders(gm);
|
||||||
log("Limits will apply to " + gm.getDescription().getName());
|
log("Limits will apply to " + gm.getDescription().getName());
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// Register listener
|
// Register listener
|
||||||
blockLimitListener = new BlockLimitsListener(this);
|
blockLimitListener = new BlockLimitsListener(this);
|
||||||
registerListener(blockLimitListener);
|
registerListener(blockLimitListener);
|
||||||
registerListener(new JoinListener(this));
|
joinListener = new JoinListener(this);
|
||||||
|
registerListener(joinListener);
|
||||||
registerListener(new EntityLimitListener(this));
|
registerListener(new EntityLimitListener(this));
|
||||||
// Done
|
// Done
|
||||||
}
|
}
|
||||||
@ -98,9 +111,9 @@ public class Limits extends Addon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of the game mode for this world
|
* Get the permission prefix for this world
|
||||||
* @param world - world
|
* @param world - world
|
||||||
* @return game mode name or empty string if none
|
* @return permisdsion prefix or empty string if none
|
||||||
*/
|
*/
|
||||||
public String getGameModePermPrefix(World world) {
|
public String getGameModePermPrefix(World world) {
|
||||||
return gameModes.stream().filter(gm -> gm.inWorld(world)).findFirst().map(GameModeAddon::getPermissionPrefix).orElse("");
|
return gameModes.stream().filter(gm -> gm.inWorld(world)).findFirst().map(GameModeAddon::getPermissionPrefix).orElse("");
|
||||||
@ -116,4 +129,139 @@ public class Limits extends Addon {
|
|||||||
return gameModes.stream().anyMatch(gm -> gm.getDescription().getName().equals(gameMode));
|
return gameModes.stream().anyMatch(gm -> gm.getDescription().getName().equals(gameMode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the joinListener
|
||||||
|
*/
|
||||||
|
public JoinListener getJoinListener() {
|
||||||
|
return joinListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerPlaceholders(GameModeAddon gm) {
|
||||||
|
if (getPlugin().getPlaceholdersManager() == null) return;
|
||||||
|
Registry.MATERIAL.stream()
|
||||||
|
.filter(Material::isBlock)
|
||||||
|
.forEach(m -> registerCountAndLimitPlaceholders(m, gm));
|
||||||
|
|
||||||
|
Arrays.stream(EntityType.values())
|
||||||
|
.forEach(e -> registerCountAndLimitPlaceholders(e, gm));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers placeholders for the count and limit of the material
|
||||||
|
* in the format of %Limits_(gamemode prefix)_island_(lowercase material name)_count%
|
||||||
|
* and %Limits_(gamemode prefix)_island_(lowercase material name)_limit%
|
||||||
|
*
|
||||||
|
* Example: registerCountAndLimitPlaceholders("HOPPER", gm);
|
||||||
|
* Placeholders:
|
||||||
|
* "Limits_bskyblock_island_hopper_count"
|
||||||
|
* "Limits_bskyblock_island_hopper_limit"
|
||||||
|
* "Limits_bskyblock_island_hopper_base_limit"
|
||||||
|
* "Limits_bskyblock_island_zombie_limit"
|
||||||
|
*
|
||||||
|
* @param m material
|
||||||
|
* @param gm game mode
|
||||||
|
*/
|
||||||
|
private void registerCountAndLimitPlaceholders(Material m, GameModeAddon gm) {
|
||||||
|
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
|
||||||
|
gm.getDescription().getName().toLowerCase() + "_island_" + m.toString().toLowerCase() + "_count",
|
||||||
|
user -> String.valueOf(getCount(user, m, gm)));
|
||||||
|
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
|
||||||
|
gm.getDescription().getName().toLowerCase() + "_island_" + m.toString().toLowerCase() + "_limit",
|
||||||
|
user -> getLimit(user, m, gm));
|
||||||
|
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
|
||||||
|
gm.getDescription().getName().toLowerCase() + "_island_" + m.toString().toLowerCase() + "_base_limit",
|
||||||
|
user -> getBaseLimit(user, m, gm));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerCountAndLimitPlaceholders(EntityType e, GameModeAddon gm) {
|
||||||
|
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
|
||||||
|
gm.getDescription().getName().toLowerCase() + "_island_" + e.toString().toLowerCase() + "_limit",
|
||||||
|
user -> getLimit(user, e, gm));
|
||||||
|
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
|
||||||
|
gm.getDescription().getName().toLowerCase() + "_island_" + e.toString().toLowerCase() + "_base_limit",
|
||||||
|
user -> getBaseLimit(user, e, gm));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param user - Used to identify the island the user belongs to
|
||||||
|
* @param m - The material we are trying to count on the island
|
||||||
|
* @param gm Game Mode Addon
|
||||||
|
* @return Number of blocks of the specified material on the given user's island
|
||||||
|
*/
|
||||||
|
private int getCount(@Nullable User user, Material m, GameModeAddon gm) {
|
||||||
|
Island is = gm.getIslands().getIsland(gm.getOverWorld(), user);
|
||||||
|
if (is == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@Nullable IslandBlockCount ibc = getBlockLimitListener().getIsland(is.getUniqueId());
|
||||||
|
if (ibc == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ibc.getBlockCount(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param user - Used to identify the island the user belongs to
|
||||||
|
* @param m - The material whose limit we are querying
|
||||||
|
* @param gm Game Mode Addon
|
||||||
|
* @return The limit of the specified material on the given user's island
|
||||||
|
*/
|
||||||
|
private String getLimit(@Nullable User user, Material m, GameModeAddon gm) {
|
||||||
|
Island is = gm.getIslands().getIsland(gm.getOverWorld(), user);
|
||||||
|
if (is == null) {
|
||||||
|
return LIMIT_NOT_SET;
|
||||||
|
}
|
||||||
|
if (user != null) {
|
||||||
|
// Check the permissions of the user and update
|
||||||
|
this.getJoinListener().checkPerms(user.getPlayer(), gm.getPermissionPrefix() + "island.limit.",
|
||||||
|
is.getUniqueId(), gm.getDescription().getName());
|
||||||
|
}
|
||||||
|
int limit = this.getBlockLimitListener().
|
||||||
|
getMaterialLimits(is.getWorld(), is.getUniqueId()).getOrDefault(m, -1);
|
||||||
|
|
||||||
|
return limit == -1 ? LIMIT_NOT_SET : String.valueOf(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getBaseLimit(@Nullable User user, Material m, GameModeAddon gm) {
|
||||||
|
Island is = gm.getIslands().getIsland(gm.getOverWorld(), user);
|
||||||
|
if (is == null) {
|
||||||
|
return LIMIT_NOT_SET;
|
||||||
|
}
|
||||||
|
|
||||||
|
int limit = this.getBlockLimitListener().
|
||||||
|
getMaterialLimits(is.getWorld(), is.getUniqueId()).
|
||||||
|
getOrDefault(m, -1);
|
||||||
|
|
||||||
|
if (limit > 0) {
|
||||||
|
limit -= this.getBlockLimitListener().getIsland(is).getBlockLimitOffset(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
return limit == -1 ? LIMIT_NOT_SET : String.valueOf(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getLimit(@Nullable User user, EntityType e, GameModeAddon gm) {
|
||||||
|
Island is = gm.getIslands().getIsland(gm.getOverWorld(), user);
|
||||||
|
if (is == null) {
|
||||||
|
return LIMIT_NOT_SET;
|
||||||
|
}
|
||||||
|
|
||||||
|
int limit = this.getBlockLimitListener().getIsland(is).getEntityLimit(e);
|
||||||
|
if (limit < 0 && this.getSettings().getLimits().containsKey(e)) {
|
||||||
|
limit = this.getSettings().getLimits().get(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return limit == -1 ? LIMIT_NOT_SET : String.valueOf(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getBaseLimit(@Nullable User user, EntityType e, GameModeAddon gm) {
|
||||||
|
Island is = gm.getIslands().getIsland(gm.getOverWorld(), user);
|
||||||
|
if (is == null || !this.getSettings().getLimits().containsKey(e)) {
|
||||||
|
return LIMIT_NOT_SET;
|
||||||
|
}
|
||||||
|
|
||||||
|
int limit = this.getSettings().getLimits().get(e);
|
||||||
|
|
||||||
|
return limit == -1 ? LIMIT_NOT_SET : String.valueOf(limit);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
19
src/main/java/world/bentobox/limits/LimitsPladdon.java
Normal file
19
src/main/java/world/bentobox/limits/LimitsPladdon.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package world.bentobox.limits;
|
||||||
|
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.api.addons.Addon;
|
||||||
|
import world.bentobox.bentobox.api.addons.Pladdon;
|
||||||
|
|
||||||
|
|
||||||
|
public class LimitsPladdon extends Pladdon {
|
||||||
|
|
||||||
|
private Addon addon;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Addon getAddon() {
|
||||||
|
if (addon == null) {
|
||||||
|
addon = new Limits();
|
||||||
|
}
|
||||||
|
return addon;
|
||||||
|
}
|
||||||
|
}
|
@ -1,28 +1,43 @@
|
|||||||
package world.bentobox.limits;
|
package world.bentobox.limits;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
public class Settings {
|
public class Settings {
|
||||||
|
|
||||||
|
enum GeneralGroup {
|
||||||
|
ANIMALS, MOBS
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<GeneralGroup, Integer> general = new EnumMap<>(GeneralGroup.class);
|
||||||
private final Map<EntityType, Integer> limits = new EnumMap<>(EntityType.class);
|
private final Map<EntityType, Integer> limits = new EnumMap<>(EntityType.class);
|
||||||
|
private final Map<EntityType, List<EntityGroup>> groupLimits = new EnumMap<>(EntityType.class);
|
||||||
private final List<String> gameModes;
|
private final List<String> gameModes;
|
||||||
|
private final boolean asyncGolums;
|
||||||
private static final List<EntityType> DISALLOWED = Arrays.asList(
|
private static final List<EntityType> DISALLOWED = Arrays.asList(
|
||||||
EntityType.PRIMED_TNT,
|
EntityType.TNT,
|
||||||
EntityType.EVOKER_FANGS,
|
EntityType.EVOKER_FANGS,
|
||||||
EntityType.LLAMA_SPIT,
|
EntityType.LLAMA_SPIT,
|
||||||
EntityType.DRAGON_FIREBALL,
|
EntityType.DRAGON_FIREBALL,
|
||||||
EntityType.AREA_EFFECT_CLOUD,
|
EntityType.AREA_EFFECT_CLOUD,
|
||||||
EntityType.ENDER_SIGNAL,
|
EntityType.END_CRYSTAL,
|
||||||
EntityType.SMALL_FIREBALL,
|
EntityType.SMALL_FIREBALL,
|
||||||
EntityType.FIREBALL,
|
EntityType.FIREBALL,
|
||||||
EntityType.THROWN_EXP_BOTTLE,
|
EntityType.EXPERIENCE_BOTTLE,
|
||||||
EntityType.EXPERIENCE_ORB,
|
EntityType.EXPERIENCE_ORB,
|
||||||
EntityType.SHULKER_BULLET,
|
EntityType.SHULKER_BULLET,
|
||||||
EntityType.WITHER_SKULL,
|
EntityType.WITHER_SKULL,
|
||||||
@ -31,9 +46,8 @@ public class Settings {
|
|||||||
EntityType.SPECTRAL_ARROW,
|
EntityType.SPECTRAL_ARROW,
|
||||||
EntityType.SNOWBALL,
|
EntityType.SNOWBALL,
|
||||||
EntityType.EGG,
|
EntityType.EGG,
|
||||||
EntityType.LEASH_HITCH,
|
EntityType.LEASH_KNOT,
|
||||||
EntityType.GIANT,
|
EntityType.GIANT,
|
||||||
EntityType.ENDER_CRYSTAL,
|
|
||||||
EntityType.ENDER_PEARL,
|
EntityType.ENDER_PEARL,
|
||||||
EntityType.ENDER_DRAGON,
|
EntityType.ENDER_DRAGON,
|
||||||
EntityType.ITEM_FRAME,
|
EntityType.ITEM_FRAME,
|
||||||
@ -47,6 +61,11 @@ public class Settings {
|
|||||||
ConfigurationSection el = addon.getConfig().getConfigurationSection("entitylimits");
|
ConfigurationSection el = addon.getConfig().getConfigurationSection("entitylimits");
|
||||||
if (el != null) {
|
if (el != null) {
|
||||||
for (String key : el.getKeys(false)) {
|
for (String key : el.getKeys(false)) {
|
||||||
|
if (key.equalsIgnoreCase("ANIMALS")) {
|
||||||
|
general.put(GeneralGroup.ANIMALS, el.getInt(key, 0));
|
||||||
|
} else if (key.equalsIgnoreCase("MOBS")) {
|
||||||
|
general.put(GeneralGroup.MOBS, el.getInt(key, 0));
|
||||||
|
} else {
|
||||||
EntityType type = getType(key);
|
EntityType type = getType(key);
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
if (DISALLOWED.contains(type)) {
|
if (DISALLOWED.contains(type)) {
|
||||||
@ -59,8 +78,52 @@ public class Settings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// Async Golums
|
||||||
|
asyncGolums = addon.getConfig().getBoolean("async-golums", true);
|
||||||
|
|
||||||
addon.log("Entity limits:");
|
addon.log("Entity limits:");
|
||||||
limits.entrySet().stream().map(e -> "Limit " + e.getKey().toString() + " to " + e.getValue()).forEach(addon::log);
|
limits.entrySet().stream().map(e -> "Limit " + e.getKey().toString() + " to " + e.getValue()).forEach(addon::log);
|
||||||
|
|
||||||
|
//group limits
|
||||||
|
el = addon.getConfig().getConfigurationSection("entitygrouplimits");
|
||||||
|
if (el != null) {
|
||||||
|
for (String name : el.getKeys(false)) {
|
||||||
|
int limit = el.getInt(name + ".limit");
|
||||||
|
String iconName = el.getString(name + ".icon", "BARRIER");
|
||||||
|
Material icon = Material.BARRIER;
|
||||||
|
try {
|
||||||
|
icon = Material.valueOf(iconName.toUpperCase(Locale.ENGLISH));
|
||||||
|
} catch (Exception e) {
|
||||||
|
addon.logError("Invalid group icon name: " + iconName + ". Use a Bukkit Material.");
|
||||||
|
icon = Material.BARRIER;
|
||||||
|
}
|
||||||
|
Set<EntityType> entities = el.getStringList(name + ".entities").stream().map(s -> {
|
||||||
|
EntityType type = getType(s);
|
||||||
|
if (type != null) {
|
||||||
|
if (DISALLOWED.contains(type)) {
|
||||||
|
addon.logError("Entity type: " + s + " is not supported - skipping...");
|
||||||
|
} else {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addon.logError("Unknown entity type: " + s + " - skipping...");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}).filter(Objects::nonNull).collect(Collectors.toCollection(LinkedHashSet::new));
|
||||||
|
if (entities.isEmpty())
|
||||||
|
continue;
|
||||||
|
EntityGroup group = new EntityGroup(name, entities, limit, icon);
|
||||||
|
entities.forEach(e -> {
|
||||||
|
List<EntityGroup> groups = groupLimits.getOrDefault(e, new ArrayList<>());
|
||||||
|
groups.add(group);
|
||||||
|
groupLimits.put(e, groups);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addon.log("Entity group limits:");
|
||||||
|
getGroupLimitDefinitions().stream().map(e -> "Limit " + e.getName() + " (" + e.getTypes().stream().map(Enum::name).collect(Collectors.joining(", ")) + ") to " + e.getLimit()).forEach(addon::log);
|
||||||
}
|
}
|
||||||
|
|
||||||
private EntityType getType(String key) {
|
private EntityType getType(String key) {
|
||||||
@ -68,12 +131,26 @@ public class Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the limits
|
* @return the entity limits
|
||||||
*/
|
*/
|
||||||
public Map<EntityType, Integer> getLimits() {
|
public Map<EntityType, Integer> getLimits() {
|
||||||
return Collections.unmodifiableMap(limits);
|
return Collections.unmodifiableMap(limits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the group limits
|
||||||
|
*/
|
||||||
|
public Map<EntityType, List<EntityGroup>> getGroupLimits() {
|
||||||
|
return groupLimits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the group limit definitions
|
||||||
|
*/
|
||||||
|
public List<EntityGroup> getGroupLimitDefinitions() {
|
||||||
|
return groupLimits.values().stream().flatMap(Collection::stream).distinct().toList();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the gameModes
|
* @return the gameModes
|
||||||
*/
|
*/
|
||||||
@ -81,4 +158,17 @@ public class Settings {
|
|||||||
return gameModes;
|
return gameModes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the asyncGolums
|
||||||
|
*/
|
||||||
|
public boolean isAsyncGolums() {
|
||||||
|
return asyncGolums;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the general coverage map
|
||||||
|
*/
|
||||||
|
public Map<GeneralGroup, Integer> getGeneral() {
|
||||||
|
return general;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
151
src/main/java/world/bentobox/limits/calculators/Pipeliner.java
Normal file
151
src/main/java/world/bentobox/limits/calculators/Pipeliner.java
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
package world.bentobox.limits.calculators;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.calculators.Results.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pipeliner that will process one island at a time
|
||||||
|
* @author tastybento
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Pipeliner {
|
||||||
|
|
||||||
|
private static final int START_DURATION = 10; // 10 seconds
|
||||||
|
private static final int CONCURRENT_COUNTS = 1;
|
||||||
|
private final Queue<RecountCalculator> toProcessQueue;
|
||||||
|
private final Map<RecountCalculator, Long> inProcessQueue;
|
||||||
|
private final BukkitTask task;
|
||||||
|
private final Limits addon;
|
||||||
|
private long time;
|
||||||
|
private long count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the pipeliner
|
||||||
|
*/
|
||||||
|
public Pipeliner(Limits addon) {
|
||||||
|
this.addon = addon;
|
||||||
|
toProcessQueue = new ConcurrentLinkedQueue<>();
|
||||||
|
inProcessQueue = new HashMap<>();
|
||||||
|
// Loop continuously - check every tick if there is an island to scan
|
||||||
|
task = Bukkit.getScheduler().runTaskTimer(BentoBox.getInstance(), () -> {
|
||||||
|
if (!BentoBox.getInstance().isEnabled()) {
|
||||||
|
cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Complete the current to Process queue first
|
||||||
|
if (!inProcessQueue.isEmpty() || toProcessQueue.isEmpty()) return;
|
||||||
|
for (int j = 0; j < CONCURRENT_COUNTS && !toProcessQueue.isEmpty(); j++) {
|
||||||
|
RecountCalculator iD = toProcessQueue.poll();
|
||||||
|
// Ignore deleted or unonwed islands
|
||||||
|
if (!iD.getIsland().isDeleted() && !iD.getIsland().isUnowned()) {
|
||||||
|
inProcessQueue.put(iD, System.currentTimeMillis());
|
||||||
|
// Start the scanning of a island with the first chunk
|
||||||
|
scanIsland(iD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1L, 10L);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancel() {
|
||||||
|
task.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return number of islands currently in the queue or in process
|
||||||
|
*/
|
||||||
|
public int getIslandsInQueue() {
|
||||||
|
return inProcessQueue.size() + toProcessQueue.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans one chunk of an island and adds the results to a results object
|
||||||
|
* @param iD
|
||||||
|
*/
|
||||||
|
private void scanIsland(RecountCalculator iD) {
|
||||||
|
if (iD.getIsland().isDeleted() || iD.getIsland().isUnowned() || task.isCancelled()) {
|
||||||
|
// Island is deleted, so finish early with nothing
|
||||||
|
inProcessQueue.remove(iD);
|
||||||
|
iD.getR().complete(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
iD.scanIsland(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an island to the scanning queue but only if the island is not already in the queue
|
||||||
|
* @param island - the island to scan
|
||||||
|
* @return CompletableFuture of the results. Results will be null if the island is already in the queue
|
||||||
|
*/
|
||||||
|
public CompletableFuture<Results> addIsland(Island island) {
|
||||||
|
// Check if queue already contains island
|
||||||
|
if (inProcessQueue.keySet().parallelStream().map(RecountCalculator::getIsland).anyMatch(island::equals)
|
||||||
|
|| toProcessQueue.parallelStream().map(RecountCalculator::getIsland).anyMatch(island::equals)) {
|
||||||
|
return CompletableFuture.completedFuture(new Results(Result.IN_PROGRESS));
|
||||||
|
}
|
||||||
|
return addToQueue(island);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<Results> addToQueue(Island island) {
|
||||||
|
CompletableFuture<Results> r = new CompletableFuture<>();
|
||||||
|
toProcessQueue.add(new RecountCalculator(addon, island, r));
|
||||||
|
count++;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the average time it takes to run a level check
|
||||||
|
* @return the average time in seconds
|
||||||
|
*/
|
||||||
|
public int getTime() {
|
||||||
|
return time == 0 || count == 0 ? START_DURATION : (int)((double)time/count/1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit how long a level check took
|
||||||
|
* @param time the time to set
|
||||||
|
*/
|
||||||
|
public void setTime(long time) {
|
||||||
|
// Running average
|
||||||
|
this.time += time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the current queue.
|
||||||
|
*/
|
||||||
|
public void stop() {
|
||||||
|
addon.log("Stopping Level queue");
|
||||||
|
task.cancel();
|
||||||
|
this.inProcessQueue.clear();
|
||||||
|
this.toProcessQueue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the inProcessQueue
|
||||||
|
*/
|
||||||
|
protected Map<RecountCalculator, Long> getInProcessQueue() {
|
||||||
|
return inProcessQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the task
|
||||||
|
*/
|
||||||
|
protected BukkitTask getTask() {
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,359 @@
|
|||||||
|
package world.bentobox.limits.calculators;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Chunk;
|
||||||
|
import org.bukkit.ChunkSnapshot;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.Tag;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.World.Environment;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.block.data.type.Slab;
|
||||||
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.bentobox.util.Pair;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.calculators.Results.Result;
|
||||||
|
import world.bentobox.limits.listeners.BlockLimitsListener;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counter for limits
|
||||||
|
* @author tastybento
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class RecountCalculator {
|
||||||
|
public static final long MAX_AMOUNT = 10000;
|
||||||
|
private static final int CHUNKS_TO_SCAN = 100;
|
||||||
|
private static final int CALCULATION_TIMEOUT = 5; // Minutes
|
||||||
|
|
||||||
|
|
||||||
|
private final Limits addon;
|
||||||
|
private final Queue<Pair<Integer, Integer>> chunksToCheck;
|
||||||
|
private final Island island;
|
||||||
|
private final CompletableFuture<Results> r;
|
||||||
|
|
||||||
|
|
||||||
|
private final Results results;
|
||||||
|
private final Map<Environment, World> worlds = new EnumMap<>(Environment.class);
|
||||||
|
private final List<Location> stackedBlocks = new ArrayList<>();
|
||||||
|
private BukkitTask finishTask;
|
||||||
|
private final BlockLimitsListener bll;
|
||||||
|
private final World world;
|
||||||
|
private IslandBlockCount ibc;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor to get the level for an island
|
||||||
|
* @param addon - addon
|
||||||
|
* @param island - the island to scan
|
||||||
|
* @param r - completable result that will be completed when the calculation is complete
|
||||||
|
*/
|
||||||
|
public RecountCalculator(Limits addon, Island island, CompletableFuture<Results> r) {
|
||||||
|
this.addon = addon;
|
||||||
|
this.bll = addon.getBlockLimitListener();
|
||||||
|
this.island = island;
|
||||||
|
this.ibc = bll.getIsland(Objects.requireNonNull(island).getUniqueId());
|
||||||
|
this.r = r;
|
||||||
|
results = new Results();
|
||||||
|
chunksToCheck = getChunksToScan(island);
|
||||||
|
// Set up the worlds
|
||||||
|
this.world = Objects.requireNonNull(Util.getWorld(island.getWorld()));
|
||||||
|
worlds.put(Environment.NORMAL, world);
|
||||||
|
boolean isNether = addon.getPlugin().getIWM().isNetherGenerate(world) && addon.getPlugin().getIWM().isNetherIslands(world);
|
||||||
|
boolean isEnd = addon.getPlugin().getIWM().isEndGenerate(world) && addon.getPlugin().getIWM().isEndIslands(world);
|
||||||
|
|
||||||
|
// Nether
|
||||||
|
if (isNether) {
|
||||||
|
World nether = addon.getPlugin().getIWM().getNetherWorld(island.getWorld());
|
||||||
|
if (nether != null) {
|
||||||
|
worlds.put(Environment.NETHER, nether);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// End
|
||||||
|
if (isEnd) {
|
||||||
|
World end = addon.getPlugin().getIWM().getEndWorld(island.getWorld());
|
||||||
|
if (end != null) {
|
||||||
|
worlds.put(Environment.THE_END, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkBlock(BlockData b) {
|
||||||
|
Material md = bll.fixMaterial(b);
|
||||||
|
// md is limited
|
||||||
|
if (bll.getMaterialLimits(world, island.getUniqueId()).containsKey(md)) {
|
||||||
|
results.mdCount.add(md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get a set of all the chunks in island
|
||||||
|
* @param island - island
|
||||||
|
* @return - set of pairs of x,z coordinates to check
|
||||||
|
*/
|
||||||
|
private Queue<Pair<Integer, Integer>> getChunksToScan(Island island) {
|
||||||
|
Queue<Pair<Integer, Integer>> chunkQueue = new ConcurrentLinkedQueue<>();
|
||||||
|
for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + island.getProtectionRange() * 2 + 16); x += 16) {
|
||||||
|
for (int z = island.getMinProtectedZ(); z < (island.getMinProtectedZ() + island.getProtectionRange() * 2 + 16); z += 16) {
|
||||||
|
chunkQueue.add(new Pair<>(x >> 4, z >> 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chunkQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the island
|
||||||
|
*/
|
||||||
|
public Island getIsland() {
|
||||||
|
return island;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the completable result for this calculation
|
||||||
|
* @return the r
|
||||||
|
*/
|
||||||
|
public CompletableFuture<Results> getR() {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the results
|
||||||
|
*/
|
||||||
|
public Results getResults() {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a chunk async
|
||||||
|
* @param env - the environment
|
||||||
|
* @param x - chunk x coordinate
|
||||||
|
* @param z - chunk z coordinate
|
||||||
|
* @return a future chunk or future null if there is no chunk to load, e.g., there is no island nether
|
||||||
|
*/
|
||||||
|
private CompletableFuture<List<Chunk>> getWorldChunk(Environment env, Queue<Pair<Integer, Integer>> pairList) {
|
||||||
|
if (worlds.containsKey(env)) {
|
||||||
|
CompletableFuture<List<Chunk>> r2 = new CompletableFuture<>();
|
||||||
|
List<Chunk> chunkList = new ArrayList<>();
|
||||||
|
World world = worlds.get(env);
|
||||||
|
// Get the chunk, and then coincidentally check the RoseStacker
|
||||||
|
loadChunks(r2, world, pairList, chunkList);
|
||||||
|
return r2;
|
||||||
|
}
|
||||||
|
return CompletableFuture.completedFuture(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadChunks(CompletableFuture<List<Chunk>> r2, World world, Queue<Pair<Integer, Integer>> pairList,
|
||||||
|
List<Chunk> chunkList) {
|
||||||
|
if (pairList.isEmpty()) {
|
||||||
|
r2.complete(chunkList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Pair<Integer, Integer> p = pairList.poll();
|
||||||
|
Util.getChunkAtAsync(world, p.x, p.z, world.getEnvironment().equals(Environment.NETHER)).thenAccept(chunk -> {
|
||||||
|
if (chunk != null) {
|
||||||
|
chunkList.add(chunk);
|
||||||
|
// roseStackerCheck(chunk);
|
||||||
|
}
|
||||||
|
loadChunks(r2, world, pairList, chunkList); // Iteration
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
private void roseStackerCheck(Chunk chunk) {
|
||||||
|
if (addon.isRoseStackersEnabled()) {
|
||||||
|
RoseStackerAPI.getInstance().getStackedBlocks(Collections.singletonList(chunk)).forEach(e -> {
|
||||||
|
// Blocks below sea level can be scored differently
|
||||||
|
boolean belowSeaLevel = seaHeight > 0 && e.getLocation().getY() <= seaHeight;
|
||||||
|
// Check block once because the base block will be counted in the chunk snapshot
|
||||||
|
for (int _x = 0; _x < e.getStackSize() - 1; _x++) {
|
||||||
|
checkBlock(e.getBlock().getType(), belowSeaLevel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Count the blocks on the island
|
||||||
|
* @param chunk chunk to scan
|
||||||
|
*/
|
||||||
|
private void scanAsync(Chunk chunk) {
|
||||||
|
ChunkSnapshot chunkSnapshot = chunk.getChunkSnapshot();
|
||||||
|
for (int x = 0; x< 16; x++) {
|
||||||
|
// Check if the block coordinate is inside the protection zone and if not, don't count it
|
||||||
|
if (chunkSnapshot.getX() * 16 + x < island.getMinProtectedX() || chunkSnapshot.getX() * 16 + x >= island.getMinProtectedX() + island.getProtectionRange() * 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
// Check if the block coordinate is inside the protection zone and if not, don't count it
|
||||||
|
if (chunkSnapshot.getZ() * 16 + z < island.getMinProtectedZ() || chunkSnapshot.getZ() * 16 + z >= island.getMinProtectedZ() + island.getProtectionRange() * 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Only count to the highest block in the world for some optimization
|
||||||
|
for (int y = chunk.getWorld().getMinHeight(); y < chunk.getWorld().getMaxHeight(); y++) {
|
||||||
|
BlockData blockData = chunkSnapshot.getBlockData(x, y, z);
|
||||||
|
// Slabs can be doubled, so check them twice
|
||||||
|
if (Tag.SLABS.isTagged(blockData.getMaterial())) {
|
||||||
|
Slab slab = (Slab)blockData;
|
||||||
|
if (slab.getType().equals(Slab.Type.DOUBLE)) {
|
||||||
|
checkBlock(blockData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Hook for Wild Stackers (Blocks Only) - this has to use the real chunk
|
||||||
|
/*
|
||||||
|
if (addon.isStackersEnabled() && blockData.getMaterial() == Material.CAULDRON) {
|
||||||
|
stackedBlocks.add(new Location(chunk.getWorld(), x + chunkSnapshot.getX() * 16,y,z + chunkSnapshot.getZ() * 16));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// Add the value of the block's material
|
||||||
|
checkBlock(blockData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan the chunk chests and count the blocks
|
||||||
|
* @param chunks - the chunk to scan
|
||||||
|
* @return future that completes when the scan is done and supplies a boolean that will be true if the scan was successful, false if not
|
||||||
|
*/
|
||||||
|
private CompletableFuture<Boolean> scanChunk(List<Chunk> chunks) {
|
||||||
|
// If the chunk hasn't been generated, return
|
||||||
|
if (chunks == null || chunks.isEmpty()) {
|
||||||
|
return CompletableFuture.completedFuture(false);
|
||||||
|
}
|
||||||
|
// Count blocks in chunk
|
||||||
|
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskAsynchronously(BentoBox.getInstance(), () -> {
|
||||||
|
chunks.forEach(chunk -> scanAsync(chunk));
|
||||||
|
Bukkit.getScheduler().runTask(addon.getPlugin(),() -> result.complete(true));
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan the next chunk on the island
|
||||||
|
* @return completable boolean future that will be true if more chunks are left to be scanned, and false if not
|
||||||
|
*/
|
||||||
|
public CompletableFuture<Boolean> scanNextChunk() {
|
||||||
|
if (chunksToCheck.isEmpty()) {
|
||||||
|
addon.logError("Unexpected: no chunks to scan!");
|
||||||
|
// This should not be needed, but just in case
|
||||||
|
return CompletableFuture.completedFuture(false);
|
||||||
|
}
|
||||||
|
// Retrieve and remove from the queue
|
||||||
|
Queue<Pair<Integer, Integer>> pairList = new ConcurrentLinkedQueue<>();
|
||||||
|
int i = 0;
|
||||||
|
while (!chunksToCheck.isEmpty() && i++ < CHUNKS_TO_SCAN) {
|
||||||
|
pairList.add(chunksToCheck.poll());
|
||||||
|
}
|
||||||
|
Queue<Pair<Integer, Integer>> endPairList = new ConcurrentLinkedQueue<>(pairList);
|
||||||
|
Queue<Pair<Integer, Integer>> netherPairList = new ConcurrentLinkedQueue<>(pairList);
|
||||||
|
// Set up the result
|
||||||
|
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
||||||
|
// Get chunks and scan
|
||||||
|
// Get chunks and scan
|
||||||
|
getWorldChunk(Environment.THE_END, endPairList).thenAccept(endChunks ->
|
||||||
|
scanChunk(endChunks).thenAccept(b ->
|
||||||
|
getWorldChunk(Environment.NETHER, netherPairList).thenAccept(netherChunks ->
|
||||||
|
scanChunk(netherChunks).thenAccept(b2 ->
|
||||||
|
getWorldChunk(Environment.NORMAL, pairList).thenAccept(normalChunks ->
|
||||||
|
scanChunk(normalChunks).thenAccept(b3 ->
|
||||||
|
// Complete the result now that all chunks have been scanned
|
||||||
|
result.complete(!chunksToCheck.isEmpty()))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finalizes the calculations and makes the report
|
||||||
|
*/
|
||||||
|
public void tidyUp() {
|
||||||
|
// Finalize calculations
|
||||||
|
if (ibc == null) {
|
||||||
|
ibc = new IslandBlockCount(island.getUniqueId(), addon.getPlugin().getIWM().getAddon(world).map(a -> a.getDescription().getName()).orElse("default"));
|
||||||
|
}
|
||||||
|
ibc.getBlockCounts().clear();
|
||||||
|
results.getMdCount().forEach(ibc::add);
|
||||||
|
bll.setIsland(island.getUniqueId(), ibc);
|
||||||
|
//Bukkit.getScheduler().runTask(addon.getPlugin(), () -> sender.sendMessage("admin.limits.calc.finished"));
|
||||||
|
|
||||||
|
// All done.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scanIsland(Pipeliner pipeliner) {
|
||||||
|
// Scan the next chunk
|
||||||
|
scanNextChunk().thenAccept(r -> {
|
||||||
|
if (!Bukkit.isPrimaryThread()) {
|
||||||
|
addon.getPlugin().logError("scanChunk not on Primary Thread!");
|
||||||
|
}
|
||||||
|
// Timeout check
|
||||||
|
if (System.currentTimeMillis() - pipeliner.getInProcessQueue().get(this) > CALCULATION_TIMEOUT * 60000) {
|
||||||
|
// Done
|
||||||
|
pipeliner.getInProcessQueue().remove(this);
|
||||||
|
getR().complete(new Results(Result.TIMEOUT));
|
||||||
|
addon.logError("Level calculation timed out after " + CALCULATION_TIMEOUT + "m for island: " + getIsland());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Boolean.TRUE.equals(r) && !pipeliner.getTask().isCancelled()) {
|
||||||
|
// scanNextChunk returns true if there are more chunks to scan
|
||||||
|
scanIsland(pipeliner);
|
||||||
|
} else {
|
||||||
|
// Done
|
||||||
|
pipeliner.getInProcessQueue().remove(this);
|
||||||
|
// Chunk finished
|
||||||
|
// This was the last chunk
|
||||||
|
handleStackedBlocks();
|
||||||
|
long checkTime = System.currentTimeMillis();
|
||||||
|
finishTask = Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> {
|
||||||
|
// Check every half second if all the chests and stacks have been cleared
|
||||||
|
if ((stackedBlocks.isEmpty()) || System.currentTimeMillis() - checkTime > MAX_AMOUNT) {
|
||||||
|
this.tidyUp();
|
||||||
|
this.getR().complete(getResults());
|
||||||
|
finishTask.cancel();
|
||||||
|
}
|
||||||
|
}, 0, 10L);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleStackedBlocks() {
|
||||||
|
// Deal with any stacked blocks
|
||||||
|
/*
|
||||||
|
Iterator<Location> it = stackedBlocks.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Location v = it.next();
|
||||||
|
Util.getChunkAtAsync(v).thenAccept(c -> {
|
||||||
|
Block cauldronBlock = v.getBlock();
|
||||||
|
boolean belowSeaLevel = seaHeight > 0 && v.getBlockY() <= seaHeight;
|
||||||
|
if (WildStackerAPI.getWildStacker().getSystemManager().isStackedBarrel(cauldronBlock)) {
|
||||||
|
StackedBarrel barrel = WildStackerAPI.getStackedBarrel(cauldronBlock);
|
||||||
|
int barrelAmt = WildStackerAPI.getBarrelAmount(cauldronBlock);
|
||||||
|
for (int _x = 0; _x < barrelAmt; _x++) {
|
||||||
|
checkBlock(barrel.getType(), belowSeaLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
57
src/main/java/world/bentobox/limits/calculators/Results.java
Normal file
57
src/main/java/world/bentobox/limits/calculators/Results.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package world.bentobox.limits.calculators;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
|
import com.google.common.collect.HashMultiset;
|
||||||
|
import com.google.common.collect.Multiset;
|
||||||
|
|
||||||
|
public class Results {
|
||||||
|
public enum Result {
|
||||||
|
/**
|
||||||
|
* A level calc is already in progress
|
||||||
|
*/
|
||||||
|
IN_PROGRESS,
|
||||||
|
/**
|
||||||
|
* Results will be available
|
||||||
|
*/
|
||||||
|
AVAILABLE,
|
||||||
|
/**
|
||||||
|
* Result if calculation timed out
|
||||||
|
*/
|
||||||
|
TIMEOUT
|
||||||
|
}
|
||||||
|
final Multiset<Material> mdCount = HashMultiset.create();
|
||||||
|
final Multiset<EntityType> entityCount = HashMultiset.create();
|
||||||
|
|
||||||
|
final Result state;
|
||||||
|
|
||||||
|
public Results(Result state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Results() {
|
||||||
|
this.state = Result.AVAILABLE;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return the mdCount
|
||||||
|
*/
|
||||||
|
public Multiset<Material> getMdCount() {
|
||||||
|
return mdCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the state
|
||||||
|
*/
|
||||||
|
public Result getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the entityCount
|
||||||
|
*/
|
||||||
|
public Multiset<EntityType> getEntityCount() {
|
||||||
|
return entityCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
package world.bentobox.limits.calculators;
|
@ -1,158 +0,0 @@
|
|||||||
package world.bentobox.limits.commands;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.entity.EntityType;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
|
||||||
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
|
|
||||||
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
|
|
||||||
import world.bentobox.bentobox.api.user.User;
|
|
||||||
import world.bentobox.bentobox.database.objects.Island;
|
|
||||||
import world.bentobox.bentobox.util.Util;
|
|
||||||
import world.bentobox.limits.Limits;
|
|
||||||
import world.bentobox.limits.objects.IslandBlockCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows a panel of the blocks that are limited and their status
|
|
||||||
* @author tastybento
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class LimitPanel {
|
|
||||||
|
|
||||||
private final Limits addon;
|
|
||||||
// This maps the entity types to the icon that should be shown in the panel
|
|
||||||
// If the icon is null, then the entity type is not covered by the addon
|
|
||||||
private static final Map<EntityType, Material> E2M = ImmutableMap.<EntityType, Material>builder()
|
|
||||||
.put(EntityType.PIG_ZOMBIE, Material.ZOMBIE_PIGMAN_SPAWN_EGG)
|
|
||||||
.put(EntityType.MUSHROOM_COW, Material.MOOSHROOM_SPAWN_EGG)
|
|
||||||
.put(EntityType.SNOWMAN, Material.SNOW_BLOCK)
|
|
||||||
.put(EntityType.IRON_GOLEM, Material.IRON_BLOCK)
|
|
||||||
.put(EntityType.ILLUSIONER, Material.VILLAGER_SPAWN_EGG)
|
|
||||||
.put(EntityType.WITHER, Material.WITHER_SKELETON_SKULL)
|
|
||||||
.put(EntityType.BOAT, Material.OAK_BOAT)
|
|
||||||
.put(EntityType.ARMOR_STAND, Material.ARMOR_STAND)
|
|
||||||
.put(EntityType.ITEM_FRAME, Material.ITEM_FRAME)
|
|
||||||
.put(EntityType.PAINTING, Material.PAINTING)
|
|
||||||
// Minecarts
|
|
||||||
.put(EntityType.MINECART_TNT, Material.TNT_MINECART)
|
|
||||||
.put(EntityType.MINECART_CHEST, Material.CHEST_MINECART)
|
|
||||||
.put(EntityType.MINECART_COMMAND, Material.COMMAND_BLOCK_MINECART)
|
|
||||||
.put(EntityType.MINECART_FURNACE, Material.FURNACE_MINECART)
|
|
||||||
.put(EntityType.MINECART_HOPPER, Material.HOPPER_MINECART)
|
|
||||||
.put(EntityType.MINECART_MOB_SPAWNER, Material.MINECART)
|
|
||||||
.build();
|
|
||||||
// This is a map of blocks to Material
|
|
||||||
private static final Map<Material, Material> B2M;
|
|
||||||
static {
|
|
||||||
ImmutableMap.Builder<Material, Material> builder = ImmutableMap.<Material, Material>builder()
|
|
||||||
.put(Material.POTATOES, Material.POTATO)
|
|
||||||
.put(Material.CARROTS, Material.CARROT)
|
|
||||||
.put(Material.BEETROOTS, Material.BEETROOT)
|
|
||||||
.put(Material.REDSTONE_WIRE, Material.REDSTONE);
|
|
||||||
// Block to Material icons
|
|
||||||
Optional.ofNullable(Material.getMaterial("SWEET_BERRY_BUSH")).ifPresent(material -> builder.put(material, Objects.requireNonNull(Material.getMaterial("SWEET_BERRIES"))));
|
|
||||||
Optional.ofNullable(Material.getMaterial("BAMBOO_SAPLING")).ifPresent(material -> builder.put(material, Objects.requireNonNull(Material.getMaterial("BAMBOO"))));
|
|
||||||
B2M = builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param addon - limit addon
|
|
||||||
*/
|
|
||||||
public LimitPanel(Limits addon) {
|
|
||||||
this.addon = addon;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showLimits(World world, User user, UUID target) {
|
|
||||||
PanelBuilder pb = new PanelBuilder().name(user.getTranslation(world, "limits.panel-title")).user(user);
|
|
||||||
// Get the island for the target
|
|
||||||
Island island = addon.getIslands().getIsland(world, target);
|
|
||||||
if (island == null) {
|
|
||||||
if (user.getUniqueId().equals(target)) {
|
|
||||||
user.sendMessage("general.errors.no-island");
|
|
||||||
} else {
|
|
||||||
user.sendMessage("general.errors.player-has-no-island");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(island.getUniqueId());
|
|
||||||
Map<Material, Integer> matLimits = addon.getBlockLimitListener().getMaterialLimits(world, island.getUniqueId());
|
|
||||||
if (matLimits.isEmpty() && addon.getSettings().getLimits().isEmpty()) {
|
|
||||||
user.sendMessage("island.limits.no-limits");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Material limits
|
|
||||||
for (Entry<Material, Integer> en : matLimits.entrySet()) {
|
|
||||||
PanelItemBuilder pib = new PanelItemBuilder();
|
|
||||||
pib.name(Util.prettifyText(en.getKey().toString()));
|
|
||||||
// Adjust icon
|
|
||||||
pib.icon(B2M.getOrDefault(en.getKey(), en.getKey()));
|
|
||||||
|
|
||||||
int count = ibc == null ? 0 : ibc.getBlockCount().getOrDefault(en.getKey(), 0);
|
|
||||||
String color = count >= en.getValue() ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color");
|
|
||||||
pib.description(color
|
|
||||||
+ user.getTranslation("island.limits.block-limit-syntax",
|
|
||||||
TextVariables.NUMBER, String.valueOf(count),
|
|
||||||
"[limit]", String.valueOf(en.getValue())));
|
|
||||||
pb.item(pib.build());
|
|
||||||
}
|
|
||||||
// Entity limits
|
|
||||||
Map<EntityType, Integer> map = new HashMap<>(addon.getSettings().getLimits());
|
|
||||||
// Merge in any permission-based limits
|
|
||||||
if (ibc != null) ibc.getEntityLimits().forEach(map::put);
|
|
||||||
map.forEach((k,v) -> {
|
|
||||||
PanelItemBuilder pib = new PanelItemBuilder();
|
|
||||||
pib.name(Util.prettifyText(k.toString()));
|
|
||||||
Material m;
|
|
||||||
try {
|
|
||||||
if (E2M.containsKey(k)) {
|
|
||||||
m = E2M.get(k);
|
|
||||||
} else if (k.isAlive()) {
|
|
||||||
m = Material.valueOf(k.toString() + "_SPAWN_EGG");
|
|
||||||
} else {
|
|
||||||
// Regular material
|
|
||||||
m = Material.valueOf(k.toString());
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
m = Material.BARRIER;
|
|
||||||
}
|
|
||||||
pib.icon(m);
|
|
||||||
long count = getCount(island, k);
|
|
||||||
String color = count >= v ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color");
|
|
||||||
pib.description(color
|
|
||||||
+ user.getTranslation("island.limits.block-limit-syntax",
|
|
||||||
TextVariables.NUMBER, String.valueOf(count),
|
|
||||||
"[limit]", String.valueOf(v)));
|
|
||||||
pb.item(pib.build());
|
|
||||||
});
|
|
||||||
pb.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
long getCount(Island island, EntityType ent) {
|
|
||||||
long count = island.getWorld().getEntities().stream()
|
|
||||||
.filter(e -> e.getType().equals(ent))
|
|
||||||
.filter(e -> island.inIslandSpace(e.getLocation())).count();
|
|
||||||
// Nether
|
|
||||||
if (addon.getPlugin().getIWM().isNetherIslands(island.getWorld()) && addon.getPlugin().getIWM().getNetherWorld(island.getWorld()) != null) {
|
|
||||||
count += addon.getPlugin().getIWM().getNetherWorld(island.getWorld()).getEntities().stream()
|
|
||||||
.filter(e -> e.getType().equals(ent))
|
|
||||||
.filter(e -> island.inIslandSpace(e.getLocation())).count();
|
|
||||||
}
|
|
||||||
// End
|
|
||||||
if (addon.getPlugin().getIWM().isEndIslands(island.getWorld()) && addon.getPlugin().getIWM().getEndWorld(island.getWorld()) != null) {
|
|
||||||
count += addon.getPlugin().getIWM().getEndWorld(island.getWorld()).getEntities().stream()
|
|
||||||
.filter(e -> e.getType().equals(ent))
|
|
||||||
.filter(e -> island.inIslandSpace(e.getLocation())).count();
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,125 +0,0 @@
|
|||||||
package world.bentobox.limits.commands;
|
|
||||||
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.ChunkSnapshot;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.World;
|
|
||||||
|
|
||||||
import world.bentobox.bentobox.BentoBox;
|
|
||||||
import world.bentobox.bentobox.api.user.User;
|
|
||||||
import world.bentobox.bentobox.database.objects.Island;
|
|
||||||
import world.bentobox.bentobox.util.Pair;
|
|
||||||
import world.bentobox.bentobox.util.Util;
|
|
||||||
import world.bentobox.limits.Limits;
|
|
||||||
import world.bentobox.limits.listeners.BlockLimitsListener;
|
|
||||||
import world.bentobox.limits.objects.IslandBlockCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author YellowZaki, tastybento
|
|
||||||
*/
|
|
||||||
public class LimitsCalc {
|
|
||||||
|
|
||||||
private final Limits addon;
|
|
||||||
private final World world;
|
|
||||||
private final Island island;
|
|
||||||
private final BlockLimitsListener bll;
|
|
||||||
private IslandBlockCount ibc;
|
|
||||||
private final Map<Material, AtomicInteger> blockCount;
|
|
||||||
private final User sender;
|
|
||||||
private final Set<Pair<Integer, Integer>> chunksToScan;
|
|
||||||
private int count;
|
|
||||||
|
|
||||||
|
|
||||||
LimitsCalc(World world, BentoBox instance, UUID targetPlayer, Limits addon, User sender) {
|
|
||||||
this.addon = addon;
|
|
||||||
this.island = instance.getIslands().getIsland(world, targetPlayer);
|
|
||||||
this.bll = addon.getBlockLimitListener();
|
|
||||||
this.ibc = bll.getIsland(island.getUniqueId());
|
|
||||||
blockCount = new EnumMap<>(Material.class);
|
|
||||||
this.sender = sender;
|
|
||||||
this.world = world;
|
|
||||||
|
|
||||||
// Get chunks to scan
|
|
||||||
chunksToScan = getChunksToScan(island);
|
|
||||||
count = 0;
|
|
||||||
chunksToScan.forEach(c -> Util.getChunkAtAsync(world, c.x, c.z).thenAccept(ch -> {
|
|
||||||
ChunkSnapshot snapShot = ch.getChunkSnapshot();
|
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> {
|
|
||||||
this.scanChunk(snapShot);
|
|
||||||
count++;
|
|
||||||
if (count == chunksToScan.size()) {
|
|
||||||
this.tidyUp();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void scanChunk(ChunkSnapshot chunk) {
|
|
||||||
for (int x = 0; x < 16; x++) {
|
|
||||||
// Check if the block coordinate is inside the protection zone and if not, don't count it
|
|
||||||
if (chunk.getX() * 16 + x < island.getMinProtectedX() || chunk.getX() * 16 + x >= island.getMinProtectedX() + island.getProtectionRange() * 2) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (int z = 0; z < 16; z++) {
|
|
||||||
// Check if the block coordinate is inside the protection zone and if not, don't count it
|
|
||||||
if (chunk.getZ() * 16 + z < island.getMinProtectedZ() || chunk.getZ() * 16 + z >= island.getMinProtectedZ() + island.getProtectionRange() * 2) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (int y = 0; y < Objects.requireNonNull(island.getCenter().getWorld()).getMaxHeight(); y++) {
|
|
||||||
Material blockData = chunk.getBlockType(x, y, z);
|
|
||||||
// Air is free
|
|
||||||
if (!blockData.equals(Material.AIR)) {
|
|
||||||
checkBlock(blockData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkBlock(Material md) {
|
|
||||||
md = bll.fixMaterial(md);
|
|
||||||
// md is limited
|
|
||||||
if (bll.getMaterialLimits(world, island.getUniqueId()).containsKey(md)) {
|
|
||||||
if (!blockCount.containsKey(md)) {
|
|
||||||
blockCount.put(md, new AtomicInteger(1));
|
|
||||||
} else {
|
|
||||||
blockCount.get(md).getAndIncrement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<Pair<Integer, Integer>> getChunksToScan(Island island) {
|
|
||||||
Set<Pair<Integer, Integer>> chunkSnapshot = new HashSet<>();
|
|
||||||
for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + island.getProtectionRange() * 2 + 16); x += 16) {
|
|
||||||
for (int z = island.getMinProtectedZ(); z < (island.getMinProtectedZ() + island.getProtectionRange() * 2 + 16); z += 16) {
|
|
||||||
Pair<Integer, Integer> pair = new Pair<>(world.getBlockAt(x, 0, z).getChunk().getX(), world.getBlockAt(x, 0, z).getChunk().getZ());
|
|
||||||
chunkSnapshot.add(pair);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return chunkSnapshot;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tidyUp() {
|
|
||||||
if (ibc == null) {
|
|
||||||
ibc = new IslandBlockCount();
|
|
||||||
}
|
|
||||||
ibc.setBlockCount(blockCount.entrySet().stream()
|
|
||||||
.collect(Collectors.toMap(
|
|
||||||
Map.Entry::getKey,
|
|
||||||
entry -> entry.getValue().get())));
|
|
||||||
bll.setIsland(island.getUniqueId(), ibc);
|
|
||||||
Bukkit.getScheduler().runTask(addon.getPlugin(), () -> sender.sendMessage("admin.limits.calc.finished"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,14 +1,17 @@
|
|||||||
package world.bentobox.limits.commands;
|
package world.bentobox.limits.commands.admin;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.bentobox.util.Util;
|
import world.bentobox.bentobox.util.Util;
|
||||||
import world.bentobox.limits.Limits;
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.commands.player.LimitPanel;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Admin command for limits
|
* Admin command for limits
|
||||||
@ -26,7 +29,9 @@ public class AdminCommand extends CompositeCommand {
|
|||||||
public AdminCommand(Limits addon, CompositeCommand parent) {
|
public AdminCommand(Limits addon, CompositeCommand parent) {
|
||||||
super(parent, "limits");
|
super(parent, "limits");
|
||||||
this.addon = addon;
|
this.addon = addon;
|
||||||
new CalcCommand(addon, this);
|
|
||||||
|
new CalcCommand(this.addon, this);
|
||||||
|
new OffsetCommand(this.addon, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
@ -53,7 +58,7 @@ public class AdminCommand extends CompositeCommand {
|
|||||||
user.sendMessage("general.errors.unknown-player", args.get(0));
|
user.sendMessage("general.errors.unknown-player", args.get(0));
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
new LimitPanel(addon).showLimits(getWorld(), user, playerUUID);
|
new LimitPanel(addon).showLimits((GameModeAddon)getAddon(), user, playerUUID);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
@ -1,4 +1,4 @@
|
|||||||
package world.bentobox.limits.commands;
|
package world.bentobox.limits.commands.admin;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -7,16 +7,19 @@ import java.util.UUID;
|
|||||||
|
|
||||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
import world.bentobox.bentobox.util.Util;
|
import world.bentobox.bentobox.util.Util;
|
||||||
import world.bentobox.limits.Limits;
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.calculators.Pipeliner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author YellowZaki
|
* @author YellowZaki, tastybento
|
||||||
*/
|
*/
|
||||||
public class CalcCommand extends CompositeCommand {
|
public class CalcCommand extends CompositeCommand {
|
||||||
|
|
||||||
private final Limits addon;
|
private final Limits addon;
|
||||||
|
private Island island;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Admin command
|
* Admin command
|
||||||
@ -49,10 +52,26 @@ public class CalcCommand extends CompositeCommand {
|
|||||||
if (playerUUID == null) {
|
if (playerUUID == null) {
|
||||||
user.sendMessage("general.errors.unknown-player", args.get(0));
|
user.sendMessage("general.errors.unknown-player", args.get(0));
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
island = addon.getIslands().getIsland(getWorld(), playerUUID);
|
||||||
|
if (island == null) {
|
||||||
|
user.sendMessage("general.errors.player-has-no-island");
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
//Calculate
|
//Calculate
|
||||||
calcLimits(playerUUID, user);
|
user.sendMessage("island.limits.recount.now-recounting");
|
||||||
|
new Pipeliner(addon).addIsland(island).thenAccept(results -> {
|
||||||
|
if (results == null) {
|
||||||
|
user.sendMessage("island.limits.recount.in-progress");
|
||||||
|
} else {
|
||||||
|
switch (results.getState()) {
|
||||||
|
case TIMEOUT -> user.sendMessage("admin.limits.calc.timeout");
|
||||||
|
default -> user.sendMessage("admin.limits.calc.finished");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
showHelp(this, user);
|
showHelp(this, user);
|
||||||
@ -60,13 +79,7 @@ public class CalcCommand extends CompositeCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void calcLimits(UUID targetPlayer, User sender) {
|
|
||||||
if (addon.getIslands().getIsland(getWorld(), targetPlayer) != null) {
|
|
||||||
new LimitsCalc(getWorld(), getPlugin(), targetPlayer, addon, sender);
|
|
||||||
} else {
|
|
||||||
sender.sendMessage("general.errors.player-has-no-island");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
@ -0,0 +1,695 @@
|
|||||||
|
//
|
||||||
|
// Created by BONNe
|
||||||
|
// Copyright - 2022
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package world.bentobox.limits.commands.admin;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.NamespacedKey;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.common.base.Enums;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||||
|
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||||
|
import world.bentobox.bentobox.api.user.User;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This command manages offsets to the player island limits.
|
||||||
|
*/
|
||||||
|
public class OffsetCommand extends CompositeCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Instantiates a new Offset command.
|
||||||
|
*
|
||||||
|
* @param addon the addon
|
||||||
|
* @param parent the parent
|
||||||
|
*/
|
||||||
|
public OffsetCommand(Limits addon, CompositeCommand parent)
|
||||||
|
{
|
||||||
|
super(parent, "offset");
|
||||||
|
|
||||||
|
new OffsetSetCommand(addon, this);
|
||||||
|
new OffsetAddCommand(addon, this);
|
||||||
|
new OffsetRemoveCommand(addon, this);
|
||||||
|
new OffsetResetCommand(addon, this);
|
||||||
|
new OffsetDisplayCommand(addon, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
this.setOnlyPlayer(false);
|
||||||
|
|
||||||
|
this.setPermission("admin.limits.offset");
|
||||||
|
this.setParametersHelp("admin.limits.offset.parameters");
|
||||||
|
this.setDescription("admin.limits.offset.description");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(User user, String s, List<String> list)
|
||||||
|
{
|
||||||
|
this.showHelp(this, user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This command allows setting limit offset for material or entity.
|
||||||
|
*/
|
||||||
|
private static class OffsetSetCommand extends CompositeCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Instantiates a new Offset set command.
|
||||||
|
*
|
||||||
|
* @param addon the addon
|
||||||
|
* @param parent the parent
|
||||||
|
*/
|
||||||
|
public OffsetSetCommand(Limits addon, CompositeCommand parent)
|
||||||
|
{
|
||||||
|
super(parent, "set");
|
||||||
|
this.addon = addon;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
this.setOnlyPlayer(false);
|
||||||
|
|
||||||
|
this.setPermission("admin.limits.offset.set");
|
||||||
|
this.setParametersHelp("admin.limits.offset.set.parameters");
|
||||||
|
this.setDescription("admin.limits.offset.set.description");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(User user, String label, List<String> args)
|
||||||
|
{
|
||||||
|
if (args.size() != 3)
|
||||||
|
{
|
||||||
|
// Show help
|
||||||
|
this.showHelp(this, user);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get target player
|
||||||
|
UUID targetUUID = Util.getUUID(args.get(0));
|
||||||
|
|
||||||
|
if (targetUUID == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Island island = this.getIslands().getIsland(this.getWorld(), targetUUID);
|
||||||
|
|
||||||
|
if (island == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.player-has-no-island");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IslandBlockCount islandData = this.addon.getBlockLimitListener().getIsland(island);
|
||||||
|
|
||||||
|
// Get new offset
|
||||||
|
if (!Util.isInteger(args.get(2), true))
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.must-be-a-number", TextVariables.NUMBER, args.get(2));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Material material = Material.matchMaterial(args.get(1));
|
||||||
|
EntityType entityType = matchEntity(args.get(1));
|
||||||
|
|
||||||
|
if (material == null && entityType == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("admin.limits.offset.unknown", TextVariables.NAME, args.get(1));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = Integer.parseInt(args.get(2));
|
||||||
|
|
||||||
|
if (material != null && offset == islandData.getBlockLimitOffset(material) ||
|
||||||
|
entityType != null && offset == islandData.getEntityLimitOffset(entityType))
|
||||||
|
{
|
||||||
|
user.sendMessage("admin.limits.offset.set.same",
|
||||||
|
TextVariables.NAME,
|
||||||
|
args.get(1),
|
||||||
|
TextVariables.NUMBER,
|
||||||
|
args.get(2));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (material != null)
|
||||||
|
{
|
||||||
|
islandData.setBlockLimitsOffset(material, offset);
|
||||||
|
islandData.setChanged();
|
||||||
|
|
||||||
|
user.sendMessage("admin.limits.offset.set.success",
|
||||||
|
TextVariables.NUMBER, String.valueOf(offset),
|
||||||
|
TextVariables.NAME, material.name());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
islandData.setEntityLimitsOffset(entityType, offset);
|
||||||
|
islandData.setChanged();
|
||||||
|
|
||||||
|
user.sendMessage("admin.limits.offset.set.success",
|
||||||
|
TextVariables.NUMBER, String.valueOf(offset),
|
||||||
|
TextVariables.NAME, entityType.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<List<String>> tabComplete(User user, String alias, List<String> args)
|
||||||
|
{
|
||||||
|
return OffsetCommand.craftTabComplete(user, alias, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of limits addon.
|
||||||
|
*/
|
||||||
|
private final Limits addon;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This command allows increasing limit offset for material or entity.
|
||||||
|
*/
|
||||||
|
private static class OffsetAddCommand extends CompositeCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Instantiates a new Offset add command.
|
||||||
|
*
|
||||||
|
* @param addon the addon
|
||||||
|
* @param parent the parent
|
||||||
|
*/
|
||||||
|
public OffsetAddCommand(Limits addon, CompositeCommand parent)
|
||||||
|
{
|
||||||
|
super(parent, "add");
|
||||||
|
this.addon = addon;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
this.setOnlyPlayer(false);
|
||||||
|
|
||||||
|
this.setPermission("admin.limits.offset.add");
|
||||||
|
this.setParametersHelp("admin.limits.offset.add.parameters");
|
||||||
|
this.setDescription("admin.limits.offset.add.description");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(User user, String label, List<String> args)
|
||||||
|
{
|
||||||
|
if (args.size() != 3)
|
||||||
|
{
|
||||||
|
// Show help
|
||||||
|
this.showHelp(this, user);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get target player
|
||||||
|
UUID targetUUID = Util.getUUID(args.get(0));
|
||||||
|
|
||||||
|
if (targetUUID == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Island island = this.getIslands().getIsland(this.getWorld(), targetUUID);
|
||||||
|
|
||||||
|
if (island == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.player-has-no-island");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IslandBlockCount islandData = this.addon.getBlockLimitListener().getIsland(island);
|
||||||
|
|
||||||
|
// Get new offset
|
||||||
|
if (!Util.isInteger(args.get(2), true) || Integer.parseInt(args.get(2)) < 0)
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(2));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Material material = Material.matchMaterial(args.get(1));
|
||||||
|
EntityType entityType = matchEntity(args.get(1));
|
||||||
|
|
||||||
|
if (material == null && entityType == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("admin.limits.offset.unknown", TextVariables.NAME, args.get(1));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = Integer.parseInt(args.get(2));
|
||||||
|
|
||||||
|
if (material != null)
|
||||||
|
{
|
||||||
|
offset += islandData.getBlockLimitOffset(material);
|
||||||
|
|
||||||
|
islandData.setBlockLimitsOffset(material, offset);
|
||||||
|
islandData.setChanged();
|
||||||
|
|
||||||
|
user.sendMessage("admin.limits.offset.add.success",
|
||||||
|
TextVariables.NUMBER, String.valueOf(offset),
|
||||||
|
TextVariables.NAME, material.name());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
offset += islandData.getEntityLimitOffset(entityType);
|
||||||
|
|
||||||
|
islandData.setEntityLimitsOffset(entityType, offset);
|
||||||
|
islandData.setChanged();
|
||||||
|
|
||||||
|
user.sendMessage("admin.limits.offset.add.success",
|
||||||
|
TextVariables.NUMBER, String.valueOf(offset),
|
||||||
|
TextVariables.NAME, entityType.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<List<String>> tabComplete(User user, String alias, List<String> args)
|
||||||
|
{
|
||||||
|
return OffsetCommand.craftTabComplete(user, alias, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of limits addon.
|
||||||
|
*/
|
||||||
|
private final Limits addon;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This command allows reducing limit offset for material or entity.
|
||||||
|
*/
|
||||||
|
private static class OffsetRemoveCommand extends CompositeCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Instantiates a new Offset remove command.
|
||||||
|
*
|
||||||
|
* @param addon the addon
|
||||||
|
* @param parent the parent
|
||||||
|
*/
|
||||||
|
public OffsetRemoveCommand(Limits addon, CompositeCommand parent)
|
||||||
|
{
|
||||||
|
super(parent, "remove");
|
||||||
|
this.addon = addon;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
this.setOnlyPlayer(false);
|
||||||
|
|
||||||
|
this.setPermission("admin.limits.offset.remove");
|
||||||
|
this.setParametersHelp("admin.limits.offset.remove.parameters");
|
||||||
|
this.setDescription("admin.limits.offset.remove.description");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(User user, String label, List<String> args)
|
||||||
|
{
|
||||||
|
if (args.size() != 3)
|
||||||
|
{
|
||||||
|
// Show help
|
||||||
|
this.showHelp(this, user);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get target player
|
||||||
|
UUID targetUUID = Util.getUUID(args.get(0));
|
||||||
|
|
||||||
|
if (targetUUID == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Island island = this.getIslands().getIsland(this.getWorld(), targetUUID);
|
||||||
|
|
||||||
|
if (island == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.player-has-no-island");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IslandBlockCount islandData = this.addon.getBlockLimitListener().getIsland(island);
|
||||||
|
|
||||||
|
// Get new offset
|
||||||
|
if (!Util.isInteger(args.get(2), true) || Integer.parseInt(args.get(2)) < 0)
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(2));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Material material = Material.matchMaterial(args.get(1));
|
||||||
|
EntityType entityType = matchEntity(args.get(1));
|
||||||
|
|
||||||
|
if (material == null && entityType == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("admin.limits.offset.unknown", TextVariables.NAME, args.get(1));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = Integer.parseInt(args.get(2));
|
||||||
|
|
||||||
|
if (material != null)
|
||||||
|
{
|
||||||
|
offset = islandData.getBlockLimitOffset(material) - offset;
|
||||||
|
|
||||||
|
islandData.setBlockLimitsOffset(material, offset);
|
||||||
|
islandData.setChanged();
|
||||||
|
|
||||||
|
user.sendMessage("admin.limits.offset.remove.success",
|
||||||
|
TextVariables.NUMBER, String.valueOf(offset),
|
||||||
|
TextVariables.NAME, material.name());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
offset = islandData.getEntityLimitOffset(entityType) - offset;
|
||||||
|
|
||||||
|
islandData.setEntityLimitsOffset(entityType, offset);
|
||||||
|
islandData.setChanged();
|
||||||
|
|
||||||
|
user.sendMessage("admin.limits.offset.remove.success",
|
||||||
|
TextVariables.NUMBER, String.valueOf(offset),
|
||||||
|
TextVariables.NAME, entityType.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<List<String>> tabComplete(User user, String alias, List<String> args)
|
||||||
|
{
|
||||||
|
return OffsetCommand.craftTabComplete(user, alias, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of limits addon.
|
||||||
|
*/
|
||||||
|
private final Limits addon;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This command allows resetting limit offset for material or entity.
|
||||||
|
*/
|
||||||
|
private static class OffsetResetCommand extends CompositeCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Instantiates a new Offset reset command.
|
||||||
|
*
|
||||||
|
* @param addon the addon
|
||||||
|
* @param parent the parent
|
||||||
|
*/
|
||||||
|
public OffsetResetCommand(Limits addon, CompositeCommand parent)
|
||||||
|
{
|
||||||
|
super(parent, "reset");
|
||||||
|
this.addon = addon;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
this.setOnlyPlayer(false);
|
||||||
|
|
||||||
|
this.setPermission("admin.limits.offset.reset");
|
||||||
|
this.setParametersHelp("admin.limits.offset.reset.parameters");
|
||||||
|
this.setDescription("admin.limits.offset.reset.description");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(User user, String label, List<String> args)
|
||||||
|
{
|
||||||
|
if (args.size() != 2)
|
||||||
|
{
|
||||||
|
// Show help
|
||||||
|
this.showHelp(this, user);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get target player
|
||||||
|
UUID targetUUID = Util.getUUID(args.get(0));
|
||||||
|
|
||||||
|
if (targetUUID == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Island island = this.getIslands().getIsland(this.getWorld(), targetUUID);
|
||||||
|
|
||||||
|
if (island == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.player-has-no-island");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IslandBlockCount islandData = this.addon.getBlockLimitListener().getIsland(island);
|
||||||
|
|
||||||
|
Material material = Material.matchMaterial(args.get(1));
|
||||||
|
EntityType entityType = matchEntity(args.get(1));
|
||||||
|
|
||||||
|
if (material == null && entityType == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("admin.limits.offset.unknown", TextVariables.NAME, args.get(1));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (material != null)
|
||||||
|
{
|
||||||
|
islandData.setBlockLimitsOffset(material, 0);
|
||||||
|
islandData.setChanged();
|
||||||
|
|
||||||
|
user.sendMessage("admin.limits.offset.reset.success",
|
||||||
|
TextVariables.NAME, material.name());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
islandData.setEntityLimitsOffset(entityType, 0);
|
||||||
|
islandData.setChanged();
|
||||||
|
|
||||||
|
user.sendMessage("admin.limits.offset.reset.success",
|
||||||
|
TextVariables.NAME, entityType.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<List<String>> tabComplete(User user, String alias, List<String> args)
|
||||||
|
{
|
||||||
|
return OffsetCommand.craftTabComplete(user, alias, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of limits addon.
|
||||||
|
*/
|
||||||
|
private final Limits addon;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This command allows viewing limit offset for material or entity.
|
||||||
|
*/
|
||||||
|
private static class OffsetDisplayCommand extends CompositeCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Instantiates a new Offset display command.
|
||||||
|
*
|
||||||
|
* @param addon the addon
|
||||||
|
* @param parent the parent
|
||||||
|
*/
|
||||||
|
public OffsetDisplayCommand(Limits addon, CompositeCommand parent)
|
||||||
|
{
|
||||||
|
super(parent, "view", "display");
|
||||||
|
this.addon = addon;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
this.setOnlyPlayer(false);
|
||||||
|
|
||||||
|
this.setPermission("admin.limits.offset.view");
|
||||||
|
this.setParametersHelp("admin.limits.offset.view.parameters");
|
||||||
|
this.setDescription("admin.limits.offset.view.description");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(User user, String label, List<String> args)
|
||||||
|
{
|
||||||
|
if (args.size() != 2)
|
||||||
|
{
|
||||||
|
// Show help
|
||||||
|
this.showHelp(this, user);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get target player
|
||||||
|
UUID targetUUID = Util.getUUID(args.get(0));
|
||||||
|
|
||||||
|
if (targetUUID == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Island island = this.getIslands().getIsland(this.getWorld(), targetUUID);
|
||||||
|
|
||||||
|
if (island == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.player-has-no-island");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IslandBlockCount islandData = this.addon.getBlockLimitListener().getIsland(island);
|
||||||
|
|
||||||
|
Material material = Material.matchMaterial(args.get(1));
|
||||||
|
EntityType entityType = matchEntity(args.get(1));
|
||||||
|
|
||||||
|
if (material == null && entityType == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("admin.limits.offset.unknown", TextVariables.NAME, args.get(1));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (material != null)
|
||||||
|
{
|
||||||
|
int offset = islandData.getBlockLimitOffset(material);
|
||||||
|
user.sendMessage("admin.limits.offset.view.message",
|
||||||
|
TextVariables.NAME, material.name(),
|
||||||
|
TextVariables.NUMBER, String.valueOf(offset));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int offset = islandData.getEntityLimitOffset(entityType);
|
||||||
|
user.sendMessage("admin.limits.offset.view.message",
|
||||||
|
TextVariables.NAME, entityType.name(),
|
||||||
|
TextVariables.NUMBER, String.valueOf(offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<List<String>> tabComplete(User user, String alias, List<String> args)
|
||||||
|
{
|
||||||
|
return OffsetCommand.craftTabComplete(user, alias, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of limits addon.
|
||||||
|
*/
|
||||||
|
private final Limits addon;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This material matches name to an entity type.
|
||||||
|
* @param name Name that must be matched.
|
||||||
|
* @return EntityType or null.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private static EntityType matchEntity(String name)
|
||||||
|
{
|
||||||
|
String filtered = name;
|
||||||
|
|
||||||
|
if (filtered.startsWith(NamespacedKey.MINECRAFT + ":"))
|
||||||
|
{
|
||||||
|
filtered = filtered.substring((NamespacedKey.MINECRAFT + ":").length());
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered = filtered.toUpperCase(java.util.Locale.ENGLISH);
|
||||||
|
filtered = filtered.replaceAll("\\s+", "_").replaceAll("\\W", "");
|
||||||
|
|
||||||
|
return Enums.getIfPresent(EntityType.class, filtered).orNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method crafts tab complete for all subcommands
|
||||||
|
* @param user User who runs command.
|
||||||
|
* @param alias Command alias.
|
||||||
|
* @param args List of args.
|
||||||
|
* @return Optional list of strings.
|
||||||
|
*/
|
||||||
|
private static Optional<List<String>> craftTabComplete(User user, String alias, List<String> args)
|
||||||
|
{
|
||||||
|
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
|
||||||
|
|
||||||
|
if (args.isEmpty())
|
||||||
|
{
|
||||||
|
// Don't show every player on the server. Require at least the first letter
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
else if (args.size() == 4)
|
||||||
|
{
|
||||||
|
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
|
||||||
|
return Optional.of(Util.tabLimit(options, lastArg));
|
||||||
|
}
|
||||||
|
else if (args.size() == 5)
|
||||||
|
{
|
||||||
|
List<String> options = Arrays.stream(Material.values()).
|
||||||
|
map(Enum::name).
|
||||||
|
collect(Collectors.toList());
|
||||||
|
|
||||||
|
options.addAll(Arrays.stream(EntityType.values()).
|
||||||
|
map(Enum::name).
|
||||||
|
collect(Collectors.toList()));
|
||||||
|
|
||||||
|
return Optional.of(Util.tabLimit(options, lastArg));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package world.bentobox.limits.commands.player;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||||
|
import world.bentobox.bentobox.api.panels.builders.TabbedPanelBuilder;
|
||||||
|
import world.bentobox.bentobox.api.user.User;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a panel of the blocks that are limited and their status
|
||||||
|
* @author tastybento
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LimitPanel {
|
||||||
|
|
||||||
|
private final Limits addon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param addon - limit addon
|
||||||
|
*/
|
||||||
|
public LimitPanel(Limits addon) {
|
||||||
|
this.addon = addon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the limits panel
|
||||||
|
* @param gm - game mode
|
||||||
|
* @param user - user asking
|
||||||
|
* @param target - target uuid
|
||||||
|
*/
|
||||||
|
public void showLimits(GameModeAddon gm, User user, UUID target) {
|
||||||
|
// Get world
|
||||||
|
World world = gm.getOverWorld();
|
||||||
|
// Get the island for the target
|
||||||
|
Island island = addon.getIslands().getIsland(world, target);
|
||||||
|
if (island == null) {
|
||||||
|
if (user.getUniqueId().equals(target)) {
|
||||||
|
user.sendMessage("general.errors.no-island");
|
||||||
|
} else {
|
||||||
|
user.sendMessage("general.errors.player-has-no-island");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// See if the target is online
|
||||||
|
Player targetPlayer = Bukkit.getPlayer(target);
|
||||||
|
if (targetPlayer != null) {
|
||||||
|
// Update perms
|
||||||
|
addon.getJoinListener().checkPerms(targetPlayer, gm.getPermissionPrefix() + "island.limit.", island.getUniqueId(), gm.getDescription().getName());
|
||||||
|
}
|
||||||
|
// Get the limits for this island
|
||||||
|
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(island.getUniqueId());
|
||||||
|
Map<Material, Integer> matLimits = addon.getBlockLimitListener().getMaterialLimits(world, island.getUniqueId());
|
||||||
|
if (matLimits.isEmpty() && addon.getSettings().getLimits().isEmpty()) {
|
||||||
|
user.sendMessage("island.limits.no-limits");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new TabbedPanelBuilder()
|
||||||
|
.user(user)
|
||||||
|
.world(world)
|
||||||
|
.tab(0, new LimitTab(addon, ibc, matLimits, island, world, user, LimitTab.SORT_BY.A2Z))
|
||||||
|
.tab(1, new LimitTab(addon, ibc, matLimits, island, world, user, LimitTab.SORT_BY.Z2A))
|
||||||
|
.startingSlot(0)
|
||||||
|
.size(54)
|
||||||
|
.build().openPanel();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,269 @@
|
|||||||
|
package world.bentobox.limits.commands.player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||||
|
import world.bentobox.bentobox.api.panels.PanelItem;
|
||||||
|
import world.bentobox.bentobox.api.panels.Tab;
|
||||||
|
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
|
||||||
|
import world.bentobox.bentobox.api.user.User;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
import world.bentobox.limits.EntityGroup;
|
||||||
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tastybento
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LimitTab implements Tab {
|
||||||
|
|
||||||
|
enum SORT_BY {
|
||||||
|
A2Z,
|
||||||
|
Z2A
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This maps the entity types to the icon that should be shown in the panel
|
||||||
|
* If the icon is null, then the entity type is not covered by the addon
|
||||||
|
*/
|
||||||
|
private static final Map<EntityType, Material> E2M = ImmutableMap.<EntityType, Material>builder()
|
||||||
|
.put(EntityType.MOOSHROOM, Material.MOOSHROOM_SPAWN_EGG).put(EntityType.SNOW_GOLEM, Material.SNOW_BLOCK)
|
||||||
|
.put(EntityType.IRON_GOLEM, Material.IRON_BLOCK)
|
||||||
|
.put(EntityType.ILLUSIONER, Material.VILLAGER_SPAWN_EGG)
|
||||||
|
.put(EntityType.WITHER, Material.WITHER_SKELETON_SKULL)
|
||||||
|
//.put(EntityType.BOAT, Material.OAK_BOAT)
|
||||||
|
.put(EntityType.ARMOR_STAND, Material.ARMOR_STAND)
|
||||||
|
.put(EntityType.ITEM_FRAME, Material.ITEM_FRAME)
|
||||||
|
.put(EntityType.PAINTING, Material.PAINTING)
|
||||||
|
// Minecarts
|
||||||
|
.put(EntityType.TNT_MINECART, Material.TNT_MINECART).put(EntityType.CHEST_MINECART, Material.CHEST_MINECART)
|
||||||
|
.put(EntityType.COMMAND_BLOCK_MINECART, Material.COMMAND_BLOCK_MINECART)
|
||||||
|
.put(EntityType.FURNACE_MINECART, Material.FURNACE_MINECART)
|
||||||
|
.put(EntityType.HOPPER_MINECART, Material.HOPPER_MINECART)
|
||||||
|
.put(EntityType.SPAWNER_MINECART, Material.MINECART)
|
||||||
|
//.put(EntityType.CHEST_BOAT, Material.OAK_CHEST_BOAT)
|
||||||
|
.build();
|
||||||
|
// This is a map of blocks to Items
|
||||||
|
private static final Map<Material, Material> B2M;
|
||||||
|
static {
|
||||||
|
ImmutableMap.Builder<Material, Material> builder = ImmutableMap.<Material, Material>builder()
|
||||||
|
.put(Material.POTATOES, Material.POTATO)
|
||||||
|
.put(Material.CARROTS, Material.CARROT)
|
||||||
|
.put(Material.BEETROOTS, Material.BEETROOT)
|
||||||
|
.put(Material.REDSTONE_WIRE, Material.REDSTONE).put(Material.MELON_STEM, Material.MELON)
|
||||||
|
.put(Material.PUMPKIN_STEM, Material.PUMPKIN);
|
||||||
|
// Block to Material icons
|
||||||
|
Optional.ofNullable(Material.getMaterial("SWEET_BERRY_BUSH")).ifPresent(material -> builder.put(material, Objects.requireNonNull(Material.getMaterial("SWEET_BERRIES"))));
|
||||||
|
Optional.ofNullable(Material.getMaterial("BAMBOO_SAPLING")).ifPresent(material -> builder.put(material, Objects.requireNonNull(Material.getMaterial("BAMBOO"))));
|
||||||
|
B2M = builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final World world;
|
||||||
|
private final User user;
|
||||||
|
private final Limits addon;
|
||||||
|
private final List<@Nullable PanelItem> result;
|
||||||
|
private final SORT_BY sortBy;
|
||||||
|
|
||||||
|
public LimitTab(Limits addon, IslandBlockCount ibc, Map<Material, Integer> matLimits, Island island, World world, User user, SORT_BY sortBy) {
|
||||||
|
this.addon = addon;
|
||||||
|
this.world = world;
|
||||||
|
this.user = user;
|
||||||
|
this.sortBy = sortBy;
|
||||||
|
result = new ArrayList<>();
|
||||||
|
addMaterialIcons(ibc, matLimits);
|
||||||
|
addEntityLimits(ibc, island);
|
||||||
|
addEntityGroupLimits(ibc, island);
|
||||||
|
// Sort
|
||||||
|
if (sortBy == SORT_BY.Z2A) {
|
||||||
|
result.sort((o1, o2) -> o2.getName().compareTo(o1.getName()));
|
||||||
|
} else {
|
||||||
|
result.sort(Comparator.comparing(PanelItem::getName));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addEntityGroupLimits(IslandBlockCount ibc, Island island) {
|
||||||
|
// Entity group limits
|
||||||
|
Map<EntityGroup, Integer> groupMap = addon.getSettings().getGroupLimitDefinitions().stream().collect(Collectors.toMap(e -> e, EntityGroup::getLimit));
|
||||||
|
// Group by same loop up map
|
||||||
|
Map<String, EntityGroup> groupByName = groupMap.keySet().stream().collect(Collectors.toMap(EntityGroup::getName, e -> e));
|
||||||
|
// Merge in any permission-based limits
|
||||||
|
if (ibc == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ibc.getEntityGroupLimits().entrySet().stream()
|
||||||
|
.filter(e -> groupByName.containsKey(e.getKey()))
|
||||||
|
.forEach(e -> groupMap.put(groupByName.get(e.getKey()), e.getValue()));
|
||||||
|
// Update the group map for each group limit offset. If the value already exists add it
|
||||||
|
ibc.getEntityGroupLimitsOffset().forEach((key, value) -> {
|
||||||
|
if (groupByName.get(key) != null) {
|
||||||
|
groupMap.put(groupByName.get(key), (groupMap.getOrDefault(groupByName.get(key), 0) + value));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
groupMap.forEach((v, limit) -> {
|
||||||
|
PanelItemBuilder pib = new PanelItemBuilder();
|
||||||
|
pib.name(user.getTranslation("island.limits.panel.entity-group-name-syntax", TextVariables.NAME,
|
||||||
|
v.getName()));
|
||||||
|
String description = "";
|
||||||
|
description += "(" + prettyNames(v) + ")\n";
|
||||||
|
pib.icon(v.getIcon());
|
||||||
|
long count = getCount(island, v);
|
||||||
|
String color = count >= limit ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color");
|
||||||
|
description += color
|
||||||
|
+ user.getTranslation("island.limits.block-limit-syntax",
|
||||||
|
TextVariables.NUMBER, String.valueOf(count),
|
||||||
|
"[limit]", String.valueOf(limit));
|
||||||
|
pib.description(description);
|
||||||
|
result.add(pib.build());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addEntityLimits(IslandBlockCount ibc, Island island) {
|
||||||
|
// Entity limits
|
||||||
|
Map<EntityType, Integer> map = new HashMap<>(addon.getSettings().getLimits());
|
||||||
|
// Merge in any permission-based limits
|
||||||
|
if (ibc != null) {
|
||||||
|
map.putAll(ibc.getEntityLimits());
|
||||||
|
ibc.getEntityLimitsOffset().forEach((k,v) -> map.put(k, map.getOrDefault(k, 0) + v));
|
||||||
|
}
|
||||||
|
|
||||||
|
map.forEach((k,v) -> {
|
||||||
|
PanelItemBuilder pib = new PanelItemBuilder();
|
||||||
|
pib.name(user.getTranslation("island.limits.panel.entity-name-syntax", TextVariables.NAME,
|
||||||
|
Util.prettifyText(k.toString())));
|
||||||
|
Material m;
|
||||||
|
try {
|
||||||
|
if (E2M.containsKey(k)) {
|
||||||
|
m = E2M.get(k);
|
||||||
|
} else if (k.isAlive()) {
|
||||||
|
m = Material.valueOf(k + "_SPAWN_EGG");
|
||||||
|
} else {
|
||||||
|
// Regular material
|
||||||
|
m = Material.valueOf(k.toString());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
m = Material.BARRIER;
|
||||||
|
}
|
||||||
|
pib.icon(m);
|
||||||
|
long count = getCount(island, k);
|
||||||
|
String color = count >= v ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color");
|
||||||
|
pib.description(color
|
||||||
|
+ user.getTranslation("island.limits.block-limit-syntax",
|
||||||
|
TextVariables.NUMBER, String.valueOf(count),
|
||||||
|
"[limit]", String.valueOf(v)));
|
||||||
|
result.add(pib.build());
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addMaterialIcons(IslandBlockCount ibc, Map<Material, Integer> matLimits) {
|
||||||
|
// Material limits
|
||||||
|
for (Entry<Material, Integer> en : matLimits.entrySet()) {
|
||||||
|
PanelItemBuilder pib = new PanelItemBuilder();
|
||||||
|
pib.name(user.getTranslation("island.limits.panel.block-name-syntax", TextVariables.NAME,
|
||||||
|
Util.prettifyText(en.getKey().toString())));
|
||||||
|
// Adjust icon
|
||||||
|
pib.icon(B2M.getOrDefault(en.getKey(), en.getKey()));
|
||||||
|
|
||||||
|
int count = ibc == null ? 0 : ibc.getBlockCounts().getOrDefault(en.getKey(), 0);
|
||||||
|
int value = en.getValue();
|
||||||
|
String color = count >= value ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color");
|
||||||
|
pib.description(color
|
||||||
|
+ user.getTranslation("island.limits.block-limit-syntax",
|
||||||
|
TextVariables.NUMBER, String.valueOf(count),
|
||||||
|
"[limit]", String.valueOf(value)));
|
||||||
|
result.add(pib.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PanelItem getIcon() {
|
||||||
|
return new PanelItemBuilder().icon(Material.MAGENTA_GLAZED_TERRACOTTA).name(this.getName()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
String sort = user.getTranslation(world, "island.limits.panel." + sortBy);
|
||||||
|
return user.getTranslation(world, "island.limits.panel.title-syntax", "[title]",
|
||||||
|
user.getTranslation(world, "limits.panel-title"), "[sort]", sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<@Nullable PanelItem> getPanelItems() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPermission() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String prettyNames(EntityGroup v) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
List<EntityType> l = new ArrayList<>(v.getTypes());
|
||||||
|
for(int i = 0; i < l.size(); i++)
|
||||||
|
{
|
||||||
|
sb.append(Util.prettifyText(l.get(i).toString()));
|
||||||
|
if (i + 1 < l.size())
|
||||||
|
sb.append(", ");
|
||||||
|
if((i+1) % 5 == 0)
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
long getCount(Island island, EntityType ent) {
|
||||||
|
long count = island.getWorld().getEntities().stream()
|
||||||
|
.filter(e -> e.getType().equals(ent))
|
||||||
|
.filter(e -> island.inIslandSpace(e.getLocation())).count();
|
||||||
|
// Nether
|
||||||
|
if (addon.getPlugin().getIWM().isNetherIslands(island.getWorld()) && addon.getPlugin().getIWM().getNetherWorld(island.getWorld()) != null) {
|
||||||
|
count += addon.getPlugin().getIWM().getNetherWorld(island.getWorld()).getEntities().stream()
|
||||||
|
.filter(e -> e.getType().equals(ent))
|
||||||
|
.filter(e -> island.inIslandSpace(e.getLocation())).count();
|
||||||
|
}
|
||||||
|
// End
|
||||||
|
if (addon.getPlugin().getIWM().isEndIslands(island.getWorld()) && addon.getPlugin().getIWM().getEndWorld(island.getWorld()) != null) {
|
||||||
|
count += addon.getPlugin().getIWM().getEndWorld(island.getWorld()).getEntities().stream()
|
||||||
|
.filter(e -> e.getType().equals(ent))
|
||||||
|
.filter(e -> island.inIslandSpace(e.getLocation())).count();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
long getCount(Island island, EntityGroup group) {
|
||||||
|
long count = island.getWorld().getEntities().stream()
|
||||||
|
.filter(e -> group.contains(e.getType()))
|
||||||
|
.filter(e -> island.inIslandSpace(e.getLocation())).count();
|
||||||
|
// Nether
|
||||||
|
if (addon.getPlugin().getIWM().isNetherIslands(island.getWorld()) && addon.getPlugin().getIWM().getNetherWorld(island.getWorld()) != null) {
|
||||||
|
count += addon.getPlugin().getIWM().getNetherWorld(island.getWorld()).getEntities().stream()
|
||||||
|
.filter(e -> group.contains(e.getType()))
|
||||||
|
.filter(e -> island.inIslandSpace(e.getLocation())).count();
|
||||||
|
}
|
||||||
|
// End
|
||||||
|
if (addon.getPlugin().getIWM().isEndIslands(island.getWorld()) && addon.getPlugin().getIWM().getEndWorld(island.getWorld()) != null) {
|
||||||
|
count += addon.getPlugin().getIWM().getEndWorld(island.getWorld()).getEntities().stream()
|
||||||
|
.filter(e -> group.contains(e.getType()))
|
||||||
|
.filter(e -> island.inIslandSpace(e.getLocation())).count();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,12 @@
|
|||||||
package world.bentobox.limits.commands;
|
package world.bentobox.limits.commands.player;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
import world.bentobox.limits.Limits;
|
import world.bentobox.limits.Limits;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,7 +48,22 @@ public class PlayerCommand extends CompositeCommand {
|
|||||||
showHelp(this, user);
|
showHelp(this, user);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
new LimitPanel(addon).showLimits(getWorld(), user, user.getUniqueId());
|
// Report the limit for the island, which is governed by the owner of the island
|
||||||
|
Optional<Island> opIsland = getIslands().getIslandAt(user.getLocation());
|
||||||
|
if (opIsland.isEmpty()) {
|
||||||
|
user.sendMessage("island.limits.errors.not-on-island");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Island island = opIsland.get();
|
||||||
|
if (!island.getWorld().equals(getWorld())) {
|
||||||
|
user.sendMessage("general.errors.wrong-world");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (island.getOwner() == null) {
|
||||||
|
user.sendMessage("island.limits.errors.no-owner");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
new LimitPanel(addon).showLimits((GameModeAddon) getAddon(), user, island.getOwner());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,10 +1,14 @@
|
|||||||
package world.bentobox.limits.commands;
|
package world.bentobox.limits.commands.player;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
import world.bentobox.limits.Limits;
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.calculators.Pipeliner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -13,6 +17,7 @@ import world.bentobox.limits.Limits;
|
|||||||
public class RecountCommand extends CompositeCommand {
|
public class RecountCommand extends CompositeCommand {
|
||||||
|
|
||||||
private final Limits addon;
|
private final Limits addon;
|
||||||
|
private @Nullable Island island;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Player command to do a recount. Has a cooldown
|
* Player command to do a recount. Has a cooldown
|
||||||
@ -44,7 +49,8 @@ public class RecountCommand extends CompositeCommand {
|
|||||||
showHelp(this, user);
|
showHelp(this, user);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (addon.getIslands().getIsland(getWorld(), user) == null) {
|
island = addon.getIslands().getIsland(getWorld(), user);
|
||||||
|
if (island == null) {
|
||||||
user.sendMessage("general.errors.no-island");
|
user.sendMessage("general.errors.no-island");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -54,7 +60,17 @@ public class RecountCommand extends CompositeCommand {
|
|||||||
public boolean execute(User user, String label, List<String> args) {
|
public boolean execute(User user, String label, List<String> args) {
|
||||||
// Set cooldown
|
// Set cooldown
|
||||||
setCooldown(user.getUniqueId(), addon.getConfig().getInt("cooldown", 120));
|
setCooldown(user.getUniqueId(), addon.getConfig().getInt("cooldown", 120));
|
||||||
new LimitsCalc(getWorld(), getPlugin(), user.getUniqueId(), addon, user);
|
user.sendMessage("island.limits.recount.now-recounting");
|
||||||
|
new Pipeliner(addon).addIsland(island).thenAccept(results -> {
|
||||||
|
if (results == null) {
|
||||||
|
user.sendMessage("island.limits.recount.in-progress");
|
||||||
|
} else {
|
||||||
|
switch (results.getState()) {
|
||||||
|
case TIMEOUT -> user.sendMessage("admin.limits.calc.timeout");
|
||||||
|
default -> user.sendMessage("admin.limits.calc.finished");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
|||||||
|
package world.bentobox.limits.events;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.Cancellable;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.api.events.BentoBoxEvent;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a player joins the server and before limit settings for their island are changed based
|
||||||
|
* on the player's permissions. If cancelled, no limit settings will be made.
|
||||||
|
* @author tastybento
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LimitsJoinPermCheckEvent extends BentoBoxEvent implements Cancellable {
|
||||||
|
|
||||||
|
private final Player player;
|
||||||
|
private final String islandId;
|
||||||
|
private IslandBlockCount ibc;
|
||||||
|
private boolean cancel;
|
||||||
|
private boolean ignorePerms;
|
||||||
|
private static final HandlerList handlers = new HandlerList();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull HandlerList getHandlers() {
|
||||||
|
return getHandlerList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HandlerList getHandlerList() {
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a player joins the server and before limit settings for their island are changed based
|
||||||
|
* on the player's permissions. If cancelled, no limit settings will be made.
|
||||||
|
* @param player - player joining
|
||||||
|
* @param islandId - the unique island id.
|
||||||
|
* @param ibc - IslandBlockCount object for this island
|
||||||
|
*/
|
||||||
|
public LimitsJoinPermCheckEvent(@NonNull Player player, @NonNull String islandId, @Nullable IslandBlockCount ibc) {
|
||||||
|
super();
|
||||||
|
this.player = player;
|
||||||
|
this.islandId = islandId;
|
||||||
|
this.ibc = ibc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the player joining
|
||||||
|
* @return the player
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Player getPlayer() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unique island id. Use the islands manager to obtain the island
|
||||||
|
* @return the islandId
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public String getIslandId() {
|
||||||
|
return islandId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the island block count
|
||||||
|
* @return the ibc
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public IslandBlockCount getIbc() {
|
||||||
|
return ibc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the island block count to a specific setting
|
||||||
|
* @param ibc the ibc to set
|
||||||
|
*/
|
||||||
|
public void setIbc(@Nullable IslandBlockCount ibc) {
|
||||||
|
this.ibc = ibc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCancelled(boolean cancel) {
|
||||||
|
this.cancel = cancel;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if player's perms should be considered or not
|
||||||
|
* @return the ignorePerms
|
||||||
|
*/
|
||||||
|
public boolean isIgnorePerms() {
|
||||||
|
return ignorePerms;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ignore player's perms. This differs to canceling the event in that the IslandBlockCount will be used if given via
|
||||||
|
* {@link #setIbc(IslandBlockCount ibc)}
|
||||||
|
* @param ignorePerms the ignorePerms to set
|
||||||
|
*/
|
||||||
|
public void setIgnorePerms(boolean ignorePerms) {
|
||||||
|
this.ignorePerms = ignorePerms;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,116 @@
|
|||||||
|
package world.bentobox.limits.events;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
import world.bentobox.limits.EntityGroup;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a player joins the server and for each perm-based limit setting.
|
||||||
|
* If cancelled, no limit settings will be made.
|
||||||
|
* Settings can be adjusted and will be used.
|
||||||
|
* @author tastybento
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LimitsPermCheckEvent extends LimitsJoinPermCheckEvent {
|
||||||
|
|
||||||
|
private @Nullable EntityGroup entityGroup;
|
||||||
|
private @Nullable EntityType entityType;
|
||||||
|
private @Nullable Material material;
|
||||||
|
private int value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a player joins the server and for each perm-based limit setting.
|
||||||
|
* If cancelled, no limit settings will be made.
|
||||||
|
* Settings can be adjusted and will be used.
|
||||||
|
* @param player - player joining
|
||||||
|
* @param islandId - the unique island id.
|
||||||
|
* @param ibc - IslandBlockCount object for this island
|
||||||
|
* @param material - material being limited, or null
|
||||||
|
* @param entityType - entity type being limited, or null
|
||||||
|
* @param entgroup - entity group being limited, or null
|
||||||
|
* @param value - numeric limit given by the perm
|
||||||
|
*/
|
||||||
|
public LimitsPermCheckEvent(@NonNull Player player,
|
||||||
|
@NonNull String islandId,
|
||||||
|
@Nullable IslandBlockCount ibc,
|
||||||
|
@Nullable EntityGroup entgroup,
|
||||||
|
@Nullable EntityType entityType,
|
||||||
|
@Nullable Material material,
|
||||||
|
int value) {
|
||||||
|
super(player, islandId, ibc);
|
||||||
|
this.entityGroup = entgroup;
|
||||||
|
this.entityType = entityType;
|
||||||
|
this.material = material;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the entityGroup
|
||||||
|
*/
|
||||||
|
public @Nullable EntityGroup getEntityGroup() {
|
||||||
|
return entityGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param entityGroup the entityGroup to set
|
||||||
|
*/
|
||||||
|
public void setEntityGroup(@Nullable EntityGroup entityGroup) {
|
||||||
|
this.entityGroup = entityGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the entityType
|
||||||
|
*/
|
||||||
|
public @Nullable EntityType getEntityType() {
|
||||||
|
return entityType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param entityType the entityType to set
|
||||||
|
*/
|
||||||
|
public void setEntityType(@Nullable EntityType entityType) {
|
||||||
|
this.entityType = entityType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the material
|
||||||
|
*/
|
||||||
|
public @Nullable Material getMaterial() {
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param material the material to set
|
||||||
|
*/
|
||||||
|
public void setMaterial(@Nullable Material material) {
|
||||||
|
this.material = material;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the value
|
||||||
|
*/
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param value the value to set
|
||||||
|
*/
|
||||||
|
public void setValue(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -15,6 +15,8 @@ import org.bukkit.Material;
|
|||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.block.data.type.TechnicalPiston;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
import org.bukkit.event.Cancellable;
|
import org.bukkit.event.Cancellable;
|
||||||
import org.bukkit.event.Event;
|
import org.bukkit.event.Event;
|
||||||
@ -28,6 +30,7 @@ import org.bukkit.event.block.BlockExplodeEvent;
|
|||||||
import org.bukkit.event.block.BlockFadeEvent;
|
import org.bukkit.event.block.BlockFadeEvent;
|
||||||
import org.bukkit.event.block.BlockFormEvent;
|
import org.bukkit.event.block.BlockFormEvent;
|
||||||
import org.bukkit.event.block.BlockFromToEvent;
|
import org.bukkit.event.block.BlockFromToEvent;
|
||||||
|
import org.bukkit.event.block.BlockGrowEvent;
|
||||||
import org.bukkit.event.block.BlockMultiPlaceEvent;
|
import org.bukkit.event.block.BlockMultiPlaceEvent;
|
||||||
import org.bukkit.event.block.BlockPlaceEvent;
|
import org.bukkit.event.block.BlockPlaceEvent;
|
||||||
import org.bukkit.event.block.BlockSpreadEvent;
|
import org.bukkit.event.block.BlockSpreadEvent;
|
||||||
@ -36,12 +39,14 @@ import org.bukkit.event.block.LeavesDecayEvent;
|
|||||||
import org.bukkit.event.entity.EntityChangeBlockEvent;
|
import org.bukkit.event.entity.EntityChangeBlockEvent;
|
||||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
import world.bentobox.bentobox.api.events.island.IslandEvent.IslandDeleteEvent;
|
import world.bentobox.bentobox.api.events.island.IslandDeleteEvent;
|
||||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.bentobox.database.Database;
|
import world.bentobox.bentobox.database.Database;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
import world.bentobox.bentobox.util.Util;
|
import world.bentobox.bentobox.util.Util;
|
||||||
import world.bentobox.limits.Limits;
|
import world.bentobox.limits.Limits;
|
||||||
import world.bentobox.limits.objects.IslandBlockCount;
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
@ -83,7 +88,7 @@ public class BlockLimitsListener implements Listener {
|
|||||||
handler.loadObjects().forEach(ibc -> {
|
handler.loadObjects().forEach(ibc -> {
|
||||||
// Clean up
|
// Clean up
|
||||||
if (addon.isCoveredGameMode(ibc.getGameMode())) {
|
if (addon.isCoveredGameMode(ibc.getGameMode())) {
|
||||||
ibc.getBlockCount().keySet().removeIf(DO_NOT_COUNT::contains);
|
ibc.getBlockCounts().keySet().removeIf(DO_NOT_COUNT::contains);
|
||||||
// Store
|
// Store
|
||||||
islandCountMap.put(ibc.getUniqueId(), ibc);
|
islandCountMap.put(ibc.getUniqueId(), ibc);
|
||||||
} else {
|
} else {
|
||||||
@ -146,7 +151,7 @@ public class BlockLimitsListener implements Listener {
|
|||||||
* Save the count database completely
|
* Save the count database completely
|
||||||
*/
|
*/
|
||||||
public void save() {
|
public void save() {
|
||||||
islandCountMap.values().forEach(handler::saveObject);
|
islandCountMap.values().stream().filter(IslandBlockCount::isChanged).forEach(handler::saveObjectAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Player-related events
|
// Player-related events
|
||||||
@ -162,29 +167,26 @@ public class BlockLimitsListener implements Listener {
|
|||||||
|
|
||||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
public void onTurtleEggBreak(PlayerInteractEvent e) {
|
public void onTurtleEggBreak(PlayerInteractEvent e) {
|
||||||
if (e.getAction().equals(Action.PHYSICAL) && e.getClickedBlock().getType().equals(Material.TURTLE_EGG)) {
|
if (e.getAction().equals(Action.PHYSICAL) && e.getClickedBlock() != null && e.getClickedBlock().getType().equals(Material.TURTLE_EGG)) {
|
||||||
handleBreak(e, e.getClickedBlock());
|
handleBreak(e, e.getClickedBlock());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBreak(Event e, Block b) {
|
private void handleBreak(Event e, Block b) {
|
||||||
Material mat = b.getType();
|
if (!addon.inGameModeWorld(b.getWorld())) {
|
||||||
// Special handling for crops that can break in different ways
|
return;
|
||||||
if (mat.equals(Material.WHEAT_SEEDS)) {
|
|
||||||
mat = Material.WHEAT;
|
|
||||||
} else if (mat.equals(Material.BEETROOT_SEEDS)) {
|
|
||||||
mat = Material.BEETROOT;
|
|
||||||
}
|
}
|
||||||
|
Material mat = b.getType();
|
||||||
// Check for stackable plants
|
// Check for stackable plants
|
||||||
if (STACKABLE.contains(b.getType())) {
|
if (STACKABLE.contains(b.getType())) {
|
||||||
// Check for blocks above
|
// Check for blocks above
|
||||||
Block block = b;
|
Block block = b;
|
||||||
while(block.getRelative(BlockFace.UP).getType().equals(mat) && block.getY() < b.getWorld().getMaxHeight()) {
|
while(block.getRelative(BlockFace.UP).getType().equals(mat) && block.getY() < b.getWorld().getMaxHeight()) {
|
||||||
block = block.getRelative(BlockFace.UP);
|
block = block.getRelative(BlockFace.UP);
|
||||||
process(block, false, mat);
|
process(block, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
process(b, false, mat);
|
process(b, false);
|
||||||
// Player breaks a block and there was a redstone dust/repeater/... above
|
// Player breaks a block and there was a redstone dust/repeater/... above
|
||||||
if (b.getRelative(BlockFace.UP).getType() == Material.REDSTONE_WIRE || b.getRelative(BlockFace.UP).getType() == Material.REPEATER || b.getRelative(BlockFace.UP).getType() == Material.COMPARATOR || b.getRelative(BlockFace.UP).getType() == Material.REDSTONE_TORCH) {
|
if (b.getRelative(BlockFace.UP).getType() == Material.REDSTONE_WIRE || b.getRelative(BlockFace.UP).getType() == Material.REPEATER || b.getRelative(BlockFace.UP).getType() == Material.COMPARATOR || b.getRelative(BlockFace.UP).getType() == Material.REDSTONE_TORCH) {
|
||||||
process(b.getRelative(BlockFace.UP), false);
|
process(b.getRelative(BlockFace.UP), false);
|
||||||
@ -208,6 +210,13 @@ public class BlockLimitsListener implements Listener {
|
|||||||
notify(e, User.getInstance(e.getPlayer()), process(e.getBlock(), true), e.getBlock().getType());
|
notify(e, User.getInstance(e.getPlayer()), process(e.getBlock(), true), e.getBlock().getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the event and notify the user of failure
|
||||||
|
* @param e event
|
||||||
|
* @param user user
|
||||||
|
* @param limit maximum limit allowed
|
||||||
|
* @param m material
|
||||||
|
*/
|
||||||
private void notify(Cancellable e, User user, int limit, Material m) {
|
private void notify(Cancellable e, User user, int limit, Material m) {
|
||||||
if (limit > -1) {
|
if (limit > -1) {
|
||||||
user.notify("block-limits.hit-limit",
|
user.notify("block-limits.hit-limit",
|
||||||
@ -248,6 +257,16 @@ public class BlockLimitsListener implements Listener {
|
|||||||
process(e.getBlock(), true);
|
process(e.getBlock(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
|
public void onBlock(BlockGrowEvent e) {
|
||||||
|
if (process(e.getNewState().getBlock(), true) > -1) {
|
||||||
|
e.setCancelled(true);
|
||||||
|
e.getBlock().getWorld().getBlockAt(e.getBlock().getLocation()).setBlockData(e.getBlock().getBlockData());
|
||||||
|
} else {
|
||||||
|
process(e.getBlock(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
public void onBlock(LeavesDecayEvent e) {
|
public void onBlock(LeavesDecayEvent e) {
|
||||||
process(e.getBlock(), false);
|
process(e.getBlock(), false);
|
||||||
@ -278,28 +297,37 @@ public class BlockLimitsListener implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int process(Block b, boolean add) {
|
/**
|
||||||
return process(b, add, b.getType());
|
* Return equivalents. Maps things like wall materials to their non-wall equivalents
|
||||||
}
|
* @param b block data
|
||||||
|
* @return material that matches the block data
|
||||||
|
*/
|
||||||
|
public Material fixMaterial(BlockData b) {
|
||||||
|
Material mat = b.getMaterial();
|
||||||
|
|
||||||
// Return equivalents.
|
if (mat == Material.REDSTONE_WALL_TORCH) {
|
||||||
public Material fixMaterial(Material b) {
|
|
||||||
if (b == Material.REDSTONE_WALL_TORCH) {
|
|
||||||
return Material.REDSTONE_TORCH;
|
return Material.REDSTONE_TORCH;
|
||||||
} else if (b == Material.WALL_TORCH) {
|
} else if (mat == Material.WALL_TORCH) {
|
||||||
return Material.TORCH;
|
return Material.TORCH;
|
||||||
} else if (b == Material.ZOMBIE_WALL_HEAD) {
|
} else if (mat == Material.ZOMBIE_WALL_HEAD) {
|
||||||
return Material.ZOMBIE_HEAD;
|
return Material.ZOMBIE_HEAD;
|
||||||
} else if (b == Material.CREEPER_WALL_HEAD) {
|
} else if (mat == Material.CREEPER_WALL_HEAD) {
|
||||||
return Material.CREEPER_HEAD;
|
return Material.CREEPER_HEAD;
|
||||||
} else if (b == Material.PLAYER_WALL_HEAD) {
|
} else if (mat == Material.PLAYER_WALL_HEAD) {
|
||||||
return Material.PLAYER_HEAD;
|
return Material.PLAYER_HEAD;
|
||||||
} else if (b == Material.DRAGON_WALL_HEAD) {
|
} else if (mat == Material.DRAGON_WALL_HEAD) {
|
||||||
return Material.DRAGON_HEAD;
|
return Material.DRAGON_HEAD;
|
||||||
} else if (b != null && b == Material.getMaterial("BAMBOO_SAPLING")) {
|
} else if (mat == Material.BAMBOO_SAPLING) {
|
||||||
return Material.getMaterial("BAMBOO");
|
return Material.BAMBOO;
|
||||||
|
} else if (mat == Material.PISTON_HEAD || mat == Material.MOVING_PISTON) {
|
||||||
|
TechnicalPiston tp = (TechnicalPiston) b;
|
||||||
|
if (tp.getType() == TechnicalPiston.Type.NORMAL) {
|
||||||
|
return Material.PISTON;
|
||||||
|
} else {
|
||||||
|
return Material.STICKY_PISTON;
|
||||||
}
|
}
|
||||||
return b;
|
}
|
||||||
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -307,11 +335,10 @@ public class BlockLimitsListener implements Listener {
|
|||||||
*
|
*
|
||||||
* @param b - block
|
* @param b - block
|
||||||
* @param add - true to add a block, false to remove
|
* @param add - true to add a block, false to remove
|
||||||
* @param changeTo - material this block will become
|
|
||||||
* @return limit amount if over limit, or -1 if no limitation
|
* @return limit amount if over limit, or -1 if no limitation
|
||||||
*/
|
*/
|
||||||
private int process(Block b, boolean add, Material changeTo) {
|
private int process(Block b, boolean add) {
|
||||||
if (DO_NOT_COUNT.contains(fixMaterial(b.getType())) || !addon.inGameModeWorld(b.getWorld())) {
|
if (DO_NOT_COUNT.contains(fixMaterial(b.getBlockData())) || !addon.inGameModeWorld(b.getWorld())) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// Check if on island
|
// Check if on island
|
||||||
@ -322,40 +349,54 @@ public class BlockLimitsListener implements Listener {
|
|||||||
// Invalid world
|
// Invalid world
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
// Ignore the center block - usually bedrock, but for AOneBlock it's the magic block
|
||||||
|
if (addon.getConfig().getBoolean("ignore-center-block", true) && i.getCenter().equals(b.getLocation())) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
islandCountMap.putIfAbsent(id, new IslandBlockCount(id, gameMode));
|
islandCountMap.putIfAbsent(id, new IslandBlockCount(id, gameMode));
|
||||||
saveMap.putIfAbsent(id, 0);
|
|
||||||
if (add) {
|
if (add) {
|
||||||
// Check limit
|
// Check limit
|
||||||
int limit = checkLimit(b.getWorld(), fixMaterial(b.getType()), id);
|
int limit = checkLimit(b.getWorld(), fixMaterial(b.getBlockData()), id);
|
||||||
if (limit > -1) {
|
if (limit > -1) {
|
||||||
return limit;
|
return limit;
|
||||||
}
|
}
|
||||||
islandCountMap.get(id).add(fixMaterial(b.getType()));
|
islandCountMap.get(id).add(fixMaterial(b.getBlockData()));
|
||||||
saveMap.merge(id, 1, Integer::sum);
|
|
||||||
} else {
|
} else {
|
||||||
if (islandCountMap.containsKey(id)) {
|
if (islandCountMap.containsKey(id)) {
|
||||||
// Check for changes
|
islandCountMap.get(id).remove(fixMaterial(b.getBlockData()));
|
||||||
Material fixed = fixMaterial(changeTo);
|
|
||||||
if (!fixed.equals(fixMaterial(b.getType())) && fixed.isBlock() && !DO_NOT_COUNT.contains(fixed)) {
|
|
||||||
// Check limit
|
|
||||||
int limit = checkLimit(b.getWorld(), fixed, id);
|
|
||||||
if (limit > -1) {
|
|
||||||
return limit;
|
|
||||||
}
|
|
||||||
islandCountMap.get(id).add(fixed);
|
|
||||||
}
|
|
||||||
islandCountMap.get(id).remove(fixMaterial(b.getType()));
|
|
||||||
saveMap.merge(id, 1, Integer::sum);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (saveMap.get(id) > CHANGE_LIMIT) {
|
updateSaveMap(id);
|
||||||
handler.saveObject(islandCountMap.get(id));
|
|
||||||
saveMap.remove(id);
|
|
||||||
}
|
|
||||||
return -1;
|
return -1;
|
||||||
}).orElse(-1);
|
}).orElse(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removed a block from any island limit count
|
||||||
|
* @param b - block to remove
|
||||||
|
*/
|
||||||
|
public void removeBlock(Block b) {
|
||||||
|
// Get island
|
||||||
|
addon.getIslands().getIslandAt(b.getLocation()).ifPresent(i -> {
|
||||||
|
String id = i.getUniqueId();
|
||||||
|
String gameMode = addon.getGameModeName(b.getWorld());
|
||||||
|
if (gameMode.isEmpty()) {
|
||||||
|
// Invalid world
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
islandCountMap.computeIfAbsent(id, k -> new IslandBlockCount(id, gameMode)).remove(fixMaterial(b.getBlockData()));
|
||||||
|
updateSaveMap(id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
private void updateSaveMap(String id) {
|
||||||
|
saveMap.putIfAbsent(id, 0);
|
||||||
|
if (saveMap.merge(id, 1, Integer::sum) > CHANGE_LIMIT) {
|
||||||
|
handler.saveObjectAsync(islandCountMap.get(id));
|
||||||
|
saveMap.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this material is at its limit for world on this island
|
* Check if this material is at its limit for world on this island
|
||||||
*
|
*
|
||||||
@ -366,18 +407,18 @@ public class BlockLimitsListener implements Listener {
|
|||||||
*/
|
*/
|
||||||
private int checkLimit(World w, Material m, String id) {
|
private int checkLimit(World w, Material m, String id) {
|
||||||
// Check island limits
|
// Check island limits
|
||||||
IslandBlockCount island = islandCountMap.get(id);
|
IslandBlockCount ibc = islandCountMap.get(id);
|
||||||
if (island.isBlockLimited(m)) {
|
if (ibc.isBlockLimited(m)) {
|
||||||
return island.isAtLimit(m) ? island.getBlockLimit(m) : -1;
|
return ibc.isAtLimit(m) ? ibc.getBlockLimit(m) + ibc.getBlockLimitOffset(m) : -1;
|
||||||
}
|
}
|
||||||
// Check specific world limits
|
// Check specific world limits
|
||||||
if (worldLimitMap.containsKey(w) && worldLimitMap.get(w).containsKey(m)) {
|
if (worldLimitMap.containsKey(w) && worldLimitMap.get(w).containsKey(m)) {
|
||||||
// Material is overridden in world
|
// Material is overridden in world
|
||||||
return island.isAtLimit(m, worldLimitMap.get(w).get(m)) ? worldLimitMap.get(w).get(m) : -1;
|
return ibc.isAtLimit(m, worldLimitMap.get(w).get(m)) ? worldLimitMap.get(w).get(m) + ibc.getBlockLimitOffset(m) : -1;
|
||||||
}
|
}
|
||||||
// Check default limit map
|
// Check default limit map
|
||||||
if (defaultLimitMap.containsKey(m) && island.isAtLimit(m, defaultLimitMap.get(m))) {
|
if (defaultLimitMap.containsKey(m) && ibc.isAtLimit(m, defaultLimitMap.get(m))) {
|
||||||
return defaultLimitMap.get(m);
|
return defaultLimitMap.get(m) + ibc.getBlockLimitOffset(m);
|
||||||
}
|
}
|
||||||
// No limit
|
// No limit
|
||||||
return -1;
|
return -1;
|
||||||
@ -394,14 +435,19 @@ public class BlockLimitsListener implements Listener {
|
|||||||
// Merge limits
|
// Merge limits
|
||||||
Map<Material, Integer> result = new EnumMap<>(Material.class);
|
Map<Material, Integer> result = new EnumMap<>(Material.class);
|
||||||
// Default
|
// Default
|
||||||
defaultLimitMap.forEach(result::put);
|
result.putAll(defaultLimitMap);
|
||||||
// World
|
// World
|
||||||
if (worldLimitMap.containsKey(w)) {
|
if (worldLimitMap.containsKey(w)) {
|
||||||
worldLimitMap.get(w).forEach(result::put);
|
result.putAll(worldLimitMap.get(w));
|
||||||
}
|
}
|
||||||
// Island
|
// Island
|
||||||
if (islandCountMap.containsKey(id)) {
|
if (islandCountMap.containsKey(id)) {
|
||||||
islandCountMap.get(id).getBlockLimits().forEach(result::put);
|
IslandBlockCount islandBlockCount = islandCountMap.get(id);
|
||||||
|
result.putAll(islandBlockCount.getBlockLimits());
|
||||||
|
|
||||||
|
// Add offsets to the every limit.
|
||||||
|
islandBlockCount.getBlockLimitsOffset().forEach((material, offset) ->
|
||||||
|
result.put(material, result.getOrDefault(material, 0) + offset));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -428,7 +474,7 @@ public class BlockLimitsListener implements Listener {
|
|||||||
*/
|
*/
|
||||||
public void setIsland(String islandId, IslandBlockCount ibc) {
|
public void setIsland(String islandId, IslandBlockCount ibc) {
|
||||||
islandCountMap.put(islandId, ibc);
|
islandCountMap.put(islandId, ibc);
|
||||||
handler.saveObject(ibc);
|
handler.saveObjectAsync(ibc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -442,4 +488,14 @@ public class BlockLimitsListener implements Listener {
|
|||||||
return islandCountMap.get(islandId);
|
return islandCountMap.get(islandId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the island block count for island and make one if it does not exist
|
||||||
|
* @param island island
|
||||||
|
* @return island block count
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public IslandBlockCount getIsland(Island island) {
|
||||||
|
return islandCountMap.computeIfAbsent(island.getUniqueId(), k -> new IslandBlockCount(k, island.getGameMode()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,53 @@
|
|||||||
package world.bentobox.limits.listeners;
|
package world.bentobox.limits.listeners;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.Tag;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.BlockFace;
|
||||||
|
import org.bukkit.entity.Breedable;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.entity.Villager;
|
||||||
|
import org.bukkit.event.Cancellable;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
||||||
|
import org.bukkit.event.entity.EntityBreedEvent;
|
||||||
import org.bukkit.event.hanging.HangingPlaceEvent;
|
import org.bukkit.event.hanging.HangingPlaceEvent;
|
||||||
import org.bukkit.event.vehicle.VehicleCreateEvent;
|
import org.bukkit.event.vehicle.VehicleCreateEvent;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.bentobox.database.objects.Island;
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
import world.bentobox.bentobox.util.Util;
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
import world.bentobox.limits.EntityGroup;
|
||||||
import world.bentobox.limits.Limits;
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.Settings;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
public class EntityLimitListener implements Listener {
|
public class EntityLimitListener implements Listener {
|
||||||
private static final String MOD_BYPASS = "mod.bypass";
|
private static final String MOD_BYPASS = "mod.bypass";
|
||||||
private final Limits addon;
|
private final Limits addon;
|
||||||
|
private final List<UUID> justSpawned = new ArrayList<>();
|
||||||
|
private static final List<BlockFace> CARDINALS = List.of(BlockFace.UP, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST, BlockFace.DOWN);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles entity and natural limitations
|
* Handles entity and natural limitations
|
||||||
@ -39,76 +64,65 @@ public class EntityLimitListener implements Listener {
|
|||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
public void onMinecart(VehicleCreateEvent e) {
|
public void onMinecart(VehicleCreateEvent e) {
|
||||||
// Return if not in a known world
|
// Return if not in a known world
|
||||||
if (!addon.getPlugin().getIWM().inWorld(e.getVehicle().getWorld())) {
|
if (!addon.inGameModeWorld(e.getVehicle().getWorld())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Debounce
|
||||||
|
if (justSpawned.contains(e.getVehicle().getUniqueId())) {
|
||||||
|
justSpawned.remove(e.getVehicle().getUniqueId());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If someone in that area has the bypass permission, allow the spawning
|
|
||||||
for (Entity entity : Objects.requireNonNull(e.getVehicle().getLocation().getWorld()).getNearbyEntities(e.getVehicle().getLocation(), 5, 5, 5)) {
|
|
||||||
if (entity instanceof Player) {
|
|
||||||
Player player = (Player)entity;
|
|
||||||
boolean bypass = (player.isOp() || player.hasPermission(addon.getPlugin().getIWM().getPermissionPrefix(e.getVehicle().getWorld()) + MOD_BYPASS));
|
|
||||||
// Check island
|
// Check island
|
||||||
addon.getIslands().getProtectedIslandAt(e.getVehicle().getLocation()).ifPresent(island -> {
|
addon.getIslands().getProtectedIslandAt(e.getVehicle().getLocation())
|
||||||
// Ignore spawn
|
// Ignore spawn
|
||||||
if (island.isSpawn()) {
|
.filter(i -> !i.isSpawn())
|
||||||
return;
|
.ifPresent(island -> {
|
||||||
}
|
|
||||||
// Check if the player is at the limit
|
// Check if the player is at the limit
|
||||||
if (!bypass && atLimit(island, e.getVehicle())) {
|
AtLimitResult res = atLimit(island, e.getVehicle());
|
||||||
|
if (res.hit()) {
|
||||||
e.setCancelled(true);
|
e.setCancelled(true);
|
||||||
for (Entity ent : e.getVehicle().getLocation().getWorld().getNearbyEntities(e.getVehicle().getLocation(), 5, 5, 5)) {
|
this.tellPlayers(e.getVehicle().getLocation(), e.getVehicle(), SpawnReason.MOUNT, res);
|
||||||
if (ent instanceof Player) {
|
|
||||||
((Player) ent).updateInventory();
|
|
||||||
User.getInstance(ent).notify("entity-limits.hit-limit", "[entity]",
|
|
||||||
Util.prettifyText(e.getVehicle().getType().toString())
|
|
||||||
, TextVariables.NUMBER, String.valueOf(addon.getSettings().getLimits().get(e.getVehicle().getType())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
|
public void onBreed(final EntityBreedEvent e) {
|
||||||
|
if (addon.inGameModeWorld(e.getEntity().getWorld())
|
||||||
|
&& e.getBreeder() != null
|
||||||
|
&& (e.getBreeder() instanceof Player p)
|
||||||
|
&& !(p.isOp() || p.hasPermission(addon.getPlugin().getIWM().getPermissionPrefix(e.getEntity().getWorld()) + MOD_BYPASS))
|
||||||
|
&& !checkLimit(e, e.getEntity(), SpawnReason.BREEDING, false)
|
||||||
|
&& e.getFather() instanceof Breedable f && e.getMother() instanceof Breedable m) {
|
||||||
|
f.setBreed(false);
|
||||||
|
m.setBreed(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
public void onCreatureSpawn(final CreatureSpawnEvent e) {
|
public void onCreatureSpawn(final CreatureSpawnEvent e) {
|
||||||
// Return if not in a known world
|
// Return if not in a known world
|
||||||
if (!addon.getPlugin().getIWM().inWorld(e.getLocation())) {
|
if (!addon.inGameModeWorld(e.getLocation().getWorld())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean bypass = false;
|
if (justSpawned.contains(e.getEntity().getUniqueId())) {
|
||||||
// Check why it was spawned
|
justSpawned.remove(e.getEntity().getUniqueId());
|
||||||
switch (e.getSpawnReason()) {
|
return;
|
||||||
// These reasons are due to a player being involved (usually) so there may be a bypass
|
|
||||||
case BREEDING:
|
|
||||||
case BUILD_IRONGOLEM:
|
|
||||||
case BUILD_SNOWMAN:
|
|
||||||
case BUILD_WITHER:
|
|
||||||
case CURED:
|
|
||||||
case EGG:
|
|
||||||
case SPAWNER_EGG:
|
|
||||||
bypass = checkByPass(e.getLocation());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Other natural reasons
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
// Tag the entity with the island spawn location
|
if (e.getSpawnReason().equals(SpawnReason.SHOULDER_ENTITY) || (!(e.getEntity() instanceof Villager ) && e.getSpawnReason().equals(SpawnReason.BREEDING))) {
|
||||||
checkLimit(e, bypass);
|
// Special case - do nothing - jumping around spawns parrots as they drop off player's shoulder
|
||||||
|
// Ignore breeding because it's handled in the EntityBreedEvent listener
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Some checks can be done async, some not
|
||||||
|
if (e.getSpawnReason().equals(SpawnReason.BUILD_SNOWMAN) || e.getSpawnReason().equals(SpawnReason.BUILD_IRONGOLEM)) {
|
||||||
|
checkLimit(e, e.getEntity(), e.getSpawnReason(), addon.getSettings().isAsyncGolums());
|
||||||
|
} else {
|
||||||
|
// Check limit sync
|
||||||
|
checkLimit(e, e.getEntity(), e.getSpawnReason(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkByPass(Location l) {
|
|
||||||
// If someone in that area has the bypass permission, allow the spawning
|
|
||||||
for (Entity entity : Objects.requireNonNull(l.getWorld()).getNearbyEntities(l, 5, 5, 5)) {
|
|
||||||
if (entity instanceof Player) {
|
|
||||||
Player player = (Player)entity;
|
|
||||||
if (player.isOp() || player.hasPermission(addon.getPlugin().getIWM().getPermissionPrefix(l.getWorld()) + MOD_BYPASS)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -117,41 +131,234 @@ public class EntityLimitListener implements Listener {
|
|||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
public void onBlock(HangingPlaceEvent e) {
|
public void onBlock(HangingPlaceEvent e) {
|
||||||
|
if (!addon.inGameModeWorld(e.getBlock().getWorld())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Player player = e.getPlayer();
|
Player player = e.getPlayer();
|
||||||
if (player == null) return;
|
if (player == null) return;
|
||||||
addon.getIslands().getIslandAt(e.getEntity().getLocation()).ifPresent(island -> {
|
addon.getIslands().getIslandAt(e.getEntity().getLocation()).ifPresent(island -> {
|
||||||
boolean bypass = Objects.requireNonNull(player).isOp() || player.hasPermission(addon.getPlugin().getIWM().getPermissionPrefix(e.getEntity().getWorld()) + MOD_BYPASS);
|
boolean bypass = Objects.requireNonNull(player).isOp() || player.hasPermission(addon.getPlugin().getIWM().getPermissionPrefix(e.getEntity().getWorld()) + MOD_BYPASS);
|
||||||
// Check if entity can be hung
|
// Check if entity can be hung
|
||||||
if (!bypass && !island.isSpawn() && atLimit(island, e.getEntity())) {
|
AtLimitResult res;
|
||||||
|
if (!bypass && !island.isSpawn() && (res = atLimit(island, e.getEntity())).hit()) {
|
||||||
// Not allowed
|
// Not allowed
|
||||||
e.setCancelled(true);
|
e.setCancelled(true);
|
||||||
|
if (res.getTypelimit() != null) {
|
||||||
User.getInstance(player).notify("block-limits.hit-limit", "[material]",
|
User.getInstance(player).notify("block-limits.hit-limit", "[material]",
|
||||||
Util.prettifyText(e.getEntity().getType().toString()),
|
Util.prettifyText(e.getEntity().getType().toString()),
|
||||||
TextVariables.NUMBER, String.valueOf(addon.getSettings().getLimits().getOrDefault(e.getEntity().getType(), -1)));
|
TextVariables.NUMBER, String.valueOf(res.getTypelimit().getValue()));
|
||||||
|
} else {
|
||||||
|
User.getInstance(player).notify("block-limits.hit-limit", "[material]",
|
||||||
|
res.getGrouplimit().getKey().getName() + " (" + res.getGrouplimit().getKey().getTypes().stream().map(x -> Util.prettifyText(x.toString())).collect(Collectors.joining(", ")) + ")",
|
||||||
|
TextVariables.NUMBER, String.valueOf(res.getGrouplimit().getValue()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkLimit(CreatureSpawnEvent e, boolean bypass) {
|
/**
|
||||||
addon.getIslands().getIslandAt(e.getLocation()).ifPresent(island -> {
|
* Check if a creature is allowed to spawn or not
|
||||||
|
* @param e - CreatureSpawnEvent
|
||||||
|
* @param async - true if check can be done async, false if not
|
||||||
|
* @return true if allowed or asycn, false if not.
|
||||||
|
*/
|
||||||
|
private boolean checkLimit(Cancellable c, LivingEntity e, SpawnReason reason, boolean async) {
|
||||||
|
Location l = e.getLocation();
|
||||||
|
if (async) {
|
||||||
|
c.setCancelled(true);
|
||||||
|
}
|
||||||
|
return processIsland(c, e, l, reason, async);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean processIsland(Cancellable c, LivingEntity e, Location l, SpawnReason reason, boolean async) {
|
||||||
|
if (addon.getIslands().getIslandAt(e.getLocation()).isEmpty()) {
|
||||||
|
c.setCancelled(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Island island = addon.getIslands().getIslandAt(e.getLocation()).get();
|
||||||
// Check if creature is allowed to spawn or not
|
// Check if creature is allowed to spawn or not
|
||||||
if (!bypass && !island.isSpawn() && atLimit(island, e.getEntity())) {
|
AtLimitResult res = atLimit(island, e);
|
||||||
// Not allowed
|
if (island.isSpawn() || !res.hit()) {
|
||||||
e.setCancelled(true);
|
// Allowed
|
||||||
|
if (async) {
|
||||||
|
Bukkit.getScheduler().runTask(BentoBox.getInstance(), () -> preSpawn(e.getType(), reason, l));
|
||||||
|
} // else do nothing
|
||||||
|
} else {
|
||||||
|
if (async) {
|
||||||
|
e.remove();
|
||||||
|
} else {
|
||||||
|
c.setCancelled(true);
|
||||||
|
}
|
||||||
// If the reason is anything but because of a spawner then tell players within range
|
// If the reason is anything but because of a spawner then tell players within range
|
||||||
if (!e.getSpawnReason().equals(SpawnReason.SPAWNER) && !e.getSpawnReason().equals(SpawnReason.NATURAL) && !e.getSpawnReason().equals(SpawnReason.INFECTION) && !e.getSpawnReason().equals(SpawnReason.NETHER_PORTAL) && !e.getSpawnReason().equals(SpawnReason.REINFORCEMENTS) && !e.getSpawnReason().equals(SpawnReason.SLIME_SPLIT)) {
|
tellPlayers(l, e, reason, res);
|
||||||
World w = e.getLocation().getWorld();
|
return false;
|
||||||
if (w == null) return;
|
}
|
||||||
for (Entity ent : w.getNearbyEntities(e.getLocation(), 5, 5, 5)) {
|
return true;
|
||||||
if (ent instanceof Player) {
|
}
|
||||||
User.getInstance(ent).notify("entity-limits.hit-limit", "[entity]",
|
|
||||||
Util.prettifyText(e.getEntityType().toString()),
|
private void preSpawn(EntityType entityType, SpawnReason reason, Location l) {
|
||||||
TextVariables.NUMBER, String.valueOf(addon.getSettings().getLimits().get(e.getEntityType())));
|
|
||||||
|
// Check for entities that need cleanup
|
||||||
|
switch (reason) {
|
||||||
|
case BUILD_IRONGOLEM -> detectIronGolem(l);
|
||||||
|
case BUILD_SNOWMAN -> detectSnowman(l);
|
||||||
|
case BUILD_WITHER -> {
|
||||||
|
detectWither(l);
|
||||||
|
}
|
||||||
|
default -> throw new IllegalArgumentException("Unexpected value: " + reason);
|
||||||
|
}
|
||||||
|
Entity entity = l.getWorld().spawnEntity(l, entityType);
|
||||||
|
justSpawned.add(entity.getUniqueId());
|
||||||
|
if (reason == SpawnReason.BUILD_WITHER) {
|
||||||
|
// Create explosion
|
||||||
|
l.getWorld().createExplosion(l, 7F, true, true, entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void detectIronGolem(Location l) {
|
||||||
|
Block legs = l.getBlock();
|
||||||
|
// Erase legs
|
||||||
|
addon.getBlockLimitListener().removeBlock(legs);
|
||||||
|
legs.setType(Material.AIR);
|
||||||
|
// Look around for possible constructions
|
||||||
|
for (BlockFace bf : CARDINALS) {
|
||||||
|
Block body = legs.getRelative(bf);
|
||||||
|
if (body.getType().equals(Material.IRON_BLOCK)) {
|
||||||
|
// Check for head
|
||||||
|
Block head = body.getRelative(bf);
|
||||||
|
if (head.getType() == Material.CARVED_PUMPKIN || head.getType() == Material.JACK_O_LANTERN) {
|
||||||
|
// Check for arms the rule is that they must be opposite and have nothing "beneath" them
|
||||||
|
for (BlockFace bf2 : CARDINALS) {
|
||||||
|
Block arm1 = body.getRelative(bf2);
|
||||||
|
Block arm2 = body.getRelative(bf2.getOppositeFace());
|
||||||
|
if (arm1.getType() == Material.IRON_BLOCK && arm2.getType() == Material.IRON_BLOCK
|
||||||
|
&& arm1.getRelative(bf.getOppositeFace()).isEmpty()
|
||||||
|
&& arm2.getRelative(bf.getOppositeFace()).isEmpty()) {
|
||||||
|
// Erase!
|
||||||
|
addon.getBlockLimitListener().removeBlock(body);
|
||||||
|
addon.getBlockLimitListener().removeBlock(arm1);
|
||||||
|
addon.getBlockLimitListener().removeBlock(arm2);
|
||||||
|
addon.getBlockLimitListener().removeBlock(head);
|
||||||
|
body.setType(Material.AIR);
|
||||||
|
arm1.setType(Material.AIR);
|
||||||
|
arm2.setType(Material.AIR);
|
||||||
|
head.setType(Material.AIR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void detectSnowman(Location l) {
|
||||||
|
Block legs = l.getBlock();
|
||||||
|
// Erase legs
|
||||||
|
addon.getBlockLimitListener().removeBlock(legs);
|
||||||
|
legs.setType(Material.AIR);
|
||||||
|
// Look around for possible constructions
|
||||||
|
for (BlockFace bf : CARDINALS) {
|
||||||
|
Block body = legs.getRelative(bf);
|
||||||
|
if (body.getType().equals(Material.SNOW_BLOCK)) {
|
||||||
|
// Check for head
|
||||||
|
Block head = body.getRelative(bf);
|
||||||
|
if (head.getType() == Material.CARVED_PUMPKIN || head.getType() == Material.JACK_O_LANTERN) {
|
||||||
|
// Erase
|
||||||
|
addon.getBlockLimitListener().removeBlock(body);
|
||||||
|
addon.getBlockLimitListener().removeBlock(head);
|
||||||
|
|
||||||
|
body.setType(Material.AIR);
|
||||||
|
head.setType(Material.AIR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void detectWither(Location l) {
|
||||||
|
Block legs = l.getBlock();
|
||||||
|
// Erase legs
|
||||||
|
addon.getBlockLimitListener().removeBlock(legs);
|
||||||
|
legs.setType(Material.AIR);
|
||||||
|
// Look around for possible constructions
|
||||||
|
for (BlockFace bf : CARDINALS) {
|
||||||
|
Block body = legs.getRelative(bf);
|
||||||
|
if (isWither(body)) {
|
||||||
|
// Check for head
|
||||||
|
Block head = body.getRelative(bf);
|
||||||
|
if (head.getType().equals(Material.WITHER_SKELETON_SKULL) || head.getType().equals(Material.WITHER_SKELETON_WALL_SKULL)) {
|
||||||
|
// Check for arms the rule is that they must be opposite and have nothing "beneath" them
|
||||||
|
for (BlockFace bf2 : CARDINALS) {
|
||||||
|
Block arm1 = body.getRelative(bf2);
|
||||||
|
Block arm2 = body.getRelative(bf2.getOppositeFace());
|
||||||
|
Block head2 = arm1.getRelative(bf);
|
||||||
|
Block head3 = arm2.getRelative(bf);
|
||||||
|
if (isWither(arm1)
|
||||||
|
&& isWither(arm2)
|
||||||
|
&& arm1.getRelative(bf.getOppositeFace()).isEmpty()
|
||||||
|
&& arm2.getRelative(bf.getOppositeFace()).isEmpty()
|
||||||
|
&& (head2.getType().equals(Material.WITHER_SKELETON_SKULL) || head2.getType().equals(Material.WITHER_SKELETON_WALL_SKULL))
|
||||||
|
&& (head3.getType().equals(Material.WITHER_SKELETON_SKULL) || head3.getType().equals(Material.WITHER_SKELETON_WALL_SKULL))
|
||||||
|
) {
|
||||||
|
// Erase!
|
||||||
|
addon.getBlockLimitListener().removeBlock(body);
|
||||||
|
addon.getBlockLimitListener().removeBlock(arm1);
|
||||||
|
addon.getBlockLimitListener().removeBlock(arm2);
|
||||||
|
addon.getBlockLimitListener().removeBlock(head);
|
||||||
|
addon.getBlockLimitListener().removeBlock(head2);
|
||||||
|
addon.getBlockLimitListener().removeBlock(head3);
|
||||||
|
body.setType(Material.AIR);
|
||||||
|
arm1.setType(Material.AIR);
|
||||||
|
arm2.setType(Material.AIR);
|
||||||
|
head.setType(Material.AIR);
|
||||||
|
head2.setType(Material.AIR);
|
||||||
|
head3.setType(Material.AIR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isWither(Block body) {
|
||||||
|
if (Util.getMinecraftVersion() < 16) {
|
||||||
|
return body.getType().equals(Material.SOUL_SAND);
|
||||||
|
}
|
||||||
|
return Tag.WITHER_SUMMON_BASE_BLOCKS.isTagged(body.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell players within a 5 x 5 x 5 radius that the spawning was denied. Informing happens 1 tick after event
|
||||||
|
* @param l location
|
||||||
|
* @param entity entity spawned
|
||||||
|
* @param reason reason - some reasons are not reported
|
||||||
|
* @param res at limit result
|
||||||
|
*/
|
||||||
|
private void tellPlayers(Location l, Entity entity, SpawnReason reason, AtLimitResult res) {
|
||||||
|
if (reason.equals(SpawnReason.SPAWNER) || reason.equals(SpawnReason.NATURAL)
|
||||||
|
|| reason.equals(SpawnReason.INFECTION) || reason.equals(SpawnReason.NETHER_PORTAL)
|
||||||
|
|| reason.equals(SpawnReason.REINFORCEMENTS) || reason.equals(SpawnReason.SLIME_SPLIT)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
World w = l.getWorld();
|
||||||
|
if (w == null) return;
|
||||||
|
Bukkit.getScheduler().runTask(addon.getPlugin(), () -> {
|
||||||
|
for (Entity ent : w.getNearbyEntities(l, 5, 5, 5)) {
|
||||||
|
if (ent instanceof Player p) {
|
||||||
|
p.updateInventory();
|
||||||
|
if (res.getTypelimit() != null) {
|
||||||
|
User.getInstance(p).notify("entity-limits.hit-limit", "[entity]",
|
||||||
|
Util.prettifyText(entity.getType().toString()),
|
||||||
|
TextVariables.NUMBER, String.valueOf(res.getTypelimit().getValue()));
|
||||||
|
} else {
|
||||||
|
User.getInstance(p).notify("entity-limits.hit-limit", "[entity]",
|
||||||
|
res.getGrouplimit().getKey().getName() + " (" + res.getGrouplimit().getKey().getTypes().stream().map(x -> Util.prettifyText(x.toString())).collect(Collectors.joining(", ")) + ")",
|
||||||
|
TextVariables.NUMBER, String.valueOf(res.getGrouplimit().getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -163,21 +370,104 @@ public class EntityLimitListener implements Listener {
|
|||||||
* @param ent - the entity
|
* @param ent - the entity
|
||||||
* @return true if at the limit, false if not
|
* @return true if at the limit, false if not
|
||||||
*/
|
*/
|
||||||
private boolean atLimit(Island island, Entity ent) {
|
AtLimitResult atLimit(Island island, Entity ent) {
|
||||||
// Check island settings first
|
// Check island settings first
|
||||||
int limitAmount = -1;
|
int limitAmount = -1;
|
||||||
if (addon.getBlockLimitListener().getIsland(island.getUniqueId()) != null) {
|
Map<EntityGroup, Integer> groupsLimits = new HashMap<>();
|
||||||
limitAmount = addon.getBlockLimitListener().getIsland(island.getUniqueId()).getEntityLimit(ent.getType());
|
|
||||||
|
@Nullable
|
||||||
|
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(island.getUniqueId());
|
||||||
|
if (ibc != null) {
|
||||||
|
// Get the limit amount for this type
|
||||||
|
limitAmount = ibc.getEntityLimit(ent.getType());
|
||||||
|
// Handle entity groups
|
||||||
|
List<EntityGroup> groupdefs = addon.getSettings().getGroupLimits().getOrDefault(ent.getType(),
|
||||||
|
new ArrayList<>());
|
||||||
|
groupdefs.forEach(def -> {
|
||||||
|
int limit = ibc.getEntityGroupLimit(def.getName());
|
||||||
|
if (limit >= 0)
|
||||||
|
groupsLimits.put(def, limit);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// If no island settings then try global settings
|
// If no island settings then try global settings
|
||||||
if (limitAmount < 0 && addon.getSettings().getLimits().containsKey(ent.getType())) {
|
if (limitAmount < 0 && addon.getSettings().getLimits().containsKey(ent.getType())) {
|
||||||
limitAmount = addon.getSettings().getLimits().get(ent.getType());
|
limitAmount = addon.getSettings().getLimits().get(ent.getType());
|
||||||
}
|
}
|
||||||
if (limitAmount < 0) return false;
|
// Group limits
|
||||||
|
if (addon.getSettings().getGroupLimits().containsKey(ent.getType())) {
|
||||||
|
addon.getSettings().getGroupLimits().getOrDefault(ent.getType(), new ArrayList<>()).stream()
|
||||||
|
.filter(group -> !groupsLimits.containsKey(group) || groupsLimits.get(group) > group.getLimit())
|
||||||
|
.forEach(group -> groupsLimits.put(group, group.getLimit()));
|
||||||
|
}
|
||||||
|
if (limitAmount < 0 && groupsLimits.isEmpty()) {
|
||||||
|
return new AtLimitResult();
|
||||||
|
}
|
||||||
|
|
||||||
// We have to count the entities
|
// We have to count the entities
|
||||||
return ent.getWorld().getEntities().stream()
|
if (limitAmount >= 0)
|
||||||
|
{
|
||||||
|
int count = (int) ent.getWorld().getNearbyEntities(island.getBoundingBox()).stream()
|
||||||
.filter(e -> e.getType().equals(ent.getType()))
|
.filter(e -> e.getType().equals(ent.getType()))
|
||||||
.filter(e -> island.inIslandSpace(e.getLocation())).count() >= limitAmount;
|
.count();
|
||||||
|
int max = limitAmount + (ibc == null ? 0 : ibc.getEntityLimitOffset(ent.getType()));
|
||||||
|
if (count >= max) {
|
||||||
|
return new AtLimitResult(ent.getType(), max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Group limits
|
||||||
|
if (ibc != null) {
|
||||||
|
Map<String, EntityGroup> groupbyname = groupsLimits.keySet().stream()
|
||||||
|
.collect(Collectors.toMap(EntityGroup::getName, e -> e));
|
||||||
|
ibc.getEntityGroupLimits().entrySet().stream()
|
||||||
|
.filter(e -> groupbyname.containsKey(e.getKey()))
|
||||||
|
.forEach(e -> groupsLimits.put(groupbyname.get(e.getKey()), e.getValue()));
|
||||||
|
}
|
||||||
|
// Now do the group limits
|
||||||
|
for (Map.Entry<EntityGroup, Integer> group : groupsLimits.entrySet()) { //do not use lambda
|
||||||
|
if (group.getValue() < 0)
|
||||||
|
continue;
|
||||||
|
// int count = (int) ent.getWorld().getEntities().stream()
|
||||||
|
// .filter(e -> group.getKey().contains(e.getType()))
|
||||||
|
// .filter(e -> island.inIslandSpace(e.getLocation())).count();
|
||||||
|
int count = (int) ent.getWorld().getNearbyEntities(island.getBoundingBox()).stream()
|
||||||
|
.filter(e -> group.getKey().contains(e.getType()))
|
||||||
|
.count();
|
||||||
|
int max = group.getValue() + + (ibc == null ? 0 : ibc.getEntityGroupLimitOffset(group.getKey().getName()));
|
||||||
|
if (count >= max) {
|
||||||
|
return new AtLimitResult(group.getKey(), max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new AtLimitResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AtLimitResult {
|
||||||
|
private Map.Entry<EntityType, Integer> typelimit;
|
||||||
|
private Map.Entry<EntityGroup, Integer> grouplimit;
|
||||||
|
|
||||||
|
public AtLimitResult() {}
|
||||||
|
|
||||||
|
public AtLimitResult(EntityType type, int limit) {
|
||||||
|
typelimit = new AbstractMap.SimpleEntry<>(type, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AtLimitResult(EntityGroup type, int limit) {
|
||||||
|
grouplimit = new AbstractMap.SimpleEntry<>(type, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if at limit
|
||||||
|
*/
|
||||||
|
public boolean hit() {
|
||||||
|
return typelimit != null || grouplimit != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map.Entry<EntityType, Integer> getTypelimit() {
|
||||||
|
return typelimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map.Entry<EntityGroup, Integer> getGrouplimit() {
|
||||||
|
return grouplimit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ import java.util.Locale;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.apache.commons.lang.math.NumberUtils;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
@ -17,16 +16,21 @@ import org.bukkit.event.EventPriority;
|
|||||||
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.permissions.PermissionAttachmentInfo;
|
import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||||
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
|
|
||||||
import world.bentobox.bentobox.api.events.island.IslandEvent;
|
import world.bentobox.bentobox.api.events.island.IslandEvent;
|
||||||
import world.bentobox.bentobox.api.events.island.IslandEvent.Reason;
|
import world.bentobox.bentobox.api.events.island.IslandEvent.Reason;
|
||||||
import world.bentobox.bentobox.api.events.team.TeamEvent.TeamSetownerEvent;
|
import world.bentobox.bentobox.api.events.team.TeamSetownerEvent;
|
||||||
import world.bentobox.bentobox.database.objects.Island;
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.limits.EntityGroup;
|
||||||
import world.bentobox.limits.Limits;
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.events.LimitsJoinPermCheckEvent;
|
||||||
|
import world.bentobox.limits.events.LimitsPermCheckEvent;
|
||||||
import world.bentobox.limits.objects.IslandBlockCount;
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets block limits based on player permission
|
* Sets block limits based on player permission
|
||||||
|
*
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -38,63 +42,132 @@ public class JoinListener implements Listener {
|
|||||||
this.addon = addon;
|
this.addon = addon;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkPerms(Player player, String permissionPrefix, String islandId, String gameMode) {
|
/**
|
||||||
|
* Check and set the permissions of the player and how they affect the island
|
||||||
|
* limits
|
||||||
|
*
|
||||||
|
* @param player - player
|
||||||
|
* @param permissionPrefix - permission prefix for this game mode
|
||||||
|
* @param islandId - island string id
|
||||||
|
* @param gameMode - game mode string doing the checking
|
||||||
|
*/
|
||||||
|
public void checkPerms(Player player, String permissionPrefix, String islandId, String gameMode) {
|
||||||
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(islandId);
|
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(islandId);
|
||||||
|
// Check permissions
|
||||||
if (ibc != null) {
|
if (ibc != null) {
|
||||||
// Clear permission limits
|
// Clear permission limits
|
||||||
ibc.getEntityLimits().clear();
|
ibc.getEntityLimits().clear();
|
||||||
|
ibc.getEntityGroupLimits().clear();
|
||||||
ibc.getBlockLimits().clear();
|
ibc.getBlockLimits().clear();
|
||||||
}
|
}
|
||||||
for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) {
|
for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) {
|
||||||
if (!perms.getValue() || !perms.getPermission().startsWith(permissionPrefix)) continue;
|
if (!perms.getValue() || !perms.getPermission().startsWith(permissionPrefix)
|
||||||
// No wildcards
|
|| badSyntaxCheck(perms, player.getName(), permissionPrefix)) {
|
||||||
if (perms.getPermission().contains(permissionPrefix + "*")) {
|
continue;
|
||||||
logError(player.getName(), perms.getPermission(), "wildcards are not allowed.");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Check formatting
|
// Check formatting
|
||||||
String[] split = perms.getPermission().split("\\.");
|
String[] split = perms.getPermission().split("\\.");
|
||||||
if (split.length != 5) {
|
|
||||||
logError(player.getName(), perms.getPermission(), "format must be '" + permissionPrefix + "MATERIAL.NUMBER' or '" + permissionPrefix + "ENTITY-TYPE.NUMBER'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Check value
|
|
||||||
if (!NumberUtils.isDigits(split[4])) {
|
|
||||||
logError(player.getName(), perms.getPermission(), "the last part MUST be a number!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Entities & materials
|
// Entities & materials
|
||||||
EntityType et = Arrays.stream(EntityType.values()).filter(t -> t.name().equalsIgnoreCase(split[3])).findFirst().orElse(null);
|
EntityType et = Arrays.stream(EntityType.values()).filter(t -> t.name().equalsIgnoreCase(split[3]))
|
||||||
Material m = Arrays.stream(Material.values()).filter(t -> t.name().equalsIgnoreCase(split[3])).findFirst().orElse(null);
|
.findFirst().orElse(null);
|
||||||
|
Material m = Arrays.stream(Material.values()).filter(t -> t.name().equalsIgnoreCase(split[3])).findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
EntityGroup entgroup = addon.getSettings().getGroupLimitDefinitions().stream()
|
||||||
|
.filter(t -> t.getName().equalsIgnoreCase(split[3])).findFirst().orElse(null);
|
||||||
|
|
||||||
if (et == null && m == null) {
|
if (entgroup == null && et == null && m == null) {
|
||||||
logError(player.getName(), perms.getPermission(), split[3].toUpperCase(Locale.ENGLISH) + " is not a valid material or entity type.");
|
logError(player.getName(), perms.getPermission(),
|
||||||
|
split[3].toUpperCase(Locale.ENGLISH) + " is not a valid material or entity type/group.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Make an ibc if required
|
// Make an ibc if required
|
||||||
if (ibc == null) {
|
if (ibc == null) {
|
||||||
ibc = new IslandBlockCount(islandId, gameMode);
|
ibc = new IslandBlockCount(islandId, gameMode);
|
||||||
}
|
}
|
||||||
if (et != null && m == null) {
|
// Get the value
|
||||||
// Entity limit
|
int value = Integer.parseInt(split[4]);
|
||||||
ibc.setEntityLimit(et, Math.max(ibc.getEntityLimit(et), Integer.valueOf(split[4])));
|
addon.log("Setting login limit via perm for " + player.getName() + "...");
|
||||||
} else if (m != null && et == null) {
|
|
||||||
// Material limit
|
// Fire perm check event
|
||||||
ibc.setBlockLimit(m, Math.max(ibc.getBlockLimit(m), Integer.valueOf(split[4])));
|
LimitsPermCheckEvent l = new LimitsPermCheckEvent(player, islandId, ibc, entgroup, et, m, value);
|
||||||
} else {
|
Bukkit.getPluginManager().callEvent(l);
|
||||||
if (m.isBlock()) {
|
if (l.isCancelled()) {
|
||||||
// Material limit
|
addon.log("Permissions not set because another addon/plugin canceled setting.");
|
||||||
ibc.setBlockLimit(m, Math.max(ibc.getBlockLimit(m), Integer.valueOf(split[4])));
|
continue;
|
||||||
} else {
|
|
||||||
// This is an entity setting
|
|
||||||
ibc.setEntityLimit(et, Math.max(ibc.getEntityLimit(et), Integer.valueOf(split[4])));
|
|
||||||
}
|
}
|
||||||
|
// Use event values
|
||||||
|
ibc = l.getIbc();
|
||||||
|
// Make an ibc if required
|
||||||
|
if (ibc == null) {
|
||||||
|
ibc = new IslandBlockCount(islandId, gameMode);
|
||||||
}
|
}
|
||||||
|
// Run null checks and set ibc
|
||||||
|
runNullCheckAndSet(ibc, l);
|
||||||
}
|
}
|
||||||
// Check removed permissions
|
// Check removed permissions
|
||||||
|
// If any changes have been made then store it - don't make files unless they
|
||||||
|
// are needed
|
||||||
|
if (ibc != null)
|
||||||
|
addon.getBlockLimitListener().setIsland(islandId, ibc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean badSyntaxCheck(PermissionAttachmentInfo perms, String name, String permissionPrefix) {
|
||||||
|
// No wildcards
|
||||||
|
if (perms.getPermission().contains(permissionPrefix + "*")) {
|
||||||
|
logError(name, perms.getPermission(), "wildcards are not allowed.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Check formatting
|
||||||
|
String[] split = perms.getPermission().split("\\.");
|
||||||
|
if (split.length != 5) {
|
||||||
|
logError(name, perms.getPermission(), "format must be '" + permissionPrefix + "MATERIAL.NUMBER', '"
|
||||||
|
+ permissionPrefix + "ENTITY-TYPE.NUMBER', or '" + permissionPrefix + "ENTITY-GROUP.NUMBER'");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Check value
|
||||||
|
try {
|
||||||
|
Integer.parseInt(split[4]);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logError(name, perms.getPermission(), "the last part MUST be an integer!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runNullCheckAndSet(@NonNull IslandBlockCount ibc, @NonNull LimitsPermCheckEvent l) {
|
||||||
|
EntityGroup entgroup = l.getEntityGroup();
|
||||||
|
EntityType et = l.getEntityType();
|
||||||
|
Material m = l.getMaterial();
|
||||||
|
int value = l.getValue();
|
||||||
|
if (entgroup != null) {
|
||||||
|
// Entity group limit
|
||||||
|
int v = Math.max(ibc.getEntityGroupLimit(entgroup.getName()), value);
|
||||||
|
ibc.setEntityGroupLimit(entgroup.getName(), v);
|
||||||
|
addon.log("Setting group limit " + entgroup.getName() + " " + v);
|
||||||
|
} else if (et != null && m == null) {
|
||||||
|
// Entity limit
|
||||||
|
int v = Math.max(ibc.getEntityLimit(et), value);
|
||||||
|
ibc.setEntityLimit(et, v);
|
||||||
|
addon.log("Setting entity limit " + et + " " + v);
|
||||||
|
} else if (m != null && et == null) {
|
||||||
|
// Block limit
|
||||||
|
int v = Math.max(ibc.getBlockLimit(m), value);
|
||||||
|
addon.log("Setting block limit " + m + " " + v);
|
||||||
|
ibc.setBlockLimit(m, v);
|
||||||
|
} else {
|
||||||
|
if (m != null && m.isBlock()) {
|
||||||
|
int v = Math.max(ibc.getBlockLimit(m), value);
|
||||||
|
addon.log("Setting block limit " + m + " " + v);
|
||||||
|
// Material limit
|
||||||
|
ibc.setBlockLimit(m, v);
|
||||||
|
} else if (et != null) {
|
||||||
|
int v = Math.max(ibc.getEntityLimit(et), value);
|
||||||
|
addon.log("Setting entity limit " + et + " " + v);
|
||||||
|
// This is an entity setting
|
||||||
|
ibc.setEntityLimit(et, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If any changes have been made then store it - don't make files unless they are needed
|
|
||||||
if (ibc != null) addon.getBlockLimitListener().setIsland(islandId, ibc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logError(String name, String perm, String error) {
|
private void logError(String name, String perm, String error) {
|
||||||
@ -107,8 +180,7 @@ public class JoinListener implements Listener {
|
|||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
public void onNewIsland(IslandEvent e) {
|
public void onNewIsland(IslandEvent e) {
|
||||||
if (!e.getReason().equals(Reason.CREATED)
|
if (!e.getReason().equals(Reason.CREATED) && !e.getReason().equals(Reason.RESETTED)
|
||||||
&& !e.getReason().equals(Reason.RESETTED)
|
|
||||||
&& !e.getReason().equals(Reason.REGISTERED)) {
|
&& !e.getReason().equals(Reason.REGISTERED)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -121,16 +193,46 @@ public class JoinListener implements Listener {
|
|||||||
setOwnerPerms(e.getIsland(), e.getNewOwner());
|
setOwnerPerms(e.getIsland(), e.getNewOwner());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
public void onPlayerJoin(PlayerJoinEvent e) {
|
public void onPlayerJoin(PlayerJoinEvent e) {
|
||||||
// Check if player has any islands in the game modes
|
// Check if player has any islands in the game modes
|
||||||
addon.getGameModes().forEach(gm -> {
|
addon.getGameModes().forEach(gm -> {
|
||||||
if (addon.getIslands().hasIsland(gm.getOverWorld(), e.getPlayer().getUniqueId())) {
|
addon.getIslands().getIslands(gm.getOverWorld(), e.getPlayer().getUniqueId()).stream()
|
||||||
String islandId = addon.getIslands().getIsland(gm.getOverWorld(), e.getPlayer().getUniqueId()).getUniqueId();
|
.filter(island -> e.getPlayer().getUniqueId().equals(island.getOwner()))
|
||||||
checkPerms(e.getPlayer(), gm.getPermissionPrefix() + "island.limit.", islandId, gm.getDescription().getName());
|
.map(Island::getUniqueId).forEach(islandId -> {
|
||||||
|
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(islandId);
|
||||||
|
if (!joinEventCheck(e.getPlayer(), islandId, ibc)) {
|
||||||
|
checkPerms(e.getPlayer(), gm.getPermissionPrefix() + "island.limit.", islandId,
|
||||||
|
gm.getDescription().getName());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fire event so other addons can cancel this permissions change
|
||||||
|
*
|
||||||
|
* @param player player
|
||||||
|
* @param islandId island id
|
||||||
|
* @param ibc island block count
|
||||||
|
* @return true if canceled
|
||||||
|
*/
|
||||||
|
private boolean joinEventCheck(Player player, String islandId, IslandBlockCount ibc) {
|
||||||
|
// Fire event, so other addons can cancel this permissions change
|
||||||
|
LimitsJoinPermCheckEvent e = new LimitsJoinPermCheckEvent(player, islandId, ibc);
|
||||||
|
Bukkit.getPluginManager().callEvent(e);
|
||||||
|
if (e.isCancelled()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Get ibc from event if it has changed
|
||||||
|
ibc = e.getIbc();
|
||||||
|
// If perms should be ignored, but the IBC given in the event used, then set it
|
||||||
|
// and return
|
||||||
|
if (e.isIgnorePerms() && ibc != null) {
|
||||||
|
addon.getBlockLimitListener().setIsland(islandId, ibc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
@ -165,7 +267,8 @@ public class JoinListener implements Listener {
|
|||||||
String prefix = addon.getGameModePermPrefix(world);
|
String prefix = addon.getGameModePermPrefix(world);
|
||||||
String name = addon.getGameModeName(world);
|
String name = addon.getGameModeName(world);
|
||||||
if (!prefix.isEmpty() && !name.isEmpty() && owner.getPlayer() != null) {
|
if (!prefix.isEmpty() && !name.isEmpty() && owner.getPlayer() != null) {
|
||||||
checkPerms(Objects.requireNonNull(owner.getPlayer()), prefix + "island.limit.", island.getUniqueId(), name);
|
checkPerms(Objects.requireNonNull(owner.getPlayer()), prefix + "island.limit.",
|
||||||
|
island.getUniqueId(), name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,13 @@ import java.util.UUID;
|
|||||||
import com.google.gson.annotations.Expose;
|
import com.google.gson.annotations.Expose;
|
||||||
|
|
||||||
import world.bentobox.bentobox.database.objects.DataObject;
|
import world.bentobox.bentobox.database.objects.DataObject;
|
||||||
|
import world.bentobox.bentobox.database.objects.Table;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@Table(name = "EntityLimits")
|
||||||
public class EntityLimitsDO implements DataObject {
|
public class EntityLimitsDO implements DataObject {
|
||||||
|
|
||||||
@Expose
|
@Expose
|
||||||
@ -78,10 +80,9 @@ public class EntityLimitsDO implements DataObject {
|
|||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!(obj instanceof EntityLimitsDO)) {
|
if (!(obj instanceof EntityLimitsDO other)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
EntityLimitsDO other = (EntityLimitsDO) obj;
|
|
||||||
if (uniqueId == null) {
|
if (uniqueId == null) {
|
||||||
return other.uniqueId == null;
|
return other.uniqueId == null;
|
||||||
} else return uniqueId.equals(other.uniqueId);
|
} else return uniqueId.equals(other.uniqueId);
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package world.bentobox.limits.objects;
|
package world.bentobox.limits.objects;
|
||||||
|
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
@ -9,139 +11,209 @@ import org.bukkit.entity.EntityType;
|
|||||||
import com.google.gson.annotations.Expose;
|
import com.google.gson.annotations.Expose;
|
||||||
|
|
||||||
import world.bentobox.bentobox.database.objects.DataObject;
|
import world.bentobox.bentobox.database.objects.DataObject;
|
||||||
|
import world.bentobox.bentobox.database.objects.Table;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@Table(name = "IslandBlockCount")
|
||||||
public class IslandBlockCount implements DataObject {
|
public class IslandBlockCount implements DataObject {
|
||||||
|
|
||||||
@Expose
|
@Expose
|
||||||
private String uniqueId = "";
|
private Map<Material, Integer> blockCounts = new EnumMap<>(Material.class);
|
||||||
|
|
||||||
@Expose
|
|
||||||
private String gameMode = "";
|
|
||||||
|
|
||||||
@Expose
|
|
||||||
private Map<Material, Integer> blockCount = new EnumMap<>(Material.class);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Permission based limits
|
* Permission based limits
|
||||||
*/
|
*/
|
||||||
@Expose
|
@Expose
|
||||||
private Map<Material, Integer> blockLimits = new EnumMap<>(Material.class);
|
private Map<Material, Integer> blockLimits = new EnumMap<>(Material.class);
|
||||||
|
|
||||||
|
@Expose
|
||||||
|
private Map<Material, Integer> blockLimitsOffset = new EnumMap<>(Material.class);
|
||||||
|
|
||||||
|
private boolean changed;
|
||||||
|
|
||||||
|
@Expose
|
||||||
|
private Map<String, Integer> entityGroupLimits = new HashMap<>();
|
||||||
|
@Expose
|
||||||
|
private Map<String, Integer> entityGroupLimitsOffset = new HashMap<>();
|
||||||
@Expose
|
@Expose
|
||||||
private Map<EntityType, Integer> entityLimits = new EnumMap<>(EntityType.class);
|
private Map<EntityType, Integer> entityLimits = new EnumMap<>(EntityType.class);
|
||||||
|
@Expose
|
||||||
|
private Map<EntityType, Integer> entityLimitsOffset = new EnumMap<>(EntityType.class);
|
||||||
|
@Expose
|
||||||
|
private String gameMode;
|
||||||
|
@Expose
|
||||||
|
private String uniqueId;
|
||||||
|
|
||||||
// Required for YAML database
|
/**
|
||||||
public IslandBlockCount() {}
|
* Create an island block count object
|
||||||
|
*
|
||||||
public IslandBlockCount(String uniqueId2, String gameMode2) {
|
* @param islandId - unique Island ID string
|
||||||
this.uniqueId = uniqueId2;
|
* @param gameMode - Game mode name from gm.getDescription().getName()
|
||||||
this.gameMode = gameMode2;
|
*/
|
||||||
|
public IslandBlockCount(String islandId, String gameMode) {
|
||||||
|
this.uniqueId = islandId;
|
||||||
|
this.gameMode = gameMode;
|
||||||
|
setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/**
|
||||||
* @see world.bentobox.bentobox.database.objects.DataObject#getUniqueId()
|
* Add a material to the count
|
||||||
|
*
|
||||||
|
* @param material - material
|
||||||
*/
|
*/
|
||||||
@Override
|
public void add(Material material) {
|
||||||
public String getUniqueId() {
|
getBlockCounts().merge(material, 1, Integer::sum);
|
||||||
return uniqueId;
|
setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/**
|
||||||
* @see world.bentobox.bentobox.database.objects.DataObject#setUniqueId(java.lang.String)
|
* Clear all island-specific entity group limits
|
||||||
*/
|
*/
|
||||||
@Override
|
public void clearEntityGroupLimits() {
|
||||||
public void setUniqueId(String uniqueId) {
|
entityGroupLimits.clear();
|
||||||
this.uniqueId = uniqueId;
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all island-specific entity type limits
|
||||||
|
*/
|
||||||
|
public void clearEntityLimits() {
|
||||||
|
entityLimits.clear();
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the block count for this material for this island
|
||||||
|
*
|
||||||
|
* @param m - material
|
||||||
|
* @return count
|
||||||
|
*/
|
||||||
|
public Integer getBlockCount(Material m) {
|
||||||
|
return getBlockCounts().getOrDefault(m, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the blockCount
|
* @return the blockCount
|
||||||
*/
|
*/
|
||||||
public Map<Material, Integer> getBlockCount() {
|
public Map<Material, Integer> getBlockCounts() {
|
||||||
return blockCount;
|
if (blockCounts == null) {
|
||||||
|
blockCounts = new EnumMap<>(Material.class);
|
||||||
|
}
|
||||||
|
return blockCounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param blockCount the blockCount to set
|
* Get the block limit for this material for this island
|
||||||
*/
|
*
|
||||||
public void setBlockCount(Map<Material, Integer> blockCount) {
|
|
||||||
this.blockCount = blockCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a material to the count
|
|
||||||
* @param material - material
|
|
||||||
*/
|
|
||||||
public void add(Material material) {
|
|
||||||
blockCount.merge(material, 1, Integer::sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a material from the count
|
|
||||||
* @param material - material
|
|
||||||
*/
|
|
||||||
public void remove(Material material) {
|
|
||||||
blockCount.put(material, blockCount.getOrDefault(material, 0) - 1);
|
|
||||||
blockCount.values().removeIf(v -> v <= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if this material is at or over a limit
|
|
||||||
* @param material - block material
|
|
||||||
* @param limit - limit to check
|
|
||||||
* @return true if count is >= limit
|
|
||||||
*/
|
|
||||||
public boolean isAtLimit(Material material, int limit) {
|
|
||||||
return blockCount.getOrDefault(material, 0) >= limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if no more of this material can be added to this island
|
|
||||||
* @param m - material
|
* @param m - material
|
||||||
* @return true if no more material can be added
|
* @return limit or -1 for unlimited
|
||||||
*/
|
*/
|
||||||
public boolean isAtLimit(Material m) {
|
public int getBlockLimit(Material m) {
|
||||||
// Check island limits first
|
return getBlockLimits().getOrDefault(m, -1);
|
||||||
return blockLimits.containsKey(m) && blockCount.getOrDefault(m, 0) >= blockLimits.get(m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBlockLimited(Material m) {
|
/**
|
||||||
return blockLimits.containsKey(m);
|
* Get the block offset for this material for this island
|
||||||
|
*
|
||||||
|
* @param m - material
|
||||||
|
* @return offset
|
||||||
|
*/
|
||||||
|
public int getBlockLimitOffset(Material m) {
|
||||||
|
return getBlockLimitsOffset().getOrDefault(m, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the blockLimits
|
* @return the blockLimits
|
||||||
*/
|
*/
|
||||||
public Map<Material, Integer> getBlockLimits() {
|
public Map<Material, Integer> getBlockLimits() {
|
||||||
return blockLimits;
|
return Objects.requireNonNullElse(blockLimits, new EnumMap<>(Material.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param blockLimits the blockLimits to set
|
* @return the blockLimitsOffset
|
||||||
*/
|
*/
|
||||||
public void setBlockLimits(Map<Material, Integer> blockLimits) {
|
public Map<Material, Integer> getBlockLimitsOffset() {
|
||||||
this.blockLimits = blockLimits;
|
if (blockLimitsOffset == null) {
|
||||||
|
blockLimitsOffset = new EnumMap<>(Material.class);
|
||||||
|
}
|
||||||
|
return blockLimitsOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the block limit for this material for this island
|
* Get the limit for an entity group
|
||||||
* @param m - material
|
*
|
||||||
|
* @param name - entity group
|
||||||
* @return limit or -1 for unlimited
|
* @return limit or -1 for unlimited
|
||||||
*/
|
*/
|
||||||
public Integer getBlockLimit(Material m) {
|
public int getEntityGroupLimit(String name) {
|
||||||
return blockLimits.getOrDefault(m, -1);
|
return getEntityGroupLimits().getOrDefault(name, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the block limit for this material for this island
|
* Get the offset for an entity group
|
||||||
* @param m - material
|
*
|
||||||
* @param limit - maximum number allowed
|
* @param name - entity group
|
||||||
|
* @return offset
|
||||||
*/
|
*/
|
||||||
public void setBlockLimit(Material m, int limit) {
|
public int getEntityGroupLimitOffset(String name) {
|
||||||
blockLimits.put(m, limit);
|
return getEntityGroupLimitsOffset().getOrDefault(name, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the entityGroupLimits
|
||||||
|
*/
|
||||||
|
public Map<String, Integer> getEntityGroupLimits() {
|
||||||
|
return Objects.requireNonNullElse(entityGroupLimits, new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the entityGroupLimitsOffset
|
||||||
|
*/
|
||||||
|
public Map<String, Integer> getEntityGroupLimitsOffset() {
|
||||||
|
if (entityGroupLimitsOffset == null) {
|
||||||
|
entityGroupLimitsOffset = new HashMap<>();
|
||||||
|
}
|
||||||
|
return entityGroupLimitsOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the limit for an entity type
|
||||||
|
*
|
||||||
|
* @param t - entity type
|
||||||
|
* @return limit or -1 for unlimited
|
||||||
|
*/
|
||||||
|
public int getEntityLimit(EntityType t) {
|
||||||
|
return getEntityLimits().getOrDefault(t, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the limit offset for an entity type
|
||||||
|
*
|
||||||
|
* @param t - entity type
|
||||||
|
* @return offset
|
||||||
|
*/
|
||||||
|
public int getEntityLimitOffset(EntityType t) {
|
||||||
|
return getEntityLimitsOffset().getOrDefault(t, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the entityLimits
|
||||||
|
*/
|
||||||
|
public Map<EntityType, Integer> getEntityLimits() {
|
||||||
|
return Objects.requireNonNullElse(entityLimits, new EnumMap<>(EntityType.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the entityLimitsOffset
|
||||||
|
*/
|
||||||
|
public Map<EntityType, Integer> getEntityLimitsOffset() {
|
||||||
|
if (entityLimitsOffset == null) {
|
||||||
|
entityLimitsOffset = new EnumMap<>(EntityType.class);
|
||||||
|
}
|
||||||
|
return entityLimitsOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -151,22 +223,156 @@ public class IslandBlockCount implements DataObject {
|
|||||||
return gameMode;
|
return gameMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see world.bentobox.bentobox.database.objects.DataObject#getUniqueId()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getUniqueId() {
|
||||||
|
return uniqueId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if no more of this material can be added to this island
|
||||||
|
*
|
||||||
|
* @param m - material
|
||||||
|
* @return true if no more material can be added
|
||||||
|
*/
|
||||||
|
public boolean isAtLimit(Material m) {
|
||||||
|
// Check island limits first
|
||||||
|
return getBlockLimits().containsKey(m)
|
||||||
|
&& getBlockCounts().getOrDefault(m, 0) >= getBlockLimit(m) + this.getBlockLimitOffset(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this material is at or over a limit
|
||||||
|
*
|
||||||
|
* @param material - block material
|
||||||
|
* @param limit - limit to check
|
||||||
|
* @return true if count is >= limit
|
||||||
|
*/
|
||||||
|
public boolean isAtLimit(Material material, int limit) {
|
||||||
|
return getBlockCounts().getOrDefault(material, 0) >= limit + this.getBlockLimitOffset(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBlockLimited(Material m) {
|
||||||
|
return getBlockLimits().containsKey(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the changed
|
||||||
|
*/
|
||||||
|
public boolean isChanged() {
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isGameMode(String gameMode) {
|
public boolean isGameMode(String gameMode) {
|
||||||
return this.gameMode.equals(gameMode);
|
return getGameMode().equals(gameMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param gameMode the gameMode to set
|
* Remove a material from the count
|
||||||
|
*
|
||||||
|
* @param material - material
|
||||||
*/
|
*/
|
||||||
public void setGameMode(String gameMode) {
|
public void remove(Material material) {
|
||||||
this.gameMode = gameMode;
|
getBlockCounts().put(material, getBlockCounts().getOrDefault(material, 0) - 1);
|
||||||
|
getBlockCounts().values().removeIf(v -> v <= 0);
|
||||||
|
setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the entityLimits
|
* @param blockCounts the blockCount to set
|
||||||
*/
|
*/
|
||||||
public Map<EntityType, Integer> getEntityLimits() {
|
public void setBlockCounts(Map<Material, Integer> blockCounts) {
|
||||||
return entityLimits;
|
this.blockCounts = blockCounts;
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the block limit for this material for this island
|
||||||
|
*
|
||||||
|
* @param m - material
|
||||||
|
* @param limit - maximum number allowed
|
||||||
|
*/
|
||||||
|
public void setBlockLimit(Material m, int limit) {
|
||||||
|
getBlockLimits().put(m, limit);
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param blockLimits the blockLimits to set
|
||||||
|
*/
|
||||||
|
public void setBlockLimits(Map<Material, Integer> blockLimits) {
|
||||||
|
this.blockLimits = blockLimits;
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an offset to a block limit. This will increase/decrease the value of the
|
||||||
|
* limit.
|
||||||
|
*
|
||||||
|
* @param m material
|
||||||
|
* @param blockLimitsOffset the blockLimitsOffset to set
|
||||||
|
*/
|
||||||
|
public void setBlockLimitsOffset(Material m, Integer blockLimitsOffset) {
|
||||||
|
getBlockLimitsOffset().put(m, blockLimitsOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark changed
|
||||||
|
*/
|
||||||
|
public void setChanged() {
|
||||||
|
this.changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param changed the changed to set
|
||||||
|
*/
|
||||||
|
public void setChanged(boolean changed) {
|
||||||
|
this.changed = changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an island-specific entity group limit
|
||||||
|
*
|
||||||
|
* @param name - entity group
|
||||||
|
* @param limit - limit
|
||||||
|
*/
|
||||||
|
public void setEntityGroupLimit(String name, int limit) {
|
||||||
|
getEntityGroupLimits().put(name, limit);
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param entityGroupLimits the entityGroupLimits to set
|
||||||
|
*/
|
||||||
|
public void setEntityGroupLimits(Map<String, Integer> entityGroupLimits) {
|
||||||
|
this.entityGroupLimits = entityGroupLimits;
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an offset to an entity group limit. This will increase/decrease the value
|
||||||
|
* of the limit.
|
||||||
|
*
|
||||||
|
* @param name group name
|
||||||
|
* @param entityGroupLimitsOffset the entityGroupLimitsOffset to set
|
||||||
|
*/
|
||||||
|
public void setEntityGroupLimitsOffset(String name, Integer entityGroupLimitsOffset) {
|
||||||
|
getEntityGroupLimitsOffset().put(name, entityGroupLimitsOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an island-specific entity type limit
|
||||||
|
*
|
||||||
|
* @param t - entity type
|
||||||
|
* @param limit - limit
|
||||||
|
*/
|
||||||
|
public void setEntityLimit(EntityType t, int limit) {
|
||||||
|
getEntityLimits().put(t, limit);
|
||||||
|
setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -174,31 +380,38 @@ public class IslandBlockCount implements DataObject {
|
|||||||
*/
|
*/
|
||||||
public void setEntityLimits(Map<EntityType, Integer> entityLimits) {
|
public void setEntityLimits(Map<EntityType, Integer> entityLimits) {
|
||||||
this.entityLimits = entityLimits;
|
this.entityLimits = entityLimits;
|
||||||
|
setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set an island-specific entity type limit
|
* Set an offset to an entity limit. This will increase/decrease the value of
|
||||||
* @param t - entity type
|
* the limit.
|
||||||
* @param limit - limit
|
*
|
||||||
|
* @param type Entity Type
|
||||||
|
* @param entityLimitsOffset the entityLimitsOffset to set
|
||||||
*/
|
*/
|
||||||
public void setEntityLimit(EntityType t, int limit) {
|
public void setEntityLimitsOffset(EntityType type, Integer entityLimitsOffset) {
|
||||||
entityLimits.put(t, limit);
|
this.getEntityLimitsOffset().put(type, entityLimitsOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the limit for an entity type
|
* @param gameMode the gameMode to set
|
||||||
* @param t - entity type
|
|
||||||
* @return limit or -1 for unlimited
|
|
||||||
*/
|
*/
|
||||||
public int getEntityLimit(EntityType t) {
|
public void setGameMode(String gameMode) {
|
||||||
return entityLimits.getOrDefault(t, -1);
|
this.gameMode = gameMode;
|
||||||
|
setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Clear all island-specific entity type limits
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see
|
||||||
|
* world.bentobox.bentobox.database.objects.DataObject#setUniqueId(java.lang.
|
||||||
|
* String)
|
||||||
*/
|
*/
|
||||||
public void clearEntityLimits() {
|
@Override
|
||||||
entityLimits.clear();
|
public void setUniqueId(String uniqueId) {
|
||||||
|
this.uniqueId = uniqueId;
|
||||||
|
setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,22 @@
|
|||||||
name: Limits
|
name: Limits
|
||||||
main: world.bentobox.limits.Limits
|
main: world.bentobox.limits.Limits
|
||||||
version: ${version}${build.number}
|
version: ${version}${build.number}
|
||||||
api-version: 1.12
|
api-version: 2.7.1
|
||||||
|
|
||||||
authors: tastybento
|
authors: tastybento
|
||||||
|
|
||||||
softdepend: AcidIsland, BSkyBlock, CaveBlock
|
softdepend: AcidIsland, BSkyBlock, CaveBlock, AOneBlock, SkyGrid
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
acidisland.limits.player.limits:
|
'[gamemode].limits.player.limits':
|
||||||
description: Player can use limits command
|
description: Player can use limits command
|
||||||
default: true
|
default: true
|
||||||
acidisland.limits.player.recount:
|
'[gamemode].limits.player.recount':
|
||||||
description: Player can use recount command
|
description: Player can use recount command
|
||||||
default: true
|
default: true
|
||||||
acidisland.limits.admin.limits:
|
'[gamemode].limits.admin.limits':
|
||||||
description: Player can use admin limits command
|
description: Player can use admin limits command
|
||||||
default: op
|
default: op
|
||||||
bskyblock.limits.player.limits:
|
'[gamemode].mod.bypass':
|
||||||
description: Player can use limits command
|
description: Player can bypass limits
|
||||||
default: true
|
|
||||||
bskyblock.limits.player.recount:
|
|
||||||
description: Player can use recount command
|
|
||||||
default: true
|
|
||||||
bskyblock.limits.admin.limits:
|
|
||||||
description: Player can use admin limits command
|
|
||||||
default: op
|
|
||||||
caveblock.limits.player.limits:
|
|
||||||
description: Player can use limits command
|
|
||||||
default: true
|
|
||||||
caveblock.limits.player.recount:
|
|
||||||
description: Player can use recount command
|
|
||||||
default: true
|
|
||||||
caveblock.limits.admin.limits:
|
|
||||||
description: Player can use admin limits command
|
|
||||||
default: op
|
default: op
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
gamemodes:
|
gamemodes:
|
||||||
- AcidIsland
|
- AcidIsland
|
||||||
- BSkyBlock
|
- BSkyBlock
|
||||||
- CaveBock
|
- CaveBlock
|
||||||
|
|
||||||
|
# Ignore this island's center block. For most worlds, this is bedrock, but for AOneBlock it is
|
||||||
|
# the magic block, so ignoring it from limits makes sense.
|
||||||
|
ignore-center-block: true
|
||||||
|
|
||||||
# Permissions
|
# Permissions
|
||||||
# Island owners can be given permissions that override all general settings
|
# Island owners can be given permissions that override all general settings
|
||||||
@ -13,6 +17,9 @@ gamemodes:
|
|||||||
# Cooldown for player recount command in seconds
|
# Cooldown for player recount command in seconds
|
||||||
cooldown: 120
|
cooldown: 120
|
||||||
|
|
||||||
|
# Use async checks for snowmen and iron golums. Set to false if you see problems.
|
||||||
|
async-golums: true
|
||||||
|
|
||||||
# General block limiting
|
# General block limiting
|
||||||
# Use this section to limit how many blocks can be added to an island.
|
# Use this section to limit how many blocks can be added to an island.
|
||||||
# 0 means the item will be blocked from placement completely.
|
# 0 means the item will be blocked from placement completely.
|
||||||
@ -33,5 +40,91 @@ worlds:
|
|||||||
entitylimits:
|
entitylimits:
|
||||||
ENDERMAN: 5
|
ENDERMAN: 5
|
||||||
CHICKEN: 10
|
CHICKEN: 10
|
||||||
pig_ZOMbIe: 15
|
|
||||||
|
# Entity Groups
|
||||||
|
# Name the group anything you like
|
||||||
|
# Select an icon, which is a Bukkit Material
|
||||||
|
# Select the limit for the total group
|
||||||
|
# List the entities in the group using Bukkit EntityTypes
|
||||||
|
entitygrouplimits:
|
||||||
|
Monsters:
|
||||||
|
icon: ROTTEN_FLESH
|
||||||
|
limit: 250
|
||||||
|
entities:
|
||||||
|
- SKELETON
|
||||||
|
- SILVERFISH
|
||||||
|
- STRAY
|
||||||
|
- ZOMBIE_VILLAGER
|
||||||
|
- WITHER
|
||||||
|
- WARDEN
|
||||||
|
- BLAZE
|
||||||
|
- DROWNED
|
||||||
|
- BREEZE
|
||||||
|
- ZOMBIFIED_PIGLIN
|
||||||
|
- EVOKER
|
||||||
|
- PILLAGER
|
||||||
|
- HUSK
|
||||||
|
- CREEPER
|
||||||
|
- VINDICATOR
|
||||||
|
- ZOMBIE
|
||||||
|
- ENDERMAN
|
||||||
|
- ELDER_GUARDIAN
|
||||||
|
- WITHER_SKELETON
|
||||||
|
- CAVE_SPIDER
|
||||||
|
- GUARDIAN
|
||||||
|
- RAVAGER
|
||||||
|
- PIGLIN
|
||||||
|
- BOGGED
|
||||||
|
- WITCH
|
||||||
|
- ENDERMITE
|
||||||
|
- ZOGLIN
|
||||||
|
- PIGLIN_BRUTE
|
||||||
|
- ILLUSIONER
|
||||||
|
- SPIDER
|
||||||
|
- VEX
|
||||||
|
Animals:
|
||||||
|
icon: SADDLE
|
||||||
|
limit: 200
|
||||||
|
entities:
|
||||||
|
- SHEEP
|
||||||
|
- AXOLOTL
|
||||||
|
- DONKEY
|
||||||
|
- MOOSHROOM
|
||||||
|
- TRADER_LLAMA
|
||||||
|
- BEE
|
||||||
|
- HORSE
|
||||||
|
- ZOMBIE_HORSE
|
||||||
|
- PIG
|
||||||
|
- PARROT
|
||||||
|
- CHICKEN
|
||||||
|
- RABBIT
|
||||||
|
- SNIFFER
|
||||||
|
- FROG
|
||||||
|
- GOAT
|
||||||
|
- PANDA
|
||||||
|
- CAMEL
|
||||||
|
- STRIDER
|
||||||
|
- TURTLE
|
||||||
|
- CAT
|
||||||
|
- SKELETON_HORSE
|
||||||
|
- COW
|
||||||
|
- LLAMA
|
||||||
|
- ARMADILLO
|
||||||
|
- HOGLIN
|
||||||
|
- POLAR_BEAR
|
||||||
|
- WOLF
|
||||||
|
- MULE
|
||||||
|
- OCELOT
|
||||||
|
- FOX
|
||||||
|
- DOLPHIN
|
||||||
|
- COD
|
||||||
|
- PUFFERFISH
|
||||||
|
- TADPOLE
|
||||||
|
- SQUID
|
||||||
|
- SALMON
|
||||||
|
- TROPICAL_FISH
|
||||||
|
- GLOW_SQUID
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,35 +1,63 @@
|
|||||||
###########################################################################################
|
---
|
||||||
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
|
|
||||||
# the one at http://yaml-online-parser.appspot.com #
|
|
||||||
# #
|
|
||||||
# Translation by: CZghost #
|
|
||||||
###########################################################################################
|
|
||||||
|
|
||||||
block-limits:
|
block-limits:
|
||||||
hit-limit: "&c[material] omezen na [number]!"
|
hit-limit: "&c[material] omezen na [number]!"
|
||||||
entity-limits:
|
entity-limits:
|
||||||
hit-limit: "&cSpawnování [entity] omezeno na [number]!"
|
hit-limit: "&cSpawnování [entity] omezeno na [number]!"
|
||||||
limits:
|
limits:
|
||||||
panel-title: "Omezení ostrovů"
|
panel-title: Omezení ostrovů
|
||||||
|
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
limits:
|
limits:
|
||||||
main:
|
main:
|
||||||
parameters: "<player>"
|
parameters: "<player>"
|
||||||
description: "ukázat omezení ostrova hráče"
|
description: ukázat omezení ostrova hráče
|
||||||
calc:
|
calc:
|
||||||
parameters: "<player>"
|
parameters: "<player>"
|
||||||
description: "přepočítat omezení ostrova hráče"
|
description: přepočítat omezení ostrova hráče
|
||||||
finished: "&aPřepočítání ostrova úspěšně dokončeno!"
|
finished: "&aPřepočítání ostrova úspěšně dokončeno!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Neznámý materiál nebo entita [name]."
|
||||||
|
main:
|
||||||
|
description: umožňuje spravovat limity offsetů pro materiály a entity
|
||||||
|
set:
|
||||||
|
parameters: "<hráč> <materiál|entita> <číslo>"
|
||||||
|
description: nastaví nový offset pro limit materiálu nebo entity
|
||||||
|
success: "&a Mezní posun pro [name] je nastaven na [number]."
|
||||||
|
same: "&c Mezní posun pro [name] je již [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<hráč> <materiál|entita> <číslo>"
|
||||||
|
description: přidá offset pro limit materiálu nebo entity
|
||||||
|
success: "&a Posun limitu pro [name] se zvýší na [number]."
|
||||||
|
remove:
|
||||||
|
parameters: "<hráč> <materiál|entita> <číslo>"
|
||||||
|
description: snižuje offset pro limit materiálu nebo entity
|
||||||
|
success: "&a Posun limitu pro [name] se sníží na [number]."
|
||||||
|
reset:
|
||||||
|
parameters: "<hráč> <materiál|entita>"
|
||||||
|
description: odstraní offset pro materiál nebo entitu
|
||||||
|
success: "&a Mezní posun pro [jméno] je nastaven na 0."
|
||||||
|
view:
|
||||||
|
parameters: "<hráč> <materiál|entita>"
|
||||||
|
description: zobrazuje posun pro materiál nebo entitu
|
||||||
|
message: "&odsazení [name] je nastaveno na [number]."
|
||||||
island:
|
island:
|
||||||
limits:
|
limits:
|
||||||
description: "ukázat omezení tvého ostrova"
|
description: ukázat omezení tvého ostrova
|
||||||
max-color: "&c"
|
max-color: "&c"
|
||||||
regular-color: "&a"
|
regular-color: "&a"
|
||||||
block-limit-syntax: "[number]/[limit]"
|
block-limit-syntax: "[number]/[limit]"
|
||||||
no-limits: "&cTento svět nemá žádné omezení"
|
no-limits: "&c V tomto světě nejsou stanovena žádná omezení"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Tento ostrov nemá vlastníka"
|
||||||
|
not-on-island: "&c Toto umístění nemá nastavena omezení."
|
||||||
recount:
|
recount:
|
||||||
description: "přepočítá omezení tvého ostrova"
|
description: přepočítá omezení tvého ostrova
|
||||||
|
now-recounting: "&b Nyní vyprávění. Může to chvíli trvat, čekejte prosím..."
|
||||||
|
in-progress: "&c Probíhá obnovení ostrova. Čekejte prosím..."
|
||||||
|
time-out: "&c Časový limit při přepočítávání. Je ostrov opravdu velký?"
|
||||||
|
@ -14,12 +14,51 @@ admin:
|
|||||||
parameters: "<player>"
|
parameters: "<player>"
|
||||||
description: Berechne die Insel Limitierungen für den Spieler neu
|
description: Berechne die Insel Limitierungen für den Spieler neu
|
||||||
finished: "&aInselberechnung erfolgreich abgeschlossen!"
|
finished: "&aInselberechnung erfolgreich abgeschlossen!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Unbekanntes Material oder Entität [name]."
|
||||||
|
main:
|
||||||
|
description: ermöglicht die Verwaltung von Grenzwertverschiebungen für Materialien
|
||||||
|
und Entitäten
|
||||||
|
set:
|
||||||
|
parameters: "<Spieler> <Material|Entität> <Nummer>"
|
||||||
|
description: legt einen neuen Offset für Material- oder Entity-Grenzwert fest
|
||||||
|
success: "&a Der Grenzoffset für [name] ist auf [number] eingestellt."
|
||||||
|
same: "&c Der Grenzoffset für [name] ist bereits [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<Spieler> <Material|Entität> <Nummer>"
|
||||||
|
description: fügt einen Offset für Material- oder Entitätslimit hinzu
|
||||||
|
success: "&a Der Limit-Offset für [name] wird bis [number] erhöht."
|
||||||
|
remove:
|
||||||
|
parameters: "<Spieler> <Material|Entität> <Nummer>"
|
||||||
|
description: reduziert den Offset für Material- oder Entitätslimit
|
||||||
|
success: "&a Der Grenzoffset für [name] wird auf [number] reduziert."
|
||||||
|
reset:
|
||||||
|
parameters: "<Spieler> <Material|Entität>"
|
||||||
|
description: Entfernt den Offset für Material oder Entität
|
||||||
|
success: "&a Der Grenzoffset für [name] wird auf 0 gesetzt."
|
||||||
|
view:
|
||||||
|
parameters: "<Spieler> <Material|Entität>"
|
||||||
|
description: zeigt den Offset für Material oder Entität an
|
||||||
|
message: "&a [name]-Offset ist auf [number] gesetzt."
|
||||||
island:
|
island:
|
||||||
limits:
|
limits:
|
||||||
description: Zeige deine Insel Limitierungen
|
description: Zeige deine Insel Limitierungen
|
||||||
max-color: "&c"
|
max-color: "&c"
|
||||||
regular-color: "&a"
|
regular-color: "&a"
|
||||||
block-limit-syntax: "[number]/[limit]"
|
block-limit-syntax: "[number]/[limit]"
|
||||||
no-limits: "&cDiese Welt hat keine Limitierungen"
|
no-limits: "&c Es gibt keine Grenzen in dieser Welt"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Diese Insel hat keinen Besitzer"
|
||||||
|
not-on-island: "&c Für diesen Standort sind keine Beschränkungen festgelegt."
|
||||||
recount:
|
recount:
|
||||||
description: Zählt die Limitierungen für deine Insel auf
|
description: Zählt die Limitierungen für deine Insel auf
|
||||||
|
now-recounting: "&b Ich erzähle jetzt. Dies kann eine Weile dauern, bitte warten..."
|
||||||
|
in-progress: "&c Inselrückmeldung läuft. Warten Sie mal..."
|
||||||
|
time-out: "&c Zeitüberschreitung beim Erzählen. Ist die Insel wirklich so groß?"
|
||||||
|
@ -10,7 +10,6 @@ entity-limits:
|
|||||||
limits:
|
limits:
|
||||||
panel-title: "Island limits"
|
panel-title: "Island limits"
|
||||||
|
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
limits:
|
limits:
|
||||||
main:
|
main:
|
||||||
@ -19,15 +18,51 @@ admin:
|
|||||||
calc:
|
calc:
|
||||||
parameters: "<player>"
|
parameters: "<player>"
|
||||||
description: "recalculate the island limits for player"
|
description: "recalculate the island limits for player"
|
||||||
finished: "&aIsland recalc finished successfully!"
|
finished: "&a Island recalc finished successfully!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Unknown material or entity [name]."
|
||||||
|
description: "allows to manage limits offsets for materials and entities"
|
||||||
|
set:
|
||||||
|
parameters: "<player> <material|entity> <number>"
|
||||||
|
description: "sets new offset for material or entity limit"
|
||||||
|
success: "&a Limit offset for [name] is set to [number]."
|
||||||
|
same: "&c Limit offset for [name] is already [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<player> <material|entity> <number>"
|
||||||
|
description: "adds offset for material or entity limit"
|
||||||
|
success: "&a Limit offset for [name] is increased till [number]."
|
||||||
|
remove:
|
||||||
|
parameters: "<player> <material|entity> <number>"
|
||||||
|
description: "reduces offset for material or entity limit"
|
||||||
|
success: "&a Limit offset for [name] is reduced till [number]."
|
||||||
|
reset:
|
||||||
|
parameters: "<player> <material|entity>"
|
||||||
|
description: "removes offset for material or entity"
|
||||||
|
success: "&a Limit offset for [name] is set to 0."
|
||||||
|
view:
|
||||||
|
parameters: "<player> <material|entity>"
|
||||||
|
description: "displays offset for material or entity"
|
||||||
|
message: "&a [name] offset is set to [number]."
|
||||||
island:
|
island:
|
||||||
limits:
|
limits:
|
||||||
description: "show your island limits"
|
description: "show your island limits"
|
||||||
max-color: "&c"
|
max-color: "&c"
|
||||||
regular-color: "&a"
|
regular-color: "&a"
|
||||||
block-limit-syntax: "[number]/[limit]"
|
block-limit-syntax: "[number]/[limit]"
|
||||||
no-limits: "&cNo limits set in this world"
|
no-limits: "&c No limits set in this world"
|
||||||
|
panel:
|
||||||
|
title-syntax: '[title] [sort]'
|
||||||
|
entity-group-name-syntax: '[name]'
|
||||||
|
entity-name-syntax: '[name]'
|
||||||
|
block-name-syntax: '[name]'
|
||||||
|
A2Z: "a > z"
|
||||||
|
Z2A: "z > a"
|
||||||
|
errors:
|
||||||
|
no-owner: "&c That island has no owner"
|
||||||
|
not-on-island: "&c This location does not have limits set."
|
||||||
recount:
|
recount:
|
||||||
description: "recounts limits for your island"
|
description: "recounts limits for your island"
|
||||||
|
now-recounting: "&b Now recounting. This could take a while, please wait..."
|
||||||
|
in-progress: "&c Island recound is in progress. Please wait..."
|
||||||
|
time-out: "&c Time out when recounting. Is the island really big?"
|
||||||
|
|
||||||
|
65
src/main/resources/locales/es.yml
Normal file
65
src/main/resources/locales/es.yml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&c[material] limitado a [number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "¡Aparición de &c[entity] limitada a [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: Límites de la isla
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<jugador>"
|
||||||
|
description: mostrar los límites de la isla para el jugador
|
||||||
|
calc:
|
||||||
|
parameters: "<jugador>"
|
||||||
|
description: recalcular los límites de la isla para el jugador
|
||||||
|
finished: "&a ¡El recálculo de la Isla terminó con éxito!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Material o entidad desconocida [name]."
|
||||||
|
main:
|
||||||
|
description: permite gestionar compensaciones de límites para materiales y
|
||||||
|
entidades
|
||||||
|
set:
|
||||||
|
parameters: "<jugador> <material|entidad> <número>"
|
||||||
|
description: establece una nueva compensación para el límite de material o
|
||||||
|
entidad
|
||||||
|
success: "&a El desplazamiento límite para [name] está establecido en [number]."
|
||||||
|
same: "&c El límite de compensación para [name] ya es [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<jugador> <material|entidad> <número>"
|
||||||
|
description: agrega compensación por límite de material o entidad
|
||||||
|
success: "&a El límite de compensación para [name] aumenta hasta [number]."
|
||||||
|
remove:
|
||||||
|
parameters: "<jugador> <material|entidad> <número>"
|
||||||
|
description: reduce la compensación por límite de material o entidad
|
||||||
|
success: "&a La compensación límite para [name] se reduce hasta [number]."
|
||||||
|
reset:
|
||||||
|
parameters: "<jugador> <material|entidad>"
|
||||||
|
description: elimina la compensación para material o entidad
|
||||||
|
success: "&a El desplazamiento límite para [number] está establecido en 0."
|
||||||
|
view:
|
||||||
|
parameters: "<jugador> <material|entidad>"
|
||||||
|
description: muestra el desplazamiento para material o entidad
|
||||||
|
message: "& desplazamiento de [name] se establece en [number]."
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: muestra los límites de tu isla
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&c No hay límites establecidos en este mundo."
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Esa isla no tiene dueño"
|
||||||
|
not-on-island: "&c Esta ubicación no tiene límites establecidos."
|
||||||
|
recount:
|
||||||
|
description: cuenta los límites para tu isla
|
||||||
|
now-recounting: "&b Ahora contando. Esto podría tomar un tiempo, por favor espere..."
|
||||||
|
in-progress: "&c El recuento de la isla está en progreso. Espere por favor..."
|
||||||
|
time-out: "&c Ha pasado demasiado tiempo contando. ¿La isla es realmente grande?"
|
66
src/main/resources/locales/fr.yml
Normal file
66
src/main/resources/locales/fr.yml
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&c[material] limité à [number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&c[entity] spawning limited to [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: Limites de l'île
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<player>"
|
||||||
|
description: affiche les limites de l'île pour le joueur
|
||||||
|
calc:
|
||||||
|
parameters: "<player>"
|
||||||
|
description: recalcule les limites de l'île pour le joueur
|
||||||
|
finished: "&a Recomptage terminé avec succès!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Matériau ou entité inconnu [name]."
|
||||||
|
main:
|
||||||
|
description: permet de gérer les décalages de limites pour les matériaux et
|
||||||
|
les entités
|
||||||
|
set:
|
||||||
|
parameters: "<joueur> <matériel|entité> <numéro>"
|
||||||
|
description: définit un nouveau décalage pour la limite de matériau ou d'entité
|
||||||
|
success: "&a Le décalage limite pour [name] est défini sur [number]."
|
||||||
|
same: "&c Le décalage limite pour [name] est déjà [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<joueur> <matériel|entité> <numéro>"
|
||||||
|
description: ajoute un décalage pour la limite de matériau ou d'entité
|
||||||
|
success: "&a Le décalage limite pour [name] est augmenté jusqu'à [number]."
|
||||||
|
remove:
|
||||||
|
parameters: "<joueur> <matériel|entité> <numéro>"
|
||||||
|
description: réduit le décalage pour la limite de matériau ou d'entité
|
||||||
|
success: "&a Le décalage limite pour [name] est réduit jusqu'à [number]."
|
||||||
|
reset:
|
||||||
|
parameters: "<joueur> <matériel|entité>"
|
||||||
|
description: supprime le décalage pour le matériau ou l'entité
|
||||||
|
success: "&a Le décalage limite pour [name] est défini sur 0."
|
||||||
|
view:
|
||||||
|
parameters: "<joueur> <matériel|entité>"
|
||||||
|
description: affiche le décalage pour le matériau ou l'entité
|
||||||
|
message: "&a [name] le décalage est défini sur [number]."
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: affichez les limites de votre île
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&c Aucune limite n'est fixée dans ce monde"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Cette île n'a pas de propriétaire"
|
||||||
|
not-on-island: "&c Cet emplacement n'a pas de limites définies."
|
||||||
|
recount:
|
||||||
|
description: recompte les limites de votre île
|
||||||
|
now-recounting: "&b Recomptage en cours. Cela peut prendre un certain temps,
|
||||||
|
veuillez patienter..."
|
||||||
|
in-progress: "&c Le recomptage de l'île est en cours. Veuillez patienter s'il
|
||||||
|
vous plaît..."
|
||||||
|
time-out: "&c Time out lors du recomptage. L'île est-elle vraiment grande?"
|
63
src/main/resources/locales/hr.yml
Normal file
63
src/main/resources/locales/hr.yml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&c[material] ograničen na [number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&c[entity] stvaranje ograničeno na [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: Granice otoka
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<igrač>"
|
||||||
|
description: pokazati ograničenja otoka za igrača
|
||||||
|
calc:
|
||||||
|
parameters: "<igrač>"
|
||||||
|
description: ponovno izračunati ograničenja otoka za igrača
|
||||||
|
finished: "&a Recalc otoka uspješno završen!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Nepoznati materijal ili entitet [name]."
|
||||||
|
main:
|
||||||
|
description: omogućuje upravljanje pomacima ograničenja za materijale i entitete
|
||||||
|
set:
|
||||||
|
parameters: "<igrač> <materijal|entitet> <broj>"
|
||||||
|
description: postavlja novi pomak za ograničenje materijala ili entiteta
|
||||||
|
success: "&a Ograničenje pomaka za [name] postavljeno je na [number]."
|
||||||
|
same: "&c Ograničenje pomaka za [name] već je [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<igrač> <materijal|entitet> <broj>"
|
||||||
|
description: dodaje pomak za ograničenje materijala ili entiteta
|
||||||
|
success: "&a Ograničenje pomaka za [name] povećava se do [number]."
|
||||||
|
remove:
|
||||||
|
parameters: "<igrač> <materijal|entitet> <broj>"
|
||||||
|
description: smanjuje pomak za ograničenje materijala ili entiteta
|
||||||
|
success: "&a Ograničenje pomaka za [name] smanjeno je do [number]."
|
||||||
|
reset:
|
||||||
|
parameters: "<igrač> <materijal|entitet>"
|
||||||
|
description: uklanja pomak za materijal ili entitet
|
||||||
|
success: "&a Ograničenje pomaka za [name] postavljeno je na 0."
|
||||||
|
view:
|
||||||
|
parameters: "<igrač> <materijal|entitet>"
|
||||||
|
description: prikazuje pomak za materijal ili entitet
|
||||||
|
message: "&pomak [name] postavljen je na [number]."
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: pokazati granice vašeg otoka
|
||||||
|
max-color: i c
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&c U ovom svijetu nema ograničenja"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Taj otok nema vlasnika"
|
||||||
|
not-on-island: "&c Ova lokacija nema postavljena ograničenja."
|
||||||
|
recount:
|
||||||
|
description: preračunava ograničenja za vaš otok
|
||||||
|
now-recounting: "&b Sada prepričavam. Ovo bi moglo potrajati, pričekajte..."
|
||||||
|
in-progress: "&c Otok je u tijeku. Molimo pričekajte..."
|
||||||
|
time-out: "&c Time out kod prepričavanja. Je li otok stvarno velik?"
|
@ -1,33 +1,63 @@
|
|||||||
###########################################################################################
|
---
|
||||||
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
|
|
||||||
# the one at http://yaml-online-parser.appspot.com #
|
|
||||||
###########################################################################################
|
|
||||||
|
|
||||||
block-limits:
|
block-limits:
|
||||||
hit-limit: "&c[material] limitálva ennyire [number]!"
|
hit-limit: "&c[material] limitálva ennyire [number]!"
|
||||||
entity-limits:
|
entity-limits:
|
||||||
hit-limit: "&c[entity] idézés limitálva ennyire [number]!"
|
hit-limit: "&c[entity] idézés limitálva ennyire [number]!"
|
||||||
limits:
|
limits:
|
||||||
panel-title: "Sziget limitek"
|
panel-title: Sziget limitek
|
||||||
|
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
limits:
|
limits:
|
||||||
main:
|
main:
|
||||||
parameters: "<player>"
|
parameters: "<player>"
|
||||||
description: "Egy játékos sziget limitjének megtekintése"
|
description: Egy játékos sziget limitjének megtekintése
|
||||||
calc:
|
calc:
|
||||||
parameters: "<player>"
|
parameters: "<player>"
|
||||||
description: "Egy játékos sziget limitjének újraszámolása"
|
description: Egy játékos sziget limitjének újraszámolása
|
||||||
finished: "&aA Sziget újraszámolás sikeresen befejeződött!"
|
finished: "&aA Sziget újraszámolás sikeresen befejeződött!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Ismeretlen anyag vagy entitás [name]."
|
||||||
|
main:
|
||||||
|
description: lehetővé teszi az anyagok és entitások limiteltolásainak kezelését
|
||||||
|
set:
|
||||||
|
parameters: "<lejátszó> <anyag|entitás> <szám>"
|
||||||
|
description: új eltolást állít be az anyag- vagy entitáskorláthoz
|
||||||
|
success: "&a A [name] határeltolódása [number] értékre van állítva."
|
||||||
|
same: "&c A [name] korláteltolása már [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<lejátszó> <anyag|entitás> <szám>"
|
||||||
|
description: eltolást ad hozzá az anyag- vagy entitáskorláthoz
|
||||||
|
success: "&a A [name] határeltolódása a [number] értékig nő."
|
||||||
|
remove:
|
||||||
|
parameters: "<lejátszó> <anyag|entitás> <szám>"
|
||||||
|
description: csökkenti az anyag- vagy entitáskorlát ellentételezését
|
||||||
|
success: "&a A [name] limiteltolása [number] értékre csökken."
|
||||||
|
reset:
|
||||||
|
parameters: "<lejátszó> <anyag|entitás>"
|
||||||
|
description: eltávolítja az anyag vagy entitás eltolását
|
||||||
|
success: "&a A [name] határeltolódása 0-ra van állítva."
|
||||||
|
view:
|
||||||
|
parameters: "<lejátszó> <anyag|entitás>"
|
||||||
|
description: anyag vagy entitás eltolását jeleníti meg
|
||||||
|
message: "&a [name] eltolás értéke [number]."
|
||||||
island:
|
island:
|
||||||
limits:
|
limits:
|
||||||
description: "Sziget limitek megtekintése"
|
description: Sziget limitek megtekintése
|
||||||
max-color: "&c"
|
max-color: "&c"
|
||||||
regular-color: "&a"
|
regular-color: "&a"
|
||||||
block-limit-syntax: "[number]/[limit]"
|
block-limit-syntax: "[number]/[limit]"
|
||||||
no-limits: "&cNincsenek limitek ebben a világban"
|
no-limits: "&c Nincsenek korlátok ezen a világon"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Annak a szigetnek nincs gazdája"
|
||||||
|
not-on-island: "&c Ennek a helynek nincs korlátja beállítva."
|
||||||
recount:
|
recount:
|
||||||
description: "Limitek újraszámolása a szigeteden"
|
description: Limitek újraszámolása a szigeteden
|
||||||
|
now-recounting: "&b Most mesélünk. Ez eltarthat egy ideig, kérem, várjon..."
|
||||||
|
in-progress: "&c Sziget visszaállítása folyamatban van. Kérem, várjon..."
|
||||||
|
time-out: "&c Időtúllépés az újraszámláláskor. Tényleg nagy a sziget?"
|
||||||
|
65
src/main/resources/locales/id.yml
Normal file
65
src/main/resources/locales/id.yml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&c[material] dibatasi sampai [number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&c[entity] spawning dibatasi sampai [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: Batasan Pulau
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<pemain>"
|
||||||
|
description: menampilkan batasan pulau untuk pemain
|
||||||
|
calc:
|
||||||
|
parameters: "<pemain>"
|
||||||
|
description: menghitung ulang batasan pulau untuk pemain
|
||||||
|
finished: "&aPenghitungan ulang pulau selesai tanpa masalah!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Material atau entitas tidak dikenal [name]."
|
||||||
|
main:
|
||||||
|
description: memungkinkan untuk mengelola batas offset untuk material dan
|
||||||
|
entitas
|
||||||
|
set:
|
||||||
|
parameters: "<pemain> <material|entitas> <angka>"
|
||||||
|
description: menetapkan offset baru untuk batasan material atau entitas
|
||||||
|
success: "&a Batas offset untuk [name] ditetapkan ke [number]."
|
||||||
|
same: "&a Batas offset untuk [name] ditetapkan ke [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<pemain> <material|entitas> <angka>"
|
||||||
|
description: menambahkan offset untuk batasan material atau entitas
|
||||||
|
success: "&a Batas offset untuk [name] ditingkatkan hingga [number]."
|
||||||
|
remove:
|
||||||
|
parameters: "<pemain> <material|entitas> <angka>"
|
||||||
|
description: mengurangi offset untuk batas material atau entitas
|
||||||
|
success: "&a Batas offset untuk [name] dikurangi hingga [number]."
|
||||||
|
reset:
|
||||||
|
parameters: "<pemain> <material|entitas>"
|
||||||
|
description: menghapus offset untuk material atau entitas
|
||||||
|
success: "&a Batas offset untuk [name] ditetapkan ke 0."
|
||||||
|
view:
|
||||||
|
parameters: "<pemain> <material|entitas>"
|
||||||
|
description: menampilkan offset untuk material atau entitas
|
||||||
|
message: "&a [name] offset diatur ke [number]."
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: menampilkan batasan pulau kamu
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&c Tidak ada batasan yang ditetapkan di dunia ini"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Pulau itu tidak memiliki pemilik"
|
||||||
|
not-on-island: "&c Lokasi ini tidak memiliki batasan yang ditetapkan."
|
||||||
|
recount:
|
||||||
|
description: menghitung ulang batasan untuk pulau kamu
|
||||||
|
now-recounting: "&b Menghitung ulang. Membutuhkan waktu beberapa saat, silahkan
|
||||||
|
tunggu..."
|
||||||
|
in-progress: "&c Perhitungan pulau sedang diproses. Silahkan tunggu..."
|
||||||
|
time-out: "&c Gagal menghitung ulang. Apakah pulau terlalu besar?"
|
63
src/main/resources/locales/it.yml
Normal file
63
src/main/resources/locales/it.yml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&c[material] limitato a [number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&c[entity] la generazione è limitata a [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: Limiti dell'isola
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<giocatore>"
|
||||||
|
description: mostra i limiti dell'isola per il giocatore
|
||||||
|
calc:
|
||||||
|
parameters: "<giocatore>"
|
||||||
|
description: ricalcola i limiti dell'isola per il giocatore
|
||||||
|
finished: "&a Il ricalcolo dell'isola è stato completato con successo!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Materiale o entità sconosciuta [name]."
|
||||||
|
main:
|
||||||
|
description: consente di gestire gli offset dei limiti per materiali ed entità
|
||||||
|
set:
|
||||||
|
parameters: "<giocatore> <materiale|entità> <numero>"
|
||||||
|
description: imposta un nuovo offset per il limite di materiale o entità
|
||||||
|
success: "&a Il limite di offset per [name] è impostato su [number]."
|
||||||
|
same: "&c Il limite di offset per [name] è già [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<giocatore> <materiale|entità> <numero>"
|
||||||
|
description: aggiunge offset per limite di materiale o entità
|
||||||
|
success: "&a Il limite di offset per [name] è aumentato fino a [number]."
|
||||||
|
remove:
|
||||||
|
parameters: "<giocatore> <materiale|entità> <numero>"
|
||||||
|
description: riduce l'offset per il limite di materiale o entità
|
||||||
|
success: "&a Il limite di offset per [name] è ridotto fino a [number]."
|
||||||
|
reset:
|
||||||
|
parameters: "<giocatore> <materiale|entità>"
|
||||||
|
description: rimuove l'offset per materiale o entità
|
||||||
|
success: "&a Il limite di offset per [name] è impostato su 0."
|
||||||
|
view:
|
||||||
|
parameters: "<giocatore> <materiale|entità>"
|
||||||
|
description: visualizza l'offset per materiale o entità
|
||||||
|
message: "&a [name] offset è impostato su [number]."
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: mostra i limiti della tua isola
|
||||||
|
max-color: "&C"
|
||||||
|
regular-color: "&UN"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&c Non ci sono limiti in questo mondo"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Quell'isola non ha proprietario"
|
||||||
|
not-on-island: "&c Questa posizione non ha limiti impostati."
|
||||||
|
recount:
|
||||||
|
description: racconta i limiti per la tua isola
|
||||||
|
now-recounting: "&b Ora sto raccontando. Potrebbe volerci un po', attendi..."
|
||||||
|
in-progress: "&c Il conteggio dell'isola è in corso. Attendi..."
|
||||||
|
time-out: "&c Time out quando si racconta. L'isola è davvero grande?"
|
@ -1,23 +1,62 @@
|
|||||||
---
|
|
||||||
block-limits:
|
block-limits:
|
||||||
hit-limit: "&c[material]は[number]に制限されています!"
|
hit-limit: '&c[material]は[number]に制限されています!'
|
||||||
entity-limits:
|
entity-limits:
|
||||||
hit-limit: "&c[entity]の生成は[number]に制限されています!"
|
hit-limit: '&c[entity]の生成は[number]に制限されています!'
|
||||||
|
limits:
|
||||||
|
panel-title: 島の制限
|
||||||
admin:
|
admin:
|
||||||
limits:
|
limits:
|
||||||
main:
|
main:
|
||||||
parameters: "<プレイヤー>"
|
parameters: <プレイヤー>
|
||||||
description: プレイヤーの島の制限を表示する
|
description: プレイヤーの島の制限を表示する
|
||||||
calc:
|
calc:
|
||||||
parameters: "<プレイヤー>"
|
parameters: <プレイヤー>
|
||||||
finished: "&a島の再計算が正常に完了しました!"
|
|
||||||
description: プレイヤーの島の制限を再計算します
|
description: プレイヤーの島の制限を再計算します
|
||||||
|
finished: '&a 島の再計算が正常に完了しました!'
|
||||||
|
offset:
|
||||||
|
unknown: '&c 不明な素材または実体 [name]。'
|
||||||
|
main:
|
||||||
|
description: 材料とエンティティの制限オフセットを管理できます
|
||||||
|
set:
|
||||||
|
parameters: <プレイヤー> <マテリアル|エンティティ> <番号>
|
||||||
|
description: 材料またはエンティティの制限の新しいオフセットを設定します
|
||||||
|
success: '&a [name] の制限オフセットが [number] に設定されています。'
|
||||||
|
same: '&c [name] の制限オフセットはすでに [number] です。'
|
||||||
|
add:
|
||||||
|
parameters: <プレイヤー> <マテリアル|エンティティ> <番号>
|
||||||
|
description: 材料またはエンティティの制限のオフセットを追加します
|
||||||
|
success: '&a [name] の制限オフセットが [number] まで増加されます。'
|
||||||
|
remove:
|
||||||
|
parameters: <プレイヤー> <マテリアル|エンティティ> <番号>
|
||||||
|
description: 材料またはエンティティの制限のオフセットを削減します
|
||||||
|
success: '&a [name] の制限オフセットが [number] まで削減されます。'
|
||||||
|
reset:
|
||||||
|
parameters: <プレイヤー> <マテリアル|エンティティ>
|
||||||
|
description: マテリアルまたはエンティティのオフセットを削除します
|
||||||
|
success: '&a [name] の制限オフセットが 0 に設定されています。'
|
||||||
|
view:
|
||||||
|
parameters: <プレイヤー> <マテリアル|エンティティ>
|
||||||
|
description: 材料またはエンティティのオフセットを表示します
|
||||||
|
message: '&a [name] オフセットが [number] に設定されています。'
|
||||||
island:
|
island:
|
||||||
limits:
|
limits:
|
||||||
block-limit-syntax: "[number]/[limit]"
|
|
||||||
description: 島の限界を示す
|
description: 島の限界を示す
|
||||||
max-color: "&c"
|
block-limit-syntax: '[number]/[limit]'
|
||||||
regular-color: "&a"
|
no-limits: この世に限界はない
|
||||||
no-limits: "&cこの世界には制限がありません"
|
panel:
|
||||||
limits:
|
title-syntax: '[title] [sort]'
|
||||||
panel-title: 島の制限
|
entity-group-name-syntax: '[name]'
|
||||||
|
entity-name-syntax: '[name]'
|
||||||
|
block-name-syntax: '[name]'
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: その島には所有者がいない
|
||||||
|
not-on-island: '&c この場所には制限が設定されていません。'
|
||||||
|
recount:
|
||||||
|
description: あなたの島の限界を語る
|
||||||
|
now-recounting: '&b 今、話を戻します。これにはしばらく時間がかかるかもしれませんので、お待ちください...'
|
||||||
|
in-progress: '&c 島の回復作業が進行中です。お待ちください...'
|
||||||
|
time-out: '&c もう一度話すとタイムアウトになります。島は本当に大きいですか?'
|
||||||
|
max-color: '&c'
|
||||||
|
regular-color: '&a'
|
||||||
|
63
src/main/resources/locales/ko.yml
Normal file
63
src/main/resources/locales/ko.yml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&c[material]는 [number]개로 제한됩니다!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&c[entity]는 [number]개로 제한됩니다!"
|
||||||
|
limits:
|
||||||
|
panel-title: 섬의 경계
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<플레이어>"
|
||||||
|
description: 플레이어에게 섬의 한계를 보여주세요
|
||||||
|
calc:
|
||||||
|
parameters: "<플레이어>"
|
||||||
|
description: 플레이어의 섬 한계를 다시 계산합니다
|
||||||
|
finished: "&a 섬 재계산이 성공적으로 완료되었습니다!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c 알 수 없는 자료 또는 엔터티 [name]."
|
||||||
|
main:
|
||||||
|
description: 재료 및 엔티티에 대한 제한 오프셋을 관리할 수 있습니다.
|
||||||
|
set:
|
||||||
|
parameters: "<플레이어> <소재|엔티티> <숫자>"
|
||||||
|
description: 재료 또는 엔티티 제한에 대한 새로운 오프셋을 설정합니다.
|
||||||
|
success: "&a [name]에 대한 제한 오프셋이 [number]로 설정되었습니다."
|
||||||
|
same: "&c [name]에 대한 제한 오프셋은 이미 [number]입니다."
|
||||||
|
add:
|
||||||
|
parameters: "<플레이어> <소재|엔티티> <숫자>"
|
||||||
|
description: 재료 또는 엔티티 제한에 대한 오프셋을 추가합니다.
|
||||||
|
success: "&a [name]에 대한 오프셋 제한이 [number]까지 증가합니다."
|
||||||
|
remove:
|
||||||
|
parameters: "<플레이어> <소재|엔티티> <숫자>"
|
||||||
|
description: 재료 또는 엔티티 제한에 대한 오프셋을 줄입니다.
|
||||||
|
success: "&a [name]에 대한 오프셋 제한은 [number]까지 감소합니다."
|
||||||
|
reset:
|
||||||
|
parameters: "<플레이어> <소재|엔티티>"
|
||||||
|
description: 재료 또는 엔티티에 대한 오프셋을 제거합니다.
|
||||||
|
success: "&a [name]에 대한 제한 오프셋이 0으로 설정되었습니다."
|
||||||
|
view:
|
||||||
|
parameters: "<플레이어> <소재|엔티티>"
|
||||||
|
description: 재료 또는 엔티티에 대한 오프셋을 표시합니다.
|
||||||
|
message: "&a [name] 오프셋이 [number]로 설정되었습니다."
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: 섬의 경계를 보여주세요
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&c 이 세상에는 제한이 없습니다"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: 가 > 지
|
||||||
|
Z2A: 지 > 아
|
||||||
|
errors:
|
||||||
|
no-owner: "&c 그 섬에는 주인이 없다"
|
||||||
|
not-on-island: "&c 이 위치에는 제한이 설정되어 있지 않습니다."
|
||||||
|
recount:
|
||||||
|
description: 당신의 섬에 대한 한계를 다시 계산합니다
|
||||||
|
now-recounting: "&b 지금 다시 계산 중입니다. 시간이 좀 걸릴 수 있으니 기다려 주세요..."
|
||||||
|
in-progress: "&c 섬 복구가 진행 중입니다. 잠시만 기다려 주세요..."
|
||||||
|
time-out: "&c 계산할 때 시간 초과. 섬이 정말 큰가요?"
|
@ -1,25 +1,63 @@
|
|||||||
---
|
---
|
||||||
admin:
|
|
||||||
limits:
|
|
||||||
calc:
|
|
||||||
description: pārrēķinā salas ierobežojumus priekš spēlētāja
|
|
||||||
parameters: "<spēlētājs>"
|
|
||||||
finished: "&aSalas ierobežojumu pārrēķināšana pabeigta!"
|
|
||||||
main:
|
|
||||||
description: rāda salas ierobežojumus priekš spēlētāja
|
|
||||||
parameters: "<spēlētājs>"
|
|
||||||
block-limits:
|
block-limits:
|
||||||
hit-limit: "&c[material] ierobežots līdz [number]!"
|
hit-limit: "&c[material] ierobežots līdz [number]!"
|
||||||
entity-limits:
|
entity-limits:
|
||||||
hit-limit: "&c[entity] rašanās ierobežots līdz [number]!"
|
hit-limit: "&c[entity] rašanās ierobežots līdz [number]!"
|
||||||
island:
|
|
||||||
limits:
|
|
||||||
block-limit-syntax: "[number]/[limit]"
|
|
||||||
description: rāda tavas salas ierobežojumus
|
|
||||||
max-color: "&c"
|
|
||||||
no-limits: "&cŠai pasaulei nav uzstādīti ierobežojumi"
|
|
||||||
recount:
|
|
||||||
description: pārrēķina ierobežojumus tavai salai
|
|
||||||
regular-color: "&a"
|
|
||||||
limits:
|
limits:
|
||||||
panel-title: Salas ierobežojumi
|
panel-title: Salas ierobežojumi
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<spēlētājs>"
|
||||||
|
description: rāda salas ierobežojumus priekš spēlētāja
|
||||||
|
calc:
|
||||||
|
parameters: "<spēlētājs>"
|
||||||
|
description: pārrēķinā salas ierobežojumus priekš spēlētāja
|
||||||
|
finished: "&aSalas ierobežojumu pārrēķināšana pabeigta!"
|
||||||
|
offset:
|
||||||
|
unknown: un c Nezināms materiāls vai vienība [name].
|
||||||
|
main:
|
||||||
|
description: ļauj pārvaldīt limitu kompensācijas materiāliem un entītijām
|
||||||
|
set:
|
||||||
|
parameters: "<spēlētājs> <materiāls|vienība> <skaitlis>"
|
||||||
|
description: nosaka jaunu nobīdi materiāla vai entītijas ierobežojumam
|
||||||
|
success: "&a Ierobežojuma nobīde [name] ir iestatīta uz [number]."
|
||||||
|
same: "&c [name] limita nobīde jau ir [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<spēlētājs> <materiāls|vienība> <skaitlis>"
|
||||||
|
description: pievieno nobīdi materiāla vai entītijas ierobežojumam
|
||||||
|
success: "&a Ierobežojuma nobīde [name] tiek palielināta līdz [number]."
|
||||||
|
remove:
|
||||||
|
parameters: "<spēlētājs> <materiāls|vienība> <skaitlis>"
|
||||||
|
description: samazina materiāla vai entītijas limita kompensāciju
|
||||||
|
success: "&a Ierobežojuma nobīde [name] tiek samazināta līdz [number]."
|
||||||
|
reset:
|
||||||
|
parameters: "<spēlētājs> <materiāls|vienība>"
|
||||||
|
description: noņem materiāla vai entītijas nobīdi
|
||||||
|
success: "&a Ierobežojuma nobīde [name] ir iestatīta uz 0."
|
||||||
|
view:
|
||||||
|
parameters: "<spēlētājs> <materiāls|vienība>"
|
||||||
|
description: parāda nobīdi materiālam vai vienībai
|
||||||
|
message: "&a [name] nobīde ir iestatīta uz [number]."
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: rāda tavas salas ierobežojumus
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&c Šajā pasaulē nav noteikti ierobežojumi"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Tai salai nav saimnieka"
|
||||||
|
not-on-island: "&c Šai vietai nav noteikti ierobežojumi."
|
||||||
|
recount:
|
||||||
|
description: pārrēķina ierobežojumus tavai salai
|
||||||
|
now-recounting: "&b Tagad atstāsta. Tas var aizņemt kādu laiku, lūdzu, uzgaidiet..."
|
||||||
|
in-progress: "&c Notiek salas atjaunošana. Lūdzu, uzgaidiet..."
|
||||||
|
time-out: "&c Noildze, pārskaitot. Vai tiešām sala ir liela?"
|
||||||
|
65
src/main/resources/locales/nl.yml
Normal file
65
src/main/resources/locales/nl.yml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&c[material] beperkt tot [number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&c[entity] spawning beperkt tot [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: Eilandgrenzen
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<speler>"
|
||||||
|
description: toon de eilandgrenzen voor de speler
|
||||||
|
calc:
|
||||||
|
parameters: "<speler>"
|
||||||
|
description: herbereken de eilandlimieten voor de speler
|
||||||
|
finished: "&a Eiland herberekening succesvol afgerond!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Onbekend materiaal of entiteit [name]."
|
||||||
|
main:
|
||||||
|
description: maakt het mogelijk om limietoffsets voor materialen en entiteiten
|
||||||
|
te beheren
|
||||||
|
set:
|
||||||
|
parameters: "<speler> <materiaal|entiteit> <nummer>"
|
||||||
|
description: stelt nieuwe offset in voor materiaal- of entiteitslimiet
|
||||||
|
success: "&a Limietoffset voor [name] is ingesteld op [number]."
|
||||||
|
same: "&c Limietoffset voor [name] is al [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<speler> <materiaal|entiteit> <nummer>"
|
||||||
|
description: voegt offset toe voor materiaal- of entiteitslimiet
|
||||||
|
success: "&a Limietoffset voor [name] is verhoogd tot [number]."
|
||||||
|
remove:
|
||||||
|
parameters: "<speler> <materiaal|entiteit> <nummer>"
|
||||||
|
description: vermindert offset voor materiaal- of entiteitslimiet
|
||||||
|
success: "&a Limietoffset voor [name] is verlaagd tot [number]."
|
||||||
|
reset:
|
||||||
|
parameters: "<speler> <materiaal|entiteit>"
|
||||||
|
description: verwijdert offset voor materiaal of entiteit
|
||||||
|
success: "&a Limietoffset voor [name] is ingesteld op 0."
|
||||||
|
view:
|
||||||
|
parameters: "<speler> <materiaal|entiteit>"
|
||||||
|
description: geeft offset weer voor materiaal of entiteit
|
||||||
|
message: "&a [name] offset is ingesteld op [number]."
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: toon uw eilandgrenzen
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&c Er zijn geen grenzen gesteld in deze wereld"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Dat eiland heeft geen eigenaar"
|
||||||
|
not-on-island: "&c Voor deze locatie zijn geen limieten ingesteld."
|
||||||
|
recount:
|
||||||
|
description: vertelt over de grenzen van uw eiland
|
||||||
|
now-recounting: "&b Nu aan het navertellen. Dit kan even duren, even geduld
|
||||||
|
aub..."
|
||||||
|
in-progress: "&c Eilandrecovery is bezig. Even geduld..."
|
||||||
|
time-out: "&c Time-out bij het navertellen. Is het eiland echt groot?"
|
@ -1,27 +1,64 @@
|
|||||||
#
|
---
|
||||||
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
|
|
||||||
# the one at http://yaml-online-parser.appspot.com #
|
|
||||||
block-limits:
|
block-limits:
|
||||||
hit-limit: '&c[material] limitowany do [number]!'
|
hit-limit: "&c[material] limitowany do [number]!"
|
||||||
entity-limits:
|
entity-limits:
|
||||||
hit-limit: '&cSpawnowanie [entity] limitowane do [number]!'
|
hit-limit: "&cSpawnowanie [entity] limitowane do [number]!"
|
||||||
limits:
|
limits:
|
||||||
panel-title: Limity wysp
|
panel-title: Limity wysp
|
||||||
admin:
|
admin:
|
||||||
limits:
|
limits:
|
||||||
main:
|
main:
|
||||||
parameters: <gracz>
|
parameters: "<gracz>"
|
||||||
description: pokazuje limity wysp gracza
|
description: pokazuje limity wysp gracza
|
||||||
calc:
|
calc:
|
||||||
parameters: <gracz>
|
parameters: "<gracz>"
|
||||||
description: ponownie oblicza limity wyspy dla gracza
|
description: ponownie oblicza limity wyspy dla gracza
|
||||||
finished: '&aPrzeliczanie zakończone!'
|
finished: "&aPrzeliczanie zakończone!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Nieznany materiał lub podmiot [name]."
|
||||||
|
main:
|
||||||
|
description: pozwala zarządzać limitów dla materiałów i podmiotów,
|
||||||
|
set:
|
||||||
|
parameters: "<gracz> <materiał|istota> <liczba>"
|
||||||
|
description: ustawia nową wartość dla limitu materiału lub encji
|
||||||
|
success: "&a Przesunięcie limitu dla [name] jest ustawione na [number]."
|
||||||
|
same: "&c Limit dla [name] jest aktualnie [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<gracz> <materiał|istota> <liczba>"
|
||||||
|
description: dodaje przesunięcie dla limitu materiału lub potworów
|
||||||
|
success: "&a Przesunięcie limitu dla [name] jest zwiększone do [number]."
|
||||||
|
remove:
|
||||||
|
parameters: "<gracz> <materiał|istota> <liczba>"
|
||||||
|
description: zmniejsza offset dla limitu materiału lub podmiotu
|
||||||
|
success: "&a Limit dla bloku [name] \nzostał zmniejszony do [number]."
|
||||||
|
reset:
|
||||||
|
parameters: "<gracz> <materiał|istota>"
|
||||||
|
description: usuwa wartość dla materiału lub istoty
|
||||||
|
success: "&a Limit dla [name] jest ustawione na 0."
|
||||||
|
view:
|
||||||
|
parameters: "<gracz> <materiał|istota>"
|
||||||
|
description: displays offset for material or entity
|
||||||
|
message: "&a [name] wartość jest ustawiona na [number]."
|
||||||
island:
|
island:
|
||||||
limits:
|
limits:
|
||||||
description: pokazuje limity twojej wyspy
|
description: pokazuje limity twojej wyspy
|
||||||
max-color: '&c'
|
max-color: "&c"
|
||||||
regular-color: '&a'
|
regular-color: "&a"
|
||||||
block-limit-syntax: '[number]/[limit]'
|
block-limit-syntax: "[number]/[limit]"
|
||||||
no-limits: '&cBrak ustawionych limitów.'
|
no-limits: "&c Na tym świecie nie ma żadnych ograniczeń"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Ta wyspa nie ma właściciela"
|
||||||
|
not-on-island: "&c Ta lokalizacja nie ma ustalonych ograniczeń."
|
||||||
recount:
|
recount:
|
||||||
description: określa limity dla twojej wyspy
|
description: określa limity dla twojej wyspy
|
||||||
|
now-recounting: "&b Teraz przeliczam. To może chwilę potrwać, proszę czekać..."
|
||||||
|
in-progress: "&c Trwa odzyskiwanie wyspy. Proszę czekać..."
|
||||||
|
time-out: "&c Przekroczono limit czasu podczas przeliczania. Czy wyspa jest
|
||||||
|
naprawdę duża?"
|
||||||
|
63
src/main/resources/locales/pt.yml
Normal file
63
src/main/resources/locales/pt.yml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&c[material] limitado a [number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&c[entity] gerando limitado a [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: Limites da ilha
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<jogador>"
|
||||||
|
description: mostrar os limites da ilha para o jogador
|
||||||
|
calc:
|
||||||
|
parameters: "<jogador>"
|
||||||
|
description: recalcular os limites da ilha para o jogador
|
||||||
|
finished: "&a O recálculo da ilha foi concluído com sucesso!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Material ou entidade desconhecida [name]."
|
||||||
|
main:
|
||||||
|
description: permite gerenciar limites de deslocamento para materiais e entidades
|
||||||
|
set:
|
||||||
|
parameters: "<jogador> <material|entidade> <número>"
|
||||||
|
description: define novo deslocamento para limite de material ou entidade
|
||||||
|
success: "&a O deslocamento de limite para [name] é definido como [number]."
|
||||||
|
same: "&c O deslocamento limite para [name] já é [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<jogador> <material|entidade> <número>"
|
||||||
|
description: adiciona deslocamento para limite de material ou entidade
|
||||||
|
success: "&a O deslocamento de limite para [name] é aumentado até [number]."
|
||||||
|
remove:
|
||||||
|
parameters: "<jogador> <material|entidade> <número>"
|
||||||
|
description: reduz o deslocamento para o limite de material ou entidade
|
||||||
|
success: "&a O deslocamento de limite para [name] é reduzido até [number]."
|
||||||
|
reset:
|
||||||
|
parameters: "<jogador> <material|entidade>"
|
||||||
|
description: remove deslocamento para material ou entidade
|
||||||
|
success: "&a O deslocamento de limite para [name] é definido como 0."
|
||||||
|
view:
|
||||||
|
parameters: "<jogador> <material|entidade>"
|
||||||
|
description: exibe deslocamento para material ou entidade
|
||||||
|
message: "&a [name] deslocamento é definido como [number]."
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: mostre os limites da sua ilha
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&c Não há limites neste mundo"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Aquela ilha não tem dono"
|
||||||
|
not-on-island: "&c Este local não possui limites definidos."
|
||||||
|
recount:
|
||||||
|
description: reconta limites para sua ilha
|
||||||
|
now-recounting: "&b Agora recontando. Isso pode demorar um pouco, aguarde..."
|
||||||
|
in-progress: "&c A recontagem da ilha está em andamento. Aguarde..."
|
||||||
|
time-out: "&c Tempo limite ao recontar. A ilha é realmente grande?"
|
65
src/main/resources/locales/ro.yml
Normal file
65
src/main/resources/locales/ro.yml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&c[material] limitat la [number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&c[entity] reproducere limitata la [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: Limitele insulei
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<jucator>"
|
||||||
|
description: Arata limitele insulei pentru jucator
|
||||||
|
calc:
|
||||||
|
parameters: "<jucator>"
|
||||||
|
description: recalculeaza limitele insulei pentru jocator
|
||||||
|
finished: "&aRecalcularea insulei terminata cu succes!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Material sau entitate necunoscută [name]."
|
||||||
|
main:
|
||||||
|
description: permite gestionarea decalajelor de limite pentru materiale și
|
||||||
|
entități
|
||||||
|
set:
|
||||||
|
parameters: "<player> <material|entity> <number>"
|
||||||
|
description: stabilește o nouă compensare pentru limita de material sau entitate
|
||||||
|
success: "&a Decalajul limită pentru [name] este setat la [number]."
|
||||||
|
same: "&c Decalajul limită pentru [name] este deja [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<player> <material|entity> <number>"
|
||||||
|
description: adaugă compensare pentru limita de material sau entitate
|
||||||
|
success: "&a Compensarea limită pentru [name] este mărită până la [number]."
|
||||||
|
remove:
|
||||||
|
parameters: "<player> <material|entity> <number>"
|
||||||
|
description: reduce compensarea pentru limita de material sau entitate
|
||||||
|
success: "&a Limita decalajului pentru [name] este redusă până la [number]."
|
||||||
|
reset:
|
||||||
|
parameters: "<player> <material|entity>"
|
||||||
|
description: elimină decalajul pentru material sau entitate
|
||||||
|
success: "&a Decalajul limită pentru [name] este setat la 0."
|
||||||
|
view:
|
||||||
|
parameters: "<player> <material|entity>"
|
||||||
|
description: afișează offset pentru material sau entitate
|
||||||
|
message: "&a [name] offset este setat la [number]."
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: iti arata limitele insulei
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&c Nu există limite stabilite în această lume"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Insula aceea nu are proprietar"
|
||||||
|
not-on-island: "&c Această locație nu are limite setate."
|
||||||
|
recount:
|
||||||
|
description: renumara limitele insulei tale
|
||||||
|
now-recounting: "&b Acum povestind. Acest lucru ar putea dura ceva timp, vă
|
||||||
|
rugăm să așteptați..."
|
||||||
|
in-progress: "&c Recuperarea insulei este în curs. Va rugam asteptati..."
|
||||||
|
time-out: "&c Timpul expirat când relatați. Este insula cu adevărat mare?"
|
@ -1,32 +1,65 @@
|
|||||||
###########################################################################################
|
---
|
||||||
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
|
|
||||||
# the one at http://yaml-online-parser.appspot.com #
|
|
||||||
###########################################################################################
|
|
||||||
|
|
||||||
entity-limits:
|
|
||||||
hit-limit: "&3&LS&b&lC &8&L» &e[entity] &4varlığından &5[number] &4tane koyabilirsin!"
|
|
||||||
block-limits:
|
block-limits:
|
||||||
hit-limit: "&3&LS&b&lC &8&L» &e[material] &4eşyasından &5[number] &4koyabilirsin!"
|
hit-limit: "&e[material] &4eşyasından &5[number] &4koyabilirsin!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&e[entity] &4varlığından &5[number] &4tane koyabilirsin!"
|
||||||
limits:
|
limits:
|
||||||
panel-title: "&3&lSon&b&lCesurlar &eAda limiti"
|
panel-title: "&eAda limiti"
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
limits:
|
limits:
|
||||||
main:
|
main:
|
||||||
parameters: "<player>"
|
parameters: "<player>"
|
||||||
description: "Oyuncu için ada limitlerini göster."
|
description: Oyuncu için ada limitlerini göster.
|
||||||
calc:
|
calc:
|
||||||
parameters: "<player>"
|
parameters: "<player>"
|
||||||
description: "Oyuncu için ada limitlerini tekrar hesapla."
|
description: Oyuncu için ada limitlerini tekrar hesapla.
|
||||||
finished: "&aIsland recalc finished sucessfully!"
|
finished: "&aAda hesaplaması başarıyla yapıldı."
|
||||||
|
offset:
|
||||||
|
unknown: "&c Bilinmeyen malzeme veya varlık [name]."
|
||||||
|
main:
|
||||||
|
description: malzemeler ve varlıklar için limit ofsetlerini yönetmeye olanak
|
||||||
|
tanır
|
||||||
|
set:
|
||||||
|
parameters: "<oyuncu> <malzeme|varlık> <sayı>"
|
||||||
|
description: malzeme veya varlık sınırı için yeni ofset ayarlar
|
||||||
|
success: "&[name] için Limit ofseti [number] olarak ayarlandı."
|
||||||
|
same: "&c [name] için sınır ofseti zaten [number]'dır."
|
||||||
|
add:
|
||||||
|
parameters: "<oyuncu> <malzeme|varlık> <sayı>"
|
||||||
|
description: malzeme veya varlık sınırı için ofset ekler
|
||||||
|
success: "&[name] için Limit ofseti [number]'ya kadar artırıldı."
|
||||||
|
remove:
|
||||||
|
parameters: "<oyuncu> <malzeme|varlık> <sayı>"
|
||||||
|
description: malzeme veya varlık sınırı için ofseti azaltır
|
||||||
|
success: "&[name] için limit ofseti [number]'ya kadar azaltıldı."
|
||||||
|
reset:
|
||||||
|
parameters: "<oyuncu> <malzeme|varlık>"
|
||||||
|
description: malzeme veya varlık için ofseti kaldırır
|
||||||
|
success: "&[name] için Limit ofseti 0 olarak ayarlandı."
|
||||||
|
view:
|
||||||
|
parameters: "<oyuncu> <malzeme|varlık>"
|
||||||
|
description: malzeme veya varlık için ofseti görüntüler
|
||||||
|
message: "&a [name] ofseti [number] olarak ayarlandı."
|
||||||
island:
|
island:
|
||||||
limits:
|
limits:
|
||||||
parameters: ""
|
description: Ada limitlerini gör.
|
||||||
description: "Ada limitlerini gör."
|
|
||||||
max-color: "&c"
|
max-color: "&c"
|
||||||
regular-color: "&9"
|
regular-color: "&9"
|
||||||
block-limit-syntax: "&5[number]&7/&e[limit]"
|
block-limit-syntax: "&5[number]&7/&e[limit]"
|
||||||
no-limits: "&3&LS&c&lC &A&L» &4Bu dünyada limitler ayarlı değil!"
|
no-limits: "&c Bu dünyada hiçbir sınır belirlenmedi"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c O adanın bir sahibi yok"
|
||||||
|
not-on-island: "&c Bu lokasyonun belirlenmiş bir sınırı yok."
|
||||||
|
recount:
|
||||||
|
description: Ada limitlerini tekrardan hesaplar.
|
||||||
|
now-recounting: "&b Şimdi anlatmaya başlıyorum. Biraz zaman alabilir, lütfen
|
||||||
|
bekleyin..."
|
||||||
|
in-progress: "&c Ada geri kazanımı devam ediyor. Lütfen bekleyin..."
|
||||||
|
time-out: "&c Anlatırken zaman aşımı. Ada gerçekten büyük mü?"
|
||||||
|
63
src/main/resources/locales/uk.yml
Normal file
63
src/main/resources/locales/uk.yml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&c[material] обмежено [number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&c[entity] породження обмежено до [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: Межі острова
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<гравець>"
|
||||||
|
description: показати обмеження острова для гравця
|
||||||
|
calc:
|
||||||
|
parameters: "<гравець>"
|
||||||
|
description: перерахувати обмеження острова для гравця
|
||||||
|
finished: Перерахунок острова успішно завершено!
|
||||||
|
offset:
|
||||||
|
unknown: "&c Невідомий матеріал або сутність [name]."
|
||||||
|
main:
|
||||||
|
description: дозволяє керувати зсувами лімітів для матеріалів і сутностей
|
||||||
|
set:
|
||||||
|
parameters: "<гравець> <матеріал|сутність> <номер>"
|
||||||
|
description: встановлює нове зміщення для обмеження матеріалу або сутності
|
||||||
|
success: "&a Лімітне зміщення для [name] встановлено на [number]."
|
||||||
|
same: "&c Обмежене зміщення для [name] вже [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<гравець> <матеріал|сутність> <номер>"
|
||||||
|
description: додає зсув для обмеження матеріалу або сутності
|
||||||
|
success: "&a Лімітне зміщення для [name] збільшується до [number]."
|
||||||
|
remove:
|
||||||
|
parameters: "<гравець> <матеріал|сутність> <номер>"
|
||||||
|
description: зменшує зсув для ліміту матеріалу або сутності
|
||||||
|
success: "&a Лімітне зміщення для [name] зменшується до [number]."
|
||||||
|
reset:
|
||||||
|
parameters: "<гравець> <матеріал|сутність>"
|
||||||
|
description: видаляє зсув для матеріалу або сутності
|
||||||
|
success: "&a Лімітне зміщення для [name] встановлено на 0."
|
||||||
|
view:
|
||||||
|
parameters: "<гравець> <матеріал|сутність>"
|
||||||
|
description: відображає зсув для матеріалу або сутності
|
||||||
|
message: "&зміщення [name] встановлено на [number]."
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: покажіть межі свого острова
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&c У цьому світі немає обмежень"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Цей острів не має власника"
|
||||||
|
not-on-island: "&c Це місце не має обмежень."
|
||||||
|
recount:
|
||||||
|
description: перераховує ліміти для вашого острова
|
||||||
|
now-recounting: "&b Тепер перераховую. Це може зайняти деякий час, зачекайте..."
|
||||||
|
in-progress: "&c Перерахування острова триває. Будь ласка, зачекайте..."
|
||||||
|
time-out: "&c Тайм-аут під час перерахунку. Чи справді острів великий?"
|
63
src/main/resources/locales/vi.yml
Normal file
63
src/main/resources/locales/vi.yml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&c[material] bị giới hạn trong [number] khối!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&c[entity] bị giới hạn trong [number] thực thể!"
|
||||||
|
limits:
|
||||||
|
panel-title: Giới hạn đảo
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<người chơi>"
|
||||||
|
description: xem giới hạn đảo của người chơi
|
||||||
|
calc:
|
||||||
|
parameters: "<người chơi>"
|
||||||
|
description: tính toán lại giới hạn đảo của người chơi
|
||||||
|
finished: "&aTính toán đã hoàn thành!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Tài liệu hoặc thực thể không xác định [name]."
|
||||||
|
main:
|
||||||
|
description: cho phép quản lý các giới hạn bù trừ cho vật liệu và thực thể
|
||||||
|
set:
|
||||||
|
parameters: "<người chơi> <vật liệu|thực thể> <số>"
|
||||||
|
description: thiết lập độ lệch mới cho giới hạn vật liệu hoặc thực thể
|
||||||
|
success: "&a Độ lệch giới hạn cho [name] được đặt thành [number]."
|
||||||
|
same: "&c Độ lệch giới hạn cho [name] đã là [number]."
|
||||||
|
add:
|
||||||
|
parameters: "<người chơi> <vật liệu|thực thể> <số>"
|
||||||
|
description: thêm độ lệch cho giới hạn vật liệu hoặc thực thể
|
||||||
|
success: "&a Độ lệch giới hạn cho [name] được tăng lên cho đến [number]."
|
||||||
|
remove:
|
||||||
|
parameters: "<người chơi> <vật liệu|thực thể> <số>"
|
||||||
|
description: giảm độ lệch cho giới hạn vật liệu hoặc thực thể
|
||||||
|
success: "&a Độ lệch giới hạn cho [name] được giảm xuống đến [number]."
|
||||||
|
reset:
|
||||||
|
parameters: "<người chơi> <vật liệu|thực thể>"
|
||||||
|
description: xóa phần bù cho vật liệu hoặc thực thể
|
||||||
|
success: "&a Độ lệch giới hạn cho [name] được đặt thành 0."
|
||||||
|
view:
|
||||||
|
parameters: "<người chơi> <vật liệu|thực thể>"
|
||||||
|
description: hiển thị bù trừ cho vật liệu hoặc thực thể
|
||||||
|
message: "&a [name] bù trừ được đặt thành [number]."
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: xem giới hạn đảo của bạn
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&c Không có giới hạn nào được đặt ra trên thế giới này"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c Hòn đảo đó không có chủ sở hữu"
|
||||||
|
not-on-island: "&c Vị trí này không có giới hạn nào được đặt ra."
|
||||||
|
recount:
|
||||||
|
description: tính toán lại giới hạn đảo của bạn
|
||||||
|
now-recounting: "&b Đang đếm lại. Việc này có thể mất một lúc, vui lòng đợi..."
|
||||||
|
in-progress: "&c Đảo đang được thu hồi. Vui lòng đợi..."
|
||||||
|
time-out: "&c Hết giờ khi kể lại. Hòn đảo có thực sự lớn không?"
|
@ -1,32 +1,63 @@
|
|||||||
###########################################################################################
|
---
|
||||||
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
|
|
||||||
# the one at http://yaml-online-parser.appspot.com #
|
|
||||||
###########################################################################################
|
|
||||||
|
|
||||||
block-limits:
|
block-limits:
|
||||||
hit-limit: "&c[material] 已限制到 [number]!"
|
hit-limit: "&c[material] 已限制到 [number]!"
|
||||||
entity-limits:
|
entity-limits:
|
||||||
hit-limit: "&c[entity] 生成已限制到 [number]!"
|
hit-limit: "&c[entity] 生成已限制到 [number]!"
|
||||||
limits:
|
limits:
|
||||||
panel-title: "岛屿限制"
|
panel-title: 岛屿限制
|
||||||
|
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
limits:
|
limits:
|
||||||
main:
|
main:
|
||||||
parameters: "<player>"
|
parameters: "<玩家>"
|
||||||
description: "显示玩家的限制"
|
description: 显示玩家的限制
|
||||||
calc:
|
calc:
|
||||||
parameters: "<player>"
|
parameters: "<玩家>"
|
||||||
description: "重新计算玩家的岛屿限制"
|
description: 重新计算玩家的岛屿限制
|
||||||
finished: "&a岛屿重计算已完成!"
|
finished: "&a岛屿重计算已完成!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c 未知物质或实体 [name]。"
|
||||||
|
main:
|
||||||
|
description: 允许管理材料和实体的限制偏移
|
||||||
|
set:
|
||||||
|
parameters: "<玩家> <材质|实体> <数字>"
|
||||||
|
description: 为材料或实体限制设置新的偏移
|
||||||
|
success: "&a 将 [name] 的限制偏移量设置为 [number]。"
|
||||||
|
same: "&c [name] 的限制偏移量已经为 [number]。"
|
||||||
|
add:
|
||||||
|
parameters: "<玩家> <材质|实体> <数字>"
|
||||||
|
description: 添加材料或实体限制的偏移
|
||||||
|
success: "&a [name] 的限制偏移量增加至 [number]。"
|
||||||
|
remove:
|
||||||
|
parameters: "<玩家> <材质|实体> <数字>"
|
||||||
|
description: 减少材料或实体限制的偏移量
|
||||||
|
success: "&a [name] 的限制偏移量减少至 [number]。"
|
||||||
|
reset:
|
||||||
|
parameters: "<玩家> <材质|实体>"
|
||||||
|
description: 删除材料或实体的偏移
|
||||||
|
success: "&a [name] 的限制偏移量设置为 0。"
|
||||||
|
view:
|
||||||
|
parameters: "<玩家> <材质|实体>"
|
||||||
|
description: 显示材料或实体的偏移
|
||||||
|
message: "&a [name] offset 设置为 [number]。"
|
||||||
island:
|
island:
|
||||||
limits:
|
limits:
|
||||||
description: "显示您的岛屿限制"
|
description: 显示您的岛屿限制
|
||||||
max-color: "&c"
|
max-color: "&c"
|
||||||
regular-color: "&a"
|
regular-color: "&a"
|
||||||
block-limit-syntax: "[number]/[limit]"
|
block-limit-syntax: "[number]/[limit]"
|
||||||
no-limits: "&c此世界中无限制"
|
no-limits: "&c 这世上没有限制"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c 那个岛没有主人"
|
||||||
|
not-on-island: "&c 此位置未设置限制。"
|
||||||
recount:
|
recount:
|
||||||
description: "重新计数岛屿限制"
|
description: 重新计数岛屿限制
|
||||||
|
now-recounting: "&b 开始重新计算. 可能需要一定的时间, 请稍等......"
|
||||||
|
in-progress: "&c 重新计算岛屿限制中. 请稍等......"
|
||||||
|
time-out: "&c 重新计算超时. 岛屿太大了吗?"
|
||||||
|
63
src/main/resources/locales/zh-TW.yml
Normal file
63
src/main/resources/locales/zh-TW.yml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&c[material]僅限[number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&c[entity] 產生僅限於 [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: 島嶼限制
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<玩家>"
|
||||||
|
description: 顯示玩家的島嶼限制
|
||||||
|
calc:
|
||||||
|
parameters: "<玩家>"
|
||||||
|
description: 重新計算玩家的島嶼限制
|
||||||
|
finished: "&a 島嶼重新計算成功完成!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c 未知材料或實體[name]。"
|
||||||
|
main:
|
||||||
|
description: 允許管理材料和實體的限制偏移
|
||||||
|
set:
|
||||||
|
parameters: "<玩家> <材質|實體> <數字>"
|
||||||
|
description: 設定材料或實體限制的新偏移
|
||||||
|
success: "&a [name] 的限制偏移量設定為 [number]。"
|
||||||
|
same: "&c [name] 的限制偏移量已經是 [number]。"
|
||||||
|
add:
|
||||||
|
parameters: "<玩家> <材質|實體> <數字>"
|
||||||
|
description: 添加材料或實體限制的偏移量
|
||||||
|
success: "&a [name] 的限制偏移量增加到 [number]。"
|
||||||
|
remove:
|
||||||
|
parameters: "<玩家> <材質|實體> <數字>"
|
||||||
|
description: 減少材料或實體限制的偏移
|
||||||
|
success: "&a [name] 的限制偏移量減少到 [number]。"
|
||||||
|
reset:
|
||||||
|
parameters: "<玩家> <材質|實體>"
|
||||||
|
description: 刪除材質或實體的偏移
|
||||||
|
success: "&a [名稱] 的限制偏移量設定為 0。"
|
||||||
|
view:
|
||||||
|
parameters: "<玩家> <材質|實體>"
|
||||||
|
description: 顯示材料或實體的偏移
|
||||||
|
message: "&a [name] 偏移量設定為 [number]。"
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: 顯示您的島嶼限制
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&c 這個世界沒有限制"
|
||||||
|
panel:
|
||||||
|
title-syntax: "[title] [sort]"
|
||||||
|
entity-group-name-syntax: "[name]"
|
||||||
|
entity-name-syntax: "[name]"
|
||||||
|
block-name-syntax: "[name]"
|
||||||
|
A2Z: a > z
|
||||||
|
Z2A: z > a
|
||||||
|
errors:
|
||||||
|
no-owner: "&c 那島沒有主人"
|
||||||
|
not-on-island: "&c 該位置沒有設定限制。"
|
||||||
|
recount:
|
||||||
|
description: 詳細說明您的島嶼的限制
|
||||||
|
now-recounting: "&b 現在重述。這可能需要一段時間,請稍候..."
|
||||||
|
in-progress: "&c 島回收正在進行中。請稍等..."
|
||||||
|
time-out: "&c 重新計數時逾時。島真的很大嗎?"
|
9
src/main/resources/plugin.yml
Normal file
9
src/main/resources/plugin.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
name: BentoBox-Limits
|
||||||
|
main: world.bentobox.limits.LimitsPladdon
|
||||||
|
version: ${project.version}${build.number}
|
||||||
|
api-version: "1.21"
|
||||||
|
|
||||||
|
authors: [tastybento]
|
||||||
|
contributors: ["The BentoBoxWorld Community"]
|
||||||
|
website: https://bentobox.world
|
||||||
|
description: ${project.description}
|
@ -1,4 +1,4 @@
|
|||||||
package bentobox.addon.limits.listeners;
|
package world.bentobox.limits;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
@ -9,8 +9,10 @@ import static org.mockito.Mockito.times;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -22,6 +24,9 @@ import org.bukkit.entity.EntityType;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
import org.bukkit.permissions.PermissionAttachmentInfo;
|
import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -34,12 +39,12 @@ import org.powermock.modules.junit4.PowerMockRunner;
|
|||||||
import world.bentobox.bentobox.api.addons.AddonDescription;
|
import world.bentobox.bentobox.api.addons.AddonDescription;
|
||||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||||
import world.bentobox.bentobox.api.events.island.IslandEvent;
|
import world.bentobox.bentobox.api.events.island.IslandEvent;
|
||||||
import world.bentobox.bentobox.api.events.team.TeamEvent.TeamSetownerEvent;
|
import world.bentobox.bentobox.api.events.team.TeamSetownerEvent;
|
||||||
import world.bentobox.bentobox.database.objects.Island;
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
import world.bentobox.bentobox.managers.IslandsManager;
|
import world.bentobox.bentobox.managers.IslandsManager;
|
||||||
import world.bentobox.limits.Limits;
|
|
||||||
import world.bentobox.limits.listeners.BlockLimitsListener;
|
import world.bentobox.limits.listeners.BlockLimitsListener;
|
||||||
import world.bentobox.limits.listeners.JoinListener;
|
import world.bentobox.limits.listeners.JoinListener;
|
||||||
|
import world.bentobox.limits.mocks.ServerMocks;
|
||||||
import world.bentobox.limits.objects.IslandBlockCount;
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,12 +52,14 @@ import world.bentobox.limits.objects.IslandBlockCount;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@RunWith(PowerMockRunner.class)
|
@RunWith(PowerMockRunner.class)
|
||||||
@PrepareForTest( {Bukkit.class} )
|
@PrepareForTest({ Bukkit.class })
|
||||||
public class JoinListenerTest {
|
public class JoinListenerTest {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private Limits addon;
|
private Limits addon;
|
||||||
@Mock
|
@Mock
|
||||||
|
private Settings settings;
|
||||||
|
@Mock
|
||||||
private GameModeAddon bskyblock;
|
private GameModeAddon bskyblock;
|
||||||
@Mock
|
@Mock
|
||||||
private Player player;
|
private Player player;
|
||||||
@ -68,22 +75,31 @@ public class JoinListenerTest {
|
|||||||
private OfflinePlayer owner;
|
private OfflinePlayer owner;
|
||||||
@Mock
|
@Mock
|
||||||
private Island island;
|
private Island island;
|
||||||
|
@Mock
|
||||||
|
private PluginManager pim;
|
||||||
|
private @Nullable UUID uuid = UUID.randomUUID();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
ServerMocks.newServer();
|
||||||
jl = new JoinListener(addon);
|
jl = new JoinListener(addon);
|
||||||
// Setup addon
|
// Setup addon
|
||||||
when(addon.getGameModes()).thenReturn(Collections.singletonList(bskyblock));
|
when(addon.getGameModes()).thenReturn(Collections.singletonList(bskyblock));
|
||||||
when(addon.getGameModeName(any())).thenReturn("bskyblock");
|
when(addon.getGameModeName(any())).thenReturn("bskyblock");
|
||||||
when(addon.getGameModePermPrefix(any())).thenReturn("bskyblock.");
|
when(addon.getGameModePermPrefix(any())).thenReturn("bskyblock.");
|
||||||
|
when(addon.getSettings()).thenReturn(settings);
|
||||||
|
// Settings
|
||||||
|
when(settings.getGroupLimitDefinitions())
|
||||||
|
.thenReturn(new ArrayList<>(List.of(new EntityGroup("friendly", new HashSet<>(), -1, null))));
|
||||||
// Island Manager
|
// Island Manager
|
||||||
when(im.hasIsland(any(), any(UUID.class))).thenReturn(true);
|
|
||||||
when(island.getUniqueId()).thenReturn("unique_id");
|
when(island.getUniqueId()).thenReturn("unique_id");
|
||||||
|
when(island.getOwner()).thenReturn(uuid);
|
||||||
when(im.getIsland(any(), any(UUID.class))).thenReturn(island);
|
when(im.getIsland(any(), any(UUID.class))).thenReturn(island);
|
||||||
|
when(im.getIslands(any(), any(UUID.class))).thenReturn(List.of(island));
|
||||||
// Default is that player has island
|
// Default is that player has island
|
||||||
when(addon.getIslands()).thenReturn(im);
|
when(addon.getIslands()).thenReturn(im);
|
||||||
// Player
|
// Player
|
||||||
when(player.getUniqueId()).thenReturn(UUID.randomUUID());
|
when(player.getUniqueId()).thenReturn(uuid);
|
||||||
when(player.getName()).thenReturn("tastybento");
|
when(player.getName()).thenReturn("tastybento");
|
||||||
// No permissions by default
|
// No permissions by default
|
||||||
when(player.getEffectivePermissions()).thenReturn(Collections.emptySet());
|
when(player.getEffectivePermissions()).thenReturn(Collections.emptySet());
|
||||||
@ -102,13 +118,18 @@ public class JoinListenerTest {
|
|||||||
when(owner.isOnline()).thenReturn(true);
|
when(owner.isOnline()).thenReturn(true);
|
||||||
when(owner.getPlayer()).thenReturn(player);
|
when(owner.getPlayer()).thenReturn(player);
|
||||||
when(Bukkit.getOfflinePlayer(any(UUID.class))).thenReturn(owner);
|
when(Bukkit.getOfflinePlayer(any(UUID.class))).thenReturn(owner);
|
||||||
|
when(Bukkit.getPluginManager()).thenReturn(pim);
|
||||||
|
|
||||||
// Island
|
}
|
||||||
when(island.getOwner()).thenReturn(UUID.randomUUID());
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
ServerMocks.unsetBukkitServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onNewIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onNewIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnNewIslandWrongReason() {
|
public void testOnNewIslandWrongReason() {
|
||||||
@ -118,7 +139,8 @@ public class JoinListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onNewIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onNewIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnNewIslandRegistered() {
|
public void testOnNewIslandRegistered() {
|
||||||
@ -128,7 +150,8 @@ public class JoinListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onNewIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onNewIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnNewIslandResetted() {
|
public void testOnNewIslandResetted() {
|
||||||
@ -179,7 +202,8 @@ public class JoinListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onOwnerChange(world.bentobox.bentobox.api.events.team.TeamEvent.TeamSetownerEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onOwnerChange(world.bentobox.bentobox.api.events.team.TeamEvent.TeamSetownerEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnOwnerChange() {
|
public void testOnOwnerChange() {
|
||||||
@ -192,7 +216,8 @@ public class JoinListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnPlayerJoin() {
|
public void testOnPlayerJoin() {
|
||||||
@ -203,7 +228,8 @@ public class JoinListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnPlayerJoinIBCNull() {
|
public void testOnPlayerJoinIBCNull() {
|
||||||
@ -214,9 +240,9 @@ public class JoinListenerTest {
|
|||||||
verify(bll, never()).setIsland("unique_id", ibc);
|
verify(bll, never()).setIsland("unique_id", ibc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnPlayerJoinWithPermNotLimits() {
|
public void testOnPlayerJoinWithPermNotLimits() {
|
||||||
@ -232,7 +258,8 @@ public class JoinListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnPlayerJoinWithPermLimitsWrongSize() {
|
public void testOnPlayerJoinWithPermLimitsWrongSize() {
|
||||||
@ -244,11 +271,13 @@ public class JoinListenerTest {
|
|||||||
when(player.getEffectivePermissions()).thenReturn(perms);
|
when(player.getEffectivePermissions()).thenReturn(perms);
|
||||||
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
|
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
|
||||||
jl.onPlayerJoin(e);
|
jl.onPlayerJoin(e);
|
||||||
verify(addon).logError("Player tastybento has permission: 'bskyblock.island.limit.my.perm.for.game' but format must be 'bskyblock.island.limit.MATERIAL.NUMBER' or 'bskyblock.island.limit.ENTITY-TYPE.NUMBER' Ignoring...");
|
verify(addon).logError(
|
||||||
|
"Player tastybento has permission: 'bskyblock.island.limit.my.perm.for.game' but format must be 'bskyblock.island.limit.MATERIAL.NUMBER', 'bskyblock.island.limit.ENTITY-TYPE.NUMBER', or 'bskyblock.island.limit.ENTITY-GROUP.NUMBER' Ignoring...");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnPlayerJoinWithPermLimitsInvalidMaterial() {
|
public void testOnPlayerJoinWithPermLimitsInvalidMaterial() {
|
||||||
@ -260,11 +289,13 @@ public class JoinListenerTest {
|
|||||||
when(player.getEffectivePermissions()).thenReturn(perms);
|
when(player.getEffectivePermissions()).thenReturn(perms);
|
||||||
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
|
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
|
||||||
jl.onPlayerJoin(e);
|
jl.onPlayerJoin(e);
|
||||||
verify(addon).logError("Player tastybento has permission: 'bskyblock.island.limit.mumbo.34' but MUMBO is not a valid material or entity type. Ignoring...");
|
verify(addon).logError(
|
||||||
|
"Player tastybento has permission: 'bskyblock.island.limit.mumbo.34' but MUMBO is not a valid material or entity type/group. Ignoring...");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnPlayerJoinWithPermLimitsWildcard() {
|
public void testOnPlayerJoinWithPermLimitsWildcard() {
|
||||||
@ -276,11 +307,13 @@ public class JoinListenerTest {
|
|||||||
when(player.getEffectivePermissions()).thenReturn(perms);
|
when(player.getEffectivePermissions()).thenReturn(perms);
|
||||||
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
|
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
|
||||||
jl.onPlayerJoin(e);
|
jl.onPlayerJoin(e);
|
||||||
verify(addon).logError("Player tastybento has permission: 'bskyblock.island.limit.*' but wildcards are not allowed. Ignoring...");
|
verify(addon).logError(
|
||||||
|
"Player tastybento has permission: 'bskyblock.island.limit.*' but wildcards are not allowed. Ignoring...");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnPlayerJoinWithPermLimitsNotNumber() {
|
public void testOnPlayerJoinWithPermLimitsNotNumber() {
|
||||||
@ -292,11 +325,13 @@ public class JoinListenerTest {
|
|||||||
when(player.getEffectivePermissions()).thenReturn(perms);
|
when(player.getEffectivePermissions()).thenReturn(perms);
|
||||||
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
|
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
|
||||||
jl.onPlayerJoin(e);
|
jl.onPlayerJoin(e);
|
||||||
verify(addon).logError("Player tastybento has permission: 'bskyblock.island.limit.STONE.abc' but the last part MUST be a number! Ignoring...");
|
verify(addon).logError(
|
||||||
|
"Player tastybento has permission: 'bskyblock.island.limit.STONE.abc' but the last part MUST be an integer! Ignoring...");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnPlayerJoinWithPermLimitsSuccess() {
|
public void testOnPlayerJoinWithPermLimitsSuccess() {
|
||||||
@ -313,7 +348,8 @@ public class JoinListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnPlayerJoinWithPermLimitsSuccessEntity() {
|
public void testOnPlayerJoinWithPermLimitsSuccessEntity() {
|
||||||
@ -330,7 +366,26 @@ public class JoinListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testOnPlayerJoinWithPermLimitsSuccessEntityGroup() {
|
||||||
|
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
||||||
|
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
||||||
|
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.friendly.24");
|
||||||
|
when(permAtt.getValue()).thenReturn(true);
|
||||||
|
perms.add(permAtt);
|
||||||
|
when(player.getEffectivePermissions()).thenReturn(perms);
|
||||||
|
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
|
||||||
|
jl.onPlayerJoin(e);
|
||||||
|
verify(addon, never()).logError(anyString());
|
||||||
|
verify(ibc).setEntityGroupLimit(eq("friendly"), eq(24));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnPlayerJoinWithPermLimitsMultiPerms() {
|
public void testOnPlayerJoinWithPermLimitsMultiPerms() {
|
||||||
@ -340,7 +395,7 @@ public class JoinListenerTest {
|
|||||||
when(permAtt.getValue()).thenReturn(true);
|
when(permAtt.getValue()).thenReturn(true);
|
||||||
perms.add(permAtt);
|
perms.add(permAtt);
|
||||||
PermissionAttachmentInfo permAtt2 = mock(PermissionAttachmentInfo.class);
|
PermissionAttachmentInfo permAtt2 = mock(PermissionAttachmentInfo.class);
|
||||||
when(permAtt2.getPermission()).thenReturn("bskyblock.island.limit.grass.14");
|
when(permAtt2.getPermission()).thenReturn("bskyblock.island.limit.short_grass.14");
|
||||||
when(permAtt2.getValue()).thenReturn(true);
|
when(permAtt2.getValue()).thenReturn(true);
|
||||||
perms.add(permAtt2);
|
perms.add(permAtt2);
|
||||||
PermissionAttachmentInfo permAtt3 = mock(PermissionAttachmentInfo.class);
|
PermissionAttachmentInfo permAtt3 = mock(PermissionAttachmentInfo.class);
|
||||||
@ -365,7 +420,7 @@ public class JoinListenerTest {
|
|||||||
jl.onPlayerJoin(e);
|
jl.onPlayerJoin(e);
|
||||||
verify(addon, never()).logError(anyString());
|
verify(addon, never()).logError(anyString());
|
||||||
verify(ibc).setBlockLimit(eq(Material.STONE), eq(24));
|
verify(ibc).setBlockLimit(eq(Material.STONE), eq(24));
|
||||||
verify(ibc).setBlockLimit(eq(Material.GRASS), eq(14));
|
verify(ibc).setBlockLimit(eq(Material.SHORT_GRASS), eq(14));
|
||||||
verify(ibc).setBlockLimit(eq(Material.DIRT), eq(34));
|
verify(ibc).setBlockLimit(eq(Material.DIRT), eq(34));
|
||||||
verify(ibc).setEntityLimit(eq(EntityType.CHICKEN), eq(34));
|
verify(ibc).setEntityLimit(eq(EntityType.CHICKEN), eq(34));
|
||||||
verify(ibc).setEntityLimit(eq(EntityType.CAVE_SPIDER), eq(4));
|
verify(ibc).setEntityLimit(eq(EntityType.CAVE_SPIDER), eq(4));
|
||||||
@ -432,7 +487,8 @@ public class JoinListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onUnregisterIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onUnregisterIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnUnregisterIslandNotUnregistered() {
|
public void testOnUnregisterIslandNotUnregistered() {
|
||||||
@ -442,7 +498,8 @@ public class JoinListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onUnregisterIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onUnregisterIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnUnregisterIslandNotInWorld() {
|
public void testOnUnregisterIslandNotInWorld() {
|
||||||
@ -453,7 +510,8 @@ public class JoinListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onUnregisterIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
|
* Test method for
|
||||||
|
* {@link world.bentobox.limits.listeners.JoinListener#onUnregisterIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnUnregisterIslandInWorld() {
|
public void testOnUnregisterIslandInWorld() {
|
||||||
@ -487,6 +545,4 @@ public class JoinListenerTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
366
src/test/java/world/bentobox/limits/LimitsTest.java
Normal file
366
src/test/java/world/bentobox/limits/LimitsTest.java
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
package world.bentobox.limits;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarOutputStream;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.bukkit.UnsafeValues;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemFactory;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
|
import org.bukkit.scheduler.BukkitScheduler;
|
||||||
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
import org.powermock.api.mockito.PowerMockito;
|
||||||
|
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||||
|
import org.powermock.modules.junit4.PowerMockRunner;
|
||||||
|
import org.powermock.reflect.Whitebox;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
|
import world.bentobox.bentobox.Settings;
|
||||||
|
import world.bentobox.bentobox.api.addons.AddonDescription;
|
||||||
|
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||||
|
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||||
|
import world.bentobox.bentobox.api.user.User;
|
||||||
|
import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.bentobox.managers.AddonsManager;
|
||||||
|
import world.bentobox.bentobox.managers.CommandsManager;
|
||||||
|
import world.bentobox.bentobox.managers.FlagsManager;
|
||||||
|
import world.bentobox.bentobox.managers.IslandWorldManager;
|
||||||
|
import world.bentobox.bentobox.managers.IslandsManager;
|
||||||
|
import world.bentobox.bentobox.managers.PlaceholdersManager;
|
||||||
|
import world.bentobox.limits.mocks.ServerMocks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tastybento
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@RunWith(PowerMockRunner.class)
|
||||||
|
@PrepareForTest({Bukkit.class, BentoBox.class, User.class})
|
||||||
|
public class LimitsTest {
|
||||||
|
private static File jFile;
|
||||||
|
@Mock
|
||||||
|
private User user;
|
||||||
|
@Mock
|
||||||
|
private IslandsManager im;
|
||||||
|
@Mock
|
||||||
|
private Island island;
|
||||||
|
@Mock
|
||||||
|
private BentoBox plugin;
|
||||||
|
@Mock
|
||||||
|
private FlagsManager fm;
|
||||||
|
@Mock
|
||||||
|
private GameModeAddon gameMode;
|
||||||
|
@Mock
|
||||||
|
private AddonsManager am;
|
||||||
|
@Mock
|
||||||
|
private BukkitScheduler scheduler;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Settings pluginSettings;
|
||||||
|
@Mock
|
||||||
|
private PlaceholdersManager phm;
|
||||||
|
@Mock
|
||||||
|
private CompositeCommand cmd;
|
||||||
|
@Mock
|
||||||
|
private CompositeCommand adminCmd;
|
||||||
|
@Mock
|
||||||
|
private World world;
|
||||||
|
private UUID uuid;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PluginManager pim;
|
||||||
|
|
||||||
|
|
||||||
|
private Limits addon;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
cleanUp();
|
||||||
|
// Make the addon jar
|
||||||
|
jFile = new File("addon.jar");
|
||||||
|
// Copy over config file from src folder
|
||||||
|
Path fromPath = Paths.get("src/main/resources/config.yml");
|
||||||
|
Path path = Paths.get("config.yml");
|
||||||
|
Files.copy(fromPath, path);
|
||||||
|
try (JarOutputStream tempJarOutputStream = new JarOutputStream(new FileOutputStream(jFile))) {
|
||||||
|
//Added the new files to the jar.
|
||||||
|
try (FileInputStream fis = new FileInputStream(path.toFile())) {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead = 0;
|
||||||
|
JarEntry entry = new JarEntry(path.toString());
|
||||||
|
tempJarOutputStream.putNextEntry(entry);
|
||||||
|
while((bytesRead = fis.read(buffer)) != -1) {
|
||||||
|
tempJarOutputStream.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws java.lang.Exception
|
||||||
|
*/
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
Server server = ServerMocks.newServer();
|
||||||
|
// Set up plugin
|
||||||
|
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
|
||||||
|
when(plugin.getLogger()).thenReturn(Logger.getAnonymousLogger());
|
||||||
|
|
||||||
|
// The database type has to be created one line before the thenReturn() to work!
|
||||||
|
DatabaseType value = DatabaseType.JSON;
|
||||||
|
when(plugin.getSettings()).thenReturn(pluginSettings);
|
||||||
|
when(pluginSettings.getDatabaseType()).thenReturn(value);
|
||||||
|
|
||||||
|
//when(plugin.isEnabled()).thenReturn(true);
|
||||||
|
// Command manager
|
||||||
|
CommandsManager cm = mock(CommandsManager.class);
|
||||||
|
when(plugin.getCommandsManager()).thenReturn(cm);
|
||||||
|
|
||||||
|
// Player
|
||||||
|
Player p = mock(Player.class);
|
||||||
|
// Sometimes use Mockito.withSettings().verboseLogging()
|
||||||
|
when(user.isOp()).thenReturn(false);
|
||||||
|
uuid = UUID.randomUUID();
|
||||||
|
when(user.getUniqueId()).thenReturn(uuid);
|
||||||
|
when(user.getPlayer()).thenReturn(p);
|
||||||
|
when(user.getName()).thenReturn("tastybento");
|
||||||
|
User.setPlugin(plugin);
|
||||||
|
|
||||||
|
// Island World Manager
|
||||||
|
IslandWorldManager iwm = mock(IslandWorldManager.class);
|
||||||
|
when(plugin.getIWM()).thenReturn(iwm);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Player has island to begin with
|
||||||
|
when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island);
|
||||||
|
when(plugin.getIslands()).thenReturn(im);
|
||||||
|
|
||||||
|
// Locales
|
||||||
|
// Return the reference (USE THIS IN THE FUTURE)
|
||||||
|
when(user.getTranslation(Mockito.anyString())).thenAnswer((Answer<String>) invocation -> invocation.getArgument(0, String.class));
|
||||||
|
|
||||||
|
// Server
|
||||||
|
PowerMockito.mockStatic(Bukkit.class);
|
||||||
|
when(Bukkit.getServer()).thenReturn(server);
|
||||||
|
when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger());
|
||||||
|
when(Bukkit.getPluginManager()).thenReturn(mock(PluginManager.class));
|
||||||
|
|
||||||
|
// Addon
|
||||||
|
addon = new Limits();
|
||||||
|
File dataFolder = new File("addons/Level");
|
||||||
|
addon.setDataFolder(dataFolder);
|
||||||
|
addon.setFile(jFile);
|
||||||
|
AddonDescription desc = new AddonDescription.Builder("bentobox", "Level", "1.3").description("test").authors("tastybento").build();
|
||||||
|
addon.setDescription(desc);
|
||||||
|
|
||||||
|
// Addons manager
|
||||||
|
when(plugin.getAddonsManager()).thenReturn(am);
|
||||||
|
// One game mode
|
||||||
|
when(am.getGameModeAddons()).thenReturn(Collections.singletonList(gameMode));
|
||||||
|
AddonDescription desc2 = new AddonDescription.Builder("bentobox", "BSkyBlock", "1.3").description("test").authors("tasty").build();
|
||||||
|
when(gameMode.getDescription()).thenReturn(desc2);
|
||||||
|
when(gameMode.getOverWorld()).thenReturn(world);
|
||||||
|
|
||||||
|
// Player command
|
||||||
|
@NonNull
|
||||||
|
Optional<CompositeCommand> opCmd = Optional.of(cmd);
|
||||||
|
when(gameMode.getPlayerCommand()).thenReturn(opCmd);
|
||||||
|
// Admin command
|
||||||
|
Optional<CompositeCommand> opAdminCmd = Optional.of(adminCmd);
|
||||||
|
when(gameMode.getAdminCommand()).thenReturn(opAdminCmd);
|
||||||
|
|
||||||
|
// Perm prefix
|
||||||
|
when(gameMode.getPermissionPrefix()).thenReturn("bskyblock.");
|
||||||
|
|
||||||
|
// Flags manager
|
||||||
|
when(plugin.getFlagsManager()).thenReturn(fm);
|
||||||
|
when(fm.getFlags()).thenReturn(Collections.emptyList());
|
||||||
|
|
||||||
|
|
||||||
|
// Bukkit
|
||||||
|
when(Bukkit.getScheduler()).thenReturn(scheduler);
|
||||||
|
ItemMeta meta = mock(ItemMeta.class);
|
||||||
|
ItemFactory itemFactory = mock(ItemFactory.class);
|
||||||
|
when(itemFactory.getItemMeta(any())).thenReturn(meta);
|
||||||
|
when(Bukkit.getItemFactory()).thenReturn(itemFactory);
|
||||||
|
UnsafeValues unsafe = mock(UnsafeValues.class);
|
||||||
|
when(unsafe.getDataVersion()).thenReturn(777);
|
||||||
|
when(Bukkit.getUnsafe()).thenReturn(unsafe);
|
||||||
|
when(Bukkit.getPluginManager()).thenReturn(pim);
|
||||||
|
|
||||||
|
// placeholders
|
||||||
|
when(plugin.getPlaceholdersManager()).thenReturn(phm);
|
||||||
|
|
||||||
|
// World
|
||||||
|
when(world.getName()).thenReturn("bskyblock-world");
|
||||||
|
// Island
|
||||||
|
when(island.getWorld()).thenReturn(world);
|
||||||
|
when(island.getOwner()).thenReturn(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws java.lang.Exception
|
||||||
|
*/
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
ServerMocks.unsetBukkitServer();
|
||||||
|
User.clearUsers();
|
||||||
|
Mockito.framework().clearInlineMocks();
|
||||||
|
deleteAll(new File("database"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void cleanUp() throws Exception {
|
||||||
|
new File("addon.jar").delete();
|
||||||
|
new File("config.yml").delete();
|
||||||
|
deleteAll(new File("addons"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void deleteAll(File file) throws IOException {
|
||||||
|
if (file.exists()) {
|
||||||
|
Files.walk(file.toPath())
|
||||||
|
.sorted(Comparator.reverseOrder())
|
||||||
|
.map(Path::toFile)
|
||||||
|
.forEach(File::delete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for {@link world.bentobox.limits.Limits#onEnable()}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testOnEnable() {
|
||||||
|
addon.onEnable();
|
||||||
|
File f = new File("config.yml");
|
||||||
|
assertTrue(f.exists());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for {@link world.bentobox.limits.Limits#onDisable()}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testOnDisable() {
|
||||||
|
addon.onDisable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for {@link world.bentobox.limits.Limits#getSettings()}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetSettings() {
|
||||||
|
assertNull(addon.getSettings());
|
||||||
|
addon.onEnable();
|
||||||
|
world.bentobox.limits.Settings set = addon.getSettings();
|
||||||
|
assertFalse(set.getLimits().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for {@link world.bentobox.limits.Limits#getGameModes()}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetGameModes() {
|
||||||
|
assertTrue(addon.getGameModes().isEmpty());
|
||||||
|
addon.onEnable();
|
||||||
|
assertFalse(addon.getGameModes().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for {@link world.bentobox.limits.Limits#getBlockLimitListener()}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetBlockLimitListener() {
|
||||||
|
assertNull(addon.getBlockLimitListener());
|
||||||
|
addon.onEnable();
|
||||||
|
assertNotNull(addon.getBlockLimitListener());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for {@link world.bentobox.limits.Limits#inGameModeWorld(org.bukkit.World)}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testInGameModeWorld() {
|
||||||
|
addon.onEnable();
|
||||||
|
assertFalse(addon.inGameModeWorld(world));
|
||||||
|
when(gameMode.inWorld(world)).thenReturn(true);
|
||||||
|
assertTrue(addon.inGameModeWorld(world));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for {@link world.bentobox.limits.Limits#getGameModeName(org.bukkit.World)}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetGameModeName() {
|
||||||
|
when(gameMode.inWorld(world)).thenReturn(true);
|
||||||
|
assertTrue(addon.getGameModeName(world).isEmpty());
|
||||||
|
addon.onEnable();
|
||||||
|
assertEquals("BSkyBlock", addon.getGameModeName(world));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for {@link world.bentobox.limits.Limits#getGameModePermPrefix(org.bukkit.World)}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetGameModePermPrefix() {
|
||||||
|
when(gameMode.inWorld(world)).thenReturn(true);
|
||||||
|
addon.onEnable();
|
||||||
|
assertEquals("bskyblock.", addon.getGameModePermPrefix(world));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for {@link world.bentobox.limits.Limits#isCoveredGameMode(java.lang.String)}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testIsCoveredGameMode() {
|
||||||
|
assertFalse(addon.isCoveredGameMode("BSkyBlock"));
|
||||||
|
addon.onEnable();
|
||||||
|
assertTrue(addon.isCoveredGameMode("BSkyBlock"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for {@link world.bentobox.limits.Limits#getJoinListener()}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetJoinListener() {
|
||||||
|
assertNull(addon.getJoinListener());
|
||||||
|
addon.onEnable();
|
||||||
|
assertNotNull(addon.getJoinListener());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package world.bentobox.limits.commands;
|
package world.bentobox.limits.commands.player;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
@ -27,15 +27,17 @@ import world.bentobox.bentobox.BentoBox;
|
|||||||
import world.bentobox.bentobox.database.objects.Island;
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
import world.bentobox.bentobox.managers.IslandWorldManager;
|
import world.bentobox.bentobox.managers.IslandWorldManager;
|
||||||
import world.bentobox.limits.Limits;
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.Settings;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
@RunWith(PowerMockRunner.class)
|
@RunWith(PowerMockRunner.class)
|
||||||
@PrepareForTest( Bukkit.class )
|
@PrepareForTest( Bukkit.class )
|
||||||
public class LimitPanelTest {
|
public class LimitTabTest {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private Limits addon;
|
private Limits addon;
|
||||||
|
|
||||||
private LimitPanel lp;
|
private LimitTab lp;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private Island island;
|
private Island island;
|
||||||
@ -47,15 +49,19 @@ public class LimitPanelTest {
|
|||||||
private World end;
|
private World end;
|
||||||
@Mock
|
@Mock
|
||||||
private BentoBox plugin;
|
private BentoBox plugin;
|
||||||
@Mock
|
@Mock
|
||||||
private IslandWorldManager iwm;
|
private IslandWorldManager iwm;
|
||||||
|
@Mock
|
||||||
|
private Settings settings;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() {
|
||||||
// Island
|
// Island
|
||||||
when(island.getWorld()).thenReturn(world);
|
when(island.getWorld()).thenReturn(world);
|
||||||
// Addon
|
// Addon
|
||||||
when(addon.getPlugin()).thenReturn(plugin);
|
when(addon.getPlugin()).thenReturn(plugin);
|
||||||
|
when(addon.getSettings()).thenReturn(settings);
|
||||||
|
when(settings.getLimits()).thenReturn(Collections.emptyMap());
|
||||||
when(plugin.getIWM()).thenReturn(iwm);
|
when(plugin.getIWM()).thenReturn(iwm);
|
||||||
when(iwm.isNetherIslands(any())).thenReturn(true);
|
when(iwm.isNetherIslands(any())).thenReturn(true);
|
||||||
when(iwm.isEndIslands(any())).thenReturn(true);
|
when(iwm.isEndIslands(any())).thenReturn(true);
|
||||||
@ -68,11 +74,11 @@ public class LimitPanelTest {
|
|||||||
when(world.getEntities()).thenReturn(Collections.singletonList(entity));
|
when(world.getEntities()).thenReturn(Collections.singletonList(entity));
|
||||||
when(nether.getEntities()).thenReturn(Collections.singletonList(entity));
|
when(nether.getEntities()).thenReturn(Collections.singletonList(entity));
|
||||||
when(end.getEntities()).thenReturn(Collections.singletonList(entity));
|
when(end.getEntities()).thenReturn(Collections.singletonList(entity));
|
||||||
lp = new LimitPanel(addon);
|
lp = new LimitTab(addon, new IslandBlockCount("", ""), Collections.emptyMap(), island, world, null, LimitTab.SORT_BY.A2Z);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
@ -0,0 +1,146 @@
|
|||||||
|
package world.bentobox.limits.listeners;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||||
|
import org.powermock.modules.junit4.PowerMockRunner;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.Settings;
|
||||||
|
import world.bentobox.limits.listeners.EntityLimitListener.AtLimitResult;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
|
@RunWith(PowerMockRunner.class)
|
||||||
|
@PrepareForTest( Bukkit.class )
|
||||||
|
public class EntityLimitListenerTest {
|
||||||
|
@Mock
|
||||||
|
private Limits addon;
|
||||||
|
private EntityLimitListener ell;
|
||||||
|
@Mock
|
||||||
|
private Island island;
|
||||||
|
@Mock
|
||||||
|
private LivingEntity ent;
|
||||||
|
@Mock
|
||||||
|
private BlockLimitsListener bll;
|
||||||
|
@Mock
|
||||||
|
private World world;
|
||||||
|
private List<Entity> collection;
|
||||||
|
@Mock
|
||||||
|
private Location location;
|
||||||
|
private IslandBlockCount ibc;
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
// Entity
|
||||||
|
when(ent.getType()).thenReturn(EntityType.ENDERMAN);
|
||||||
|
when(ent.getLocation()).thenReturn(location);
|
||||||
|
// Island
|
||||||
|
when(island.getUniqueId()).thenReturn(UUID.randomUUID().toString());
|
||||||
|
when(island.inIslandSpace(any(Location.class))).thenReturn(true);
|
||||||
|
|
||||||
|
ibc = new IslandBlockCount("","");
|
||||||
|
when(bll.getIsland(anyString())).thenReturn(ibc);
|
||||||
|
when(addon.getBlockLimitListener()).thenReturn(bll);
|
||||||
|
|
||||||
|
FileConfiguration config = new YamlConfiguration();
|
||||||
|
config.load("src/main/resources/config.yml");
|
||||||
|
// Settings
|
||||||
|
when(addon.getConfig()).thenReturn(config);
|
||||||
|
Settings settings = new Settings(addon);
|
||||||
|
when(addon.getSettings()).thenReturn(settings);
|
||||||
|
|
||||||
|
// World
|
||||||
|
when(ent.getWorld()).thenReturn(world);
|
||||||
|
collection = new ArrayList<>();
|
||||||
|
collection.add(ent);
|
||||||
|
collection.add(ent);
|
||||||
|
collection.add(ent);
|
||||||
|
collection.add(ent);
|
||||||
|
when(world.getNearbyEntities(any())).thenReturn(collection);
|
||||||
|
|
||||||
|
ell = new EntityLimitListener(addon);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for {@link EntityLimitListener#atLimit(Island, Entity)}
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAtLimitUnderLimit() {
|
||||||
|
AtLimitResult result = ell.atLimit(island, ent);
|
||||||
|
assertFalse(result.hit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for {@link EntityLimitListener#atLimit(Island, Entity)}
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAtLimitAtLimit() {
|
||||||
|
collection.add(ent);
|
||||||
|
AtLimitResult result = ell.atLimit(island, ent);
|
||||||
|
assertTrue(result.hit());
|
||||||
|
assertEquals(EntityType.ENDERMAN, result.getTypelimit().getKey());
|
||||||
|
assertEquals(Integer.valueOf(5), result.getTypelimit().getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for {@link EntityLimitListener#atLimit(Island, Entity)}
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAtLimitUnderLimitIslandLimit() {
|
||||||
|
ibc.setEntityLimit(EntityType.ENDERMAN, 6);
|
||||||
|
AtLimitResult result = ell.atLimit(island, ent);
|
||||||
|
assertFalse(result.hit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for {@link EntityLimitListener#atLimit(Island, Entity)}
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAtLimitAtLimitIslandLimitNotAtLimit() {
|
||||||
|
ibc.setEntityLimit(EntityType.ENDERMAN, 6);
|
||||||
|
collection.add(ent);
|
||||||
|
AtLimitResult result = ell.atLimit(island, ent);
|
||||||
|
assertFalse(result.hit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for {@link EntityLimitListener#atLimit(Island, Entity)}
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAtLimitAtLimitIslandLimit() {
|
||||||
|
ibc.setEntityLimit(EntityType.ENDERMAN, 6);
|
||||||
|
collection.add(ent);
|
||||||
|
collection.add(ent);
|
||||||
|
AtLimitResult result = ell.atLimit(island, ent);
|
||||||
|
assertTrue(result.hit());
|
||||||
|
assertEquals(EntityType.ENDERMAN, result.getTypelimit().getKey());
|
||||||
|
assertEquals(Integer.valueOf(6), result.getTypelimit().getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
118
src/test/java/world/bentobox/limits/mocks/ServerMocks.java
Normal file
118
src/test/java/world/bentobox/limits/mocks/ServerMocks.java
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package world.bentobox.limits.mocks;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.notNull;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Keyed;
|
||||||
|
import org.bukkit.NamespacedKey;
|
||||||
|
import org.bukkit.Registry;
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.bukkit.Tag;
|
||||||
|
import org.bukkit.UnsafeValues;
|
||||||
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
|
|
||||||
|
public final class ServerMocks {
|
||||||
|
|
||||||
|
public static @NonNull Server newServer() {
|
||||||
|
Server mock = mock(Server.class);
|
||||||
|
|
||||||
|
Logger noOp = mock(Logger.class);
|
||||||
|
when(mock.getLogger()).thenReturn(noOp);
|
||||||
|
when(mock.isPrimaryThread()).thenReturn(true);
|
||||||
|
|
||||||
|
// Unsafe
|
||||||
|
UnsafeValues unsafe = mock(UnsafeValues.class);
|
||||||
|
when(mock.getUnsafe()).thenReturn(unsafe);
|
||||||
|
|
||||||
|
// Server must be available before tags can be mocked.
|
||||||
|
Bukkit.setServer(mock);
|
||||||
|
|
||||||
|
// Bukkit has a lot of static constants referencing registry values. To initialize those, the
|
||||||
|
// registries must be able to be fetched before the classes are touched.
|
||||||
|
Map<Class<? extends Keyed>, Object> registers = new HashMap<>();
|
||||||
|
|
||||||
|
doAnswer(invocationGetRegistry -> registers.computeIfAbsent(invocationGetRegistry.getArgument(0), clazz -> {
|
||||||
|
Registry<?> registry = mock(Registry.class);
|
||||||
|
Map<NamespacedKey, Keyed> cache = new HashMap<>();
|
||||||
|
doAnswer(invocationGetEntry -> {
|
||||||
|
NamespacedKey key = invocationGetEntry.getArgument(0);
|
||||||
|
// Some classes (like BlockType and ItemType) have extra generics that will be
|
||||||
|
// erased during runtime calls. To ensure accurate typing, grab the constant's field.
|
||||||
|
// This approach also allows us to return null for unsupported keys.
|
||||||
|
Class<? extends Keyed> constantClazz;
|
||||||
|
try {
|
||||||
|
//noinspection unchecked
|
||||||
|
constantClazz = (Class<? extends Keyed>) clazz
|
||||||
|
.getField(key.getKey().toUpperCase(Locale.ROOT).replace('.', '_')).getType();
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache.computeIfAbsent(key, key1 -> {
|
||||||
|
Keyed keyed = mock(constantClazz);
|
||||||
|
doReturn(key).when(keyed).getKey();
|
||||||
|
return keyed;
|
||||||
|
});
|
||||||
|
}).when(registry).get(notNull());
|
||||||
|
return registry;
|
||||||
|
})).when(mock).getRegistry(notNull());
|
||||||
|
|
||||||
|
// Tags are dependent on registries, but use a different method.
|
||||||
|
// This will set up blank tags for each constant; all that needs to be done to render them
|
||||||
|
// functional is to re-mock Tag#getValues.
|
||||||
|
doAnswer(invocationGetTag -> {
|
||||||
|
Tag<?> tag = mock(Tag.class);
|
||||||
|
doReturn(invocationGetTag.getArgument(1)).when(tag).getKey();
|
||||||
|
doReturn(Set.of()).when(tag).getValues();
|
||||||
|
doAnswer(invocationIsTagged -> {
|
||||||
|
Keyed keyed = invocationIsTagged.getArgument(0);
|
||||||
|
Class<?> type = invocationGetTag.getArgument(2);
|
||||||
|
if (!type.isAssignableFrom(keyed.getClass())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Since these are mocks, the exact instance might not be equal. Consider equal keys equal.
|
||||||
|
return tag.getValues().contains(keyed)
|
||||||
|
|| tag.getValues().stream().anyMatch(value -> value.getKey().equals(keyed.getKey()));
|
||||||
|
}).when(tag).isTagged(notNull());
|
||||||
|
return tag;
|
||||||
|
}).when(mock).getTag(notNull(), notNull(), notNull());
|
||||||
|
|
||||||
|
// Once the server is all set up, touch BlockType and ItemType to initialize.
|
||||||
|
// This prevents issues when trying to access dependent methods from a Material constant.
|
||||||
|
try {
|
||||||
|
Class.forName("org.bukkit.inventory.ItemType");
|
||||||
|
Class.forName("org.bukkit.block.BlockType");
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unsetBukkitServer() {
|
||||||
|
try {
|
||||||
|
Field server = Bukkit.class.getDeclaredField("server");
|
||||||
|
server.setAccessible(true);
|
||||||
|
server.set(null, null);
|
||||||
|
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServerMocks() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user