mirror of
https://github.com/BentoBoxWorld/Limits.git
synced 2024-06-27 15:15:01 +02:00
Compare commits
199 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
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 | ||
|
b58859ae3f | ||
|
100f5ea026 | ||
|
2d88c73559 | ||
|
432214485f | ||
|
54cb8b3a10 | ||
|
cc6781bf44 | ||
|
346f2b8ed8 | ||
|
f25c027a2b | ||
|
f77c48218a | ||
|
625bbe3a9f | ||
|
e0c208e049 | ||
|
16974ad5ed | ||
|
6abf92584b | ||
|
086d251d3f | ||
|
e9784b508c | ||
|
f8ffc9bcd0 | ||
|
4cb061d594 | ||
|
55bb288dad | ||
|
39f527777d | ||
|
8e21458a7a | ||
|
40a3b39748 | ||
|
1bc354e49b | ||
|
65a31c9308 | ||
|
ea76a6deeb | ||
|
8fa551ff7c | ||
|
2b6377fee7 | ||
|
36957205a4 | ||
|
789b73d3b3 | ||
|
6538be16bf | ||
|
fb6f6757d5 | ||
|
f907784bd7 | ||
|
0bc42f224f | ||
|
25be7a3844 | ||
|
6abd0e3588 | ||
|
4cfbea2bee | ||
|
e6d9b721b5 | ||
|
ea8b4e66ae | ||
|
12fdb63526 | ||
|
a18b29e7e6 | ||
|
b3b81db029 | ||
|
814272905a | ||
|
2ba4e7fc90 | ||
|
0e7d41d0eb | ||
|
a7e64c3358 | ||
|
af59047f57 | ||
|
d97fa9b9ae | ||
|
6b54fc5b8e | ||
|
fdfc080307 | ||
|
8e56bc8c78 | ||
|
f22c5d98fa | ||
|
b24e76ec67 |
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
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -84,3 +84,4 @@ nbdist/
|
||||||
nbactions.xml
|
nbactions.xml
|
||||||
nb-configuration.xml
|
nb-configuration.xml
|
||||||
.nb-gradle/
|
.nb-gradle/
|
||||||
|
/.idea/
|
||||||
|
|
24
README.md
24
README.md
|
@ -56,4 +56,28 @@ Usage permissions are (put the gamemode name, e.g. acidisland at the front):
|
||||||
default: op
|
default: op
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Items that cannot be limited
|
||||||
|
Some items cannot be limited (right now). The reasons are usually because there are too many ways to remove the item without it being tracked. If you are a programmer and can work out how to fix these, then please submit a PR!
|
||||||
|
|
||||||
|
* Primed TNT
|
||||||
|
* Evoker Fangs
|
||||||
|
* Llama Spit
|
||||||
|
* Dragon Fireball
|
||||||
|
* Area Effect Cloud
|
||||||
|
* Ender signal
|
||||||
|
* Small fireball
|
||||||
|
* Fireball
|
||||||
|
* Thrown Exp Bottle
|
||||||
|
* Shulker Bullet
|
||||||
|
* Wither Skull
|
||||||
|
* Tridents
|
||||||
|
* Arrows
|
||||||
|
* Spectral Arrows
|
||||||
|
* Snowballs
|
||||||
|
* Eggs
|
||||||
|
* Leashes
|
||||||
|
* Ender crystals
|
||||||
|
* Ender pearls
|
||||||
|
* Ender dragon
|
||||||
|
* Item frames
|
||||||
|
* Paintings
|
||||||
|
|
96
pom.xml
96
pom.xml
|
@ -38,21 +38,36 @@
|
||||||
<url>https://github.com/BentoBoxWorld/Limits/issues</url>
|
<url>https://github.com/BentoBoxWorld/Limits/issues</url>
|
||||||
</issueManagement>
|
</issueManagement>
|
||||||
|
|
||||||
|
<distributionManagement>
|
||||||
|
<snapshotRepository>
|
||||||
|
<id>codemc-snapshots</id>
|
||||||
|
<url>https://repo.codemc.org/repository/maven-snapshots</url>
|
||||||
|
</snapshotRepository>
|
||||||
|
<repository>
|
||||||
|
<id>codemc-releases</id>
|
||||||
|
<url>https://repo.codemc.org/repository/maven-releases</url>
|
||||||
|
</repository>
|
||||||
|
</distributionManagement>
|
||||||
|
|
||||||
|
|
||||||
<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.13.2-R0.1-SNAPSHOT</spigot.version>
|
<spigot.version>1.20.4-R0.1-SNAPSHOT</spigot.version>
|
||||||
<bentobox.version>1.6.0</bentobox.version>
|
<bentobox.version>2.3.0-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.7.0</build.version>
|
<build.version>1.21.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. -->
|
||||||
|
@ -124,7 +139,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>
|
||||||
|
@ -181,19 +196,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>
|
||||||
|
@ -213,25 +251,45 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.jacoco</groupId>
|
<groupId>org.jacoco</groupId>
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
<version>0.8.1</version>
|
<version>0.8.10</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<append>true</append>
|
<append>true</append>
|
||||||
|
<excludes>
|
||||||
|
<!-- This is required to prevent Jacoco from adding
|
||||||
|
synthetic fields to a JavaBean class (causes errors in testing) -->
|
||||||
|
<exclude>**/*Names*</exclude>
|
||||||
|
<!-- Prevents the Material is too large to mock error -->
|
||||||
|
<exclude>org/bukkit/Material*</exclude>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
package bentobox.addon.limits;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.bukkit.World;
|
|
||||||
|
|
||||||
import bentobox.addon.limits.commands.AdminCommand;
|
|
||||||
import bentobox.addon.limits.commands.PlayerCommand;
|
|
||||||
import bentobox.addon.limits.listeners.BlockLimitsListener;
|
|
||||||
import bentobox.addon.limits.listeners.EntityLimitListener;
|
|
||||||
import bentobox.addon.limits.listeners.JoinListener;
|
|
||||||
import world.bentobox.bentobox.api.addons.Addon;
|
|
||||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Addon to BentoBox that monitors and enforces limits
|
|
||||||
* @author tastybento
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class Limits extends Addon {
|
|
||||||
|
|
||||||
private Settings settings;
|
|
||||||
private List<GameModeAddon> gameModes;
|
|
||||||
private BlockLimitsListener blockLimitListener;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDisable(){
|
|
||||||
if (blockLimitListener != null) {
|
|
||||||
blockLimitListener.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnable() {
|
|
||||||
// Load the plugin's config
|
|
||||||
saveDefaultConfig();
|
|
||||||
// Load settings
|
|
||||||
settings = new Settings(this);
|
|
||||||
// Register worlds from GameModes
|
|
||||||
gameModes = getPlugin().getAddonsManager().getGameModeAddons().stream()
|
|
||||||
.filter(gm -> settings.getGameModes().contains(gm.getDescription().getName()))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
gameModes.forEach(gm ->
|
|
||||||
{
|
|
||||||
// Register commands
|
|
||||||
gm.getAdminCommand().ifPresent(a -> new AdminCommand(this, a));
|
|
||||||
gm.getPlayerCommand().ifPresent(a -> new PlayerCommand(this, a));
|
|
||||||
log("Limits will apply to " + gm.getDescription().getName());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
// Register listener
|
|
||||||
blockLimitListener = new BlockLimitsListener(this);
|
|
||||||
registerListener(blockLimitListener);
|
|
||||||
registerListener(new JoinListener(this));
|
|
||||||
registerListener(new EntityLimitListener(this));
|
|
||||||
// Done
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the settings
|
|
||||||
*/
|
|
||||||
public Settings getSettings() {
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the gameModes
|
|
||||||
*/
|
|
||||||
public List<GameModeAddon> getGameModes() {
|
|
||||||
return gameModes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the blockLimitListener
|
|
||||||
*/
|
|
||||||
public BlockLimitsListener getBlockLimitListener() {
|
|
||||||
return blockLimitListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if this world is covered by the activated game modes
|
|
||||||
* @param world - world
|
|
||||||
* @return true or false
|
|
||||||
*/
|
|
||||||
public boolean inGameModeWorld(World world) {
|
|
||||||
return gameModes.stream().anyMatch(gm -> gm.inWorld(world));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the name of the game mode for this world
|
|
||||||
* @param world - world
|
|
||||||
* @return game mode name or empty string if none
|
|
||||||
*/
|
|
||||||
public String getGameModeName(World world) {
|
|
||||||
return gameModes.stream().filter(gm -> gm.inWorld(world)).findFirst().map(gm -> gm.getDescription().getName()).orElse("");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the name of the game mode for this world
|
|
||||||
* @param world - world
|
|
||||||
* @return game mode name or empty string if none
|
|
||||||
*/
|
|
||||||
public String getGameModePermPrefix(World world) {
|
|
||||||
return gameModes.stream().filter(gm -> gm.inWorld(world)).findFirst().map(gm -> gm.getPermissionPrefix()).orElse("");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if any of the game modes covered have this name
|
|
||||||
* @param gameMode - name of game mode
|
|
||||||
* @return true or false
|
|
||||||
*/
|
|
||||||
public boolean isCoveredGameMode(String gameMode) {
|
|
||||||
return gameModes.stream().anyMatch(gm -> gm.getDescription().getName().equals(gameMode));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
package bentobox.addon.limits;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
|
||||||
import org.bukkit.entity.EntityType;
|
|
||||||
|
|
||||||
import bentobox.addon.limits.commands.LimitPanel;
|
|
||||||
|
|
||||||
public class Settings {
|
|
||||||
|
|
||||||
private final Map<EntityType, Integer> limits = new HashMap<>();
|
|
||||||
private final List<String> gameModes;
|
|
||||||
|
|
||||||
public Settings(Limits addon) {
|
|
||||||
|
|
||||||
// GameModes
|
|
||||||
gameModes = addon.getConfig().getStringList("gamemodes");
|
|
||||||
|
|
||||||
ConfigurationSection el = addon.getConfig().getConfigurationSection("entitylimits");
|
|
||||||
if (el != null) {
|
|
||||||
for (String key : el.getKeys(false)) {
|
|
||||||
EntityType type = getType(key);
|
|
||||||
if (type != null) {
|
|
||||||
if (!type.equals(EntityType.PAINTING) &&
|
|
||||||
!type.equals(EntityType.ITEM_FRAME) &&
|
|
||||||
(!type.isSpawnable() || (LimitPanel.E2M.containsKey(type) && LimitPanel.E2M.get(type) == null))) {
|
|
||||||
addon.logError("Entity type: " + key + " is not supported - skipping...");
|
|
||||||
} else {
|
|
||||||
limits.put(type, el.getInt(key, 0));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
addon.logError("Unknown entity type: " + key + " - skipping...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addon.log("Entity limits:");
|
|
||||||
limits.entrySet().stream().map(e -> "Limit " + e.getKey().toString() + " to " + e.getValue()).forEach(addon::log);
|
|
||||||
}
|
|
||||||
|
|
||||||
private EntityType getType(String key) {
|
|
||||||
return Arrays.stream(EntityType.values()).filter(v -> v.name().equalsIgnoreCase(key)).findFirst().orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the limits
|
|
||||||
*/
|
|
||||||
public Map<EntityType, Integer> getLimits() {
|
|
||||||
return limits;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the gameModes
|
|
||||||
*/
|
|
||||||
public List<String> getGameModes() {
|
|
||||||
return gameModes;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,162 +0,0 @@
|
||||||
package bentobox.addon.limits.commands;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.entity.EntityType;
|
|
||||||
|
|
||||||
import bentobox.addon.limits.Limits;
|
|
||||||
import bentobox.addon.limits.objects.IslandBlockCount;
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
public final static Map<EntityType, Material> E2M = new HashMap<>();
|
|
||||||
// This is a map of blocks to Material
|
|
||||||
public final static Map<Material, Material> B2M = new HashMap<>();
|
|
||||||
static {
|
|
||||||
E2M.put(EntityType.PIG_ZOMBIE, Material.ZOMBIE_PIGMAN_SPAWN_EGG);
|
|
||||||
E2M.put(EntityType.MUSHROOM_COW, Material.MOOSHROOM_SPAWN_EGG);
|
|
||||||
E2M.put(EntityType.SNOWMAN, Material.SNOW_BLOCK);
|
|
||||||
E2M.put(EntityType.IRON_GOLEM, Material.IRON_BLOCK);
|
|
||||||
E2M.put(EntityType.ILLUSIONER, Material.VILLAGER_SPAWN_EGG);
|
|
||||||
E2M.put(EntityType.WITHER, Material.WITHER_SKELETON_SKULL);
|
|
||||||
E2M.put(EntityType.BOAT, Material.OAK_BOAT);
|
|
||||||
E2M.put(EntityType.ARMOR_STAND, Material.ARMOR_STAND);
|
|
||||||
E2M.put(EntityType.ITEM_FRAME, Material.ITEM_FRAME);
|
|
||||||
E2M.put(EntityType.PAINTING, Material.PAINTING);
|
|
||||||
|
|
||||||
// Minecarts
|
|
||||||
E2M.put(EntityType.MINECART_TNT, Material.TNT_MINECART);
|
|
||||||
E2M.put(EntityType.MINECART_CHEST, Material.CHEST_MINECART);
|
|
||||||
E2M.put(EntityType.MINECART_COMMAND, Material.COMMAND_BLOCK_MINECART);
|
|
||||||
E2M.put(EntityType.MINECART_FURNACE, Material.FURNACE_MINECART);
|
|
||||||
E2M.put(EntityType.MINECART_HOPPER, Material.HOPPER_MINECART);
|
|
||||||
E2M.put(EntityType.MINECART_MOB_SPAWNER, Material.MINECART);
|
|
||||||
// Disallowed
|
|
||||||
E2M.put(EntityType.PRIMED_TNT, null);
|
|
||||||
E2M.put(EntityType.EVOKER_FANGS, null);
|
|
||||||
E2M.put(EntityType.LLAMA_SPIT, null);
|
|
||||||
E2M.put(EntityType.DRAGON_FIREBALL, null);
|
|
||||||
E2M.put(EntityType.AREA_EFFECT_CLOUD, null);
|
|
||||||
E2M.put(EntityType.ENDER_SIGNAL, null);
|
|
||||||
E2M.put(EntityType.SMALL_FIREBALL, null);
|
|
||||||
E2M.put(EntityType.FIREBALL, null);
|
|
||||||
E2M.put(EntityType.THROWN_EXP_BOTTLE, null);
|
|
||||||
E2M.put(EntityType.EXPERIENCE_ORB, null);
|
|
||||||
E2M.put(EntityType.SHULKER_BULLET, null);
|
|
||||||
E2M.put(EntityType.WITHER_SKULL, null);
|
|
||||||
E2M.put(EntityType.TRIDENT, null);
|
|
||||||
E2M.put(EntityType.ARROW, null);
|
|
||||||
E2M.put(EntityType.SPECTRAL_ARROW, null);
|
|
||||||
E2M.put(EntityType.SNOWBALL, null);
|
|
||||||
E2M.put(EntityType.EGG, null);
|
|
||||||
E2M.put(EntityType.LEASH_HITCH, null);
|
|
||||||
E2M.put(EntityType.GIANT, null);
|
|
||||||
E2M.put(EntityType.ENDER_CRYSTAL, null);
|
|
||||||
E2M.put(EntityType.ENDER_PEARL, null);
|
|
||||||
E2M.put(EntityType.ENDER_DRAGON, null);
|
|
||||||
// Block to Material icons
|
|
||||||
Optional.ofNullable(Material.getMaterial("SWEET_BERRY_BUSH")).ifPresent(material -> {
|
|
||||||
B2M.put(material, Material.getMaterial("SWEET_BERRIES"));
|
|
||||||
});
|
|
||||||
B2M.put(Material.POTATOES, Material.POTATO);
|
|
||||||
B2M.put(Material.CARROTS, Material.CARROT);
|
|
||||||
B2M.put(Material.BEETROOTS, Material.BEETROOT);
|
|
||||||
Optional.ofNullable(Material.getMaterial("BAMBOO_SAPLING")).ifPresent(material -> {
|
|
||||||
B2M.put(material, Material.getMaterial("BAMBOO"));
|
|
||||||
});
|
|
||||||
B2M.put(Material.REDSTONE_WIRE, Material.REDSTONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
addon.getSettings().getLimits().forEach((k,v) -> {
|
|
||||||
PanelItemBuilder pib = new PanelItemBuilder();
|
|
||||||
pib.name(Util.prettifyText(k.toString()));
|
|
||||||
Material m = Material.BARRIER;
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getCount(Island island, EntityType ent) {
|
|
||||||
return island.getWorld().getEntities().stream()
|
|
||||||
.filter(e -> e.getType().equals(ent))
|
|
||||||
.filter(e -> island.inIslandSpace(e.getLocation())).count();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,146 +0,0 @@
|
||||||
package bentobox.addon.limits.commands;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import org.bukkit.ChunkSnapshot;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
|
||||||
|
|
||||||
import bentobox.addon.limits.Limits;
|
|
||||||
import bentobox.addon.limits.listeners.BlockLimitsListener;
|
|
||||||
import bentobox.addon.limits.objects.IslandBlockCount;
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author YellowZaki
|
|
||||||
*/
|
|
||||||
public class LimitsCalc {
|
|
||||||
|
|
||||||
private boolean checking;
|
|
||||||
private Limits addon;
|
|
||||||
private World world;
|
|
||||||
private Island island;
|
|
||||||
private BlockLimitsListener bll;
|
|
||||||
private IslandBlockCount ibc;
|
|
||||||
private Map<Material, Integer> blockCount;
|
|
||||||
private BukkitTask task;
|
|
||||||
private User sender;
|
|
||||||
|
|
||||||
|
|
||||||
LimitsCalc(World world, BentoBox instance, UUID targetPlayer, Limits addon, User sender) {
|
|
||||||
this.checking = true;
|
|
||||||
this.addon = addon;
|
|
||||||
this.world = world;
|
|
||||||
this.island = instance.getIslands().getIsland(world, targetPlayer);
|
|
||||||
this.bll = addon.getBlockLimitListener();
|
|
||||||
this.ibc = bll.getIsland(island.getUniqueId());
|
|
||||||
blockCount = new HashMap<>();
|
|
||||||
this.sender = sender;
|
|
||||||
Set<Pair<Integer, Integer>> chunksToScan = getChunksToScan(island);
|
|
||||||
this.task = addon.getServer().getScheduler().runTaskTimer(addon.getPlugin(), () -> {
|
|
||||||
Set<ChunkSnapshot> chunkSnapshot = new HashSet<>();
|
|
||||||
if (checking) {
|
|
||||||
Iterator<Pair<Integer, Integer>> it = chunksToScan.iterator();
|
|
||||||
if (!it.hasNext()) {
|
|
||||||
// Nothing left
|
|
||||||
tidyUp();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Add chunk snapshots to the list
|
|
||||||
while (it.hasNext() && chunkSnapshot.size() < 200) {
|
|
||||||
Pair<Integer, Integer> pair = it.next();
|
|
||||||
if (!world.isChunkLoaded(pair.x, pair.z)) {
|
|
||||||
world.loadChunk(pair.x, pair.z);
|
|
||||||
chunkSnapshot.add(world.getChunkAt(pair.x, pair.z).getChunkSnapshot());
|
|
||||||
world.unloadChunk(pair.x, pair.z);
|
|
||||||
} else {
|
|
||||||
chunkSnapshot.add(world.getChunkAt(pair.x, pair.z).getChunkSnapshot());
|
|
||||||
}
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
// Move to next step
|
|
||||||
checking = false;
|
|
||||||
checkChunksAsync(chunkSnapshot);
|
|
||||||
}
|
|
||||||
}, 0L, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkChunksAsync(final Set<ChunkSnapshot> chunkSnapshot) {
|
|
||||||
// Run async task to scan chunks
|
|
||||||
addon.getServer().getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> {
|
|
||||||
for (ChunkSnapshot chunk : chunkSnapshot) {
|
|
||||||
scanChunk(chunk);
|
|
||||||
}
|
|
||||||
// Nothing happened, change state
|
|
||||||
checking = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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 < 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, 1);
|
|
||||||
} else {
|
|
||||||
blockCount.put(md, blockCount.get(md) + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
|
||||||
// Cancel
|
|
||||||
task.cancel();
|
|
||||||
if (ibc == null) {
|
|
||||||
ibc = new IslandBlockCount();
|
|
||||||
}
|
|
||||||
ibc.setBlockCount(blockCount);
|
|
||||||
bll.setIsland(island.getUniqueId(), ibc);
|
|
||||||
sender.sendMessage("admin.limits.calc.finished");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,173 +0,0 @@
|
||||||
package bentobox.addon.limits.listeners;
|
|
||||||
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.EventPriority;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
|
||||||
import org.bukkit.event.hanging.HangingPlaceEvent;
|
|
||||||
import org.bukkit.event.vehicle.VehicleCreateEvent;
|
|
||||||
|
|
||||||
import bentobox.addon.limits.Limits;
|
|
||||||
import world.bentobox.bentobox.api.user.User;
|
|
||||||
import world.bentobox.bentobox.database.objects.Island;
|
|
||||||
import world.bentobox.bentobox.util.Util;
|
|
||||||
|
|
||||||
public class EntityLimitListener implements Listener {
|
|
||||||
private final Limits addon;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles entity and natural limitations
|
|
||||||
* @param addon - Limits object
|
|
||||||
*/
|
|
||||||
public EntityLimitListener(Limits addon) {
|
|
||||||
this.addon = addon;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles minecart placing
|
|
||||||
* @param e - event
|
|
||||||
*/
|
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
|
||||||
public void onMinecart(VehicleCreateEvent e) {
|
|
||||||
// Return if not in a known world
|
|
||||||
if (!addon.getPlugin().getIWM().inWorld(e.getVehicle().getWorld())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (addon.getSettings().getLimits().containsKey(e.getVehicle().getType())) {
|
|
||||||
// If someone in that area has the bypass permission, allow the spawning
|
|
||||||
for (Entity entity : 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
|
|
||||||
addon.getIslands().getProtectedIslandAt(e.getVehicle().getLocation()).ifPresent(island -> {
|
|
||||||
// Ignore spawn
|
|
||||||
if (island.isSpawn()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Check if the player is at the limit
|
|
||||||
if (atLimit(island, bypass, e.getVehicle())) {
|
|
||||||
e.setCancelled(true);
|
|
||||||
for (Entity ent : e.getVehicle().getLocation().getWorld().getNearbyEntities(e.getVehicle().getLocation(), 5, 5, 5)) {
|
|
||||||
if (ent instanceof Player) {
|
|
||||||
((Player) ent).updateInventory();
|
|
||||||
User.getInstance(ent).sendMessage("entity-limits.hit-limit", "[entity]",
|
|
||||||
Util.prettifyText(e.getVehicle().getType().toString())
|
|
||||||
,"[number]", String.valueOf(addon.getSettings().getLimits().get(e.getVehicle().getType())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
|
||||||
public void onCreatureSpawn(final CreatureSpawnEvent e) {
|
|
||||||
// Return if not in a known world
|
|
||||||
if (!addon.getPlugin().getIWM().inWorld(e.getLocation())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!addon.getSettings().getLimits().containsKey(e.getEntityType())) {
|
|
||||||
// Unknown entity limit or unlimited
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
boolean bypass = false;
|
|
||||||
// Check why it was spawned
|
|
||||||
switch (e.getSpawnReason()) {
|
|
||||||
// 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
|
|
||||||
checkLimit(e, bypass);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkByPass(Location l) {
|
|
||||||
// If someone in that area has the bypass permission, allow the spawning
|
|
||||||
for (Entity entity : 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* handles paintings and item frames
|
|
||||||
* @param e - event
|
|
||||||
*/
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
|
||||||
public void onBlock(HangingPlaceEvent e) {
|
|
||||||
Player player = e.getPlayer();
|
|
||||||
addon.getIslands().getIslandAt(e.getEntity().getLocation()).ifPresent(island -> {
|
|
||||||
boolean bypass = player.isOp() || player.hasPermission(addon.getPlugin().getIWM().getPermissionPrefix(e.getEntity().getWorld()) + "mod.bypass");
|
|
||||||
// Check if entity can be hung
|
|
||||||
if (!island.isSpawn() && atLimit(island, bypass, e.getEntity())) {
|
|
||||||
// Not allowed
|
|
||||||
e.setCancelled(true);
|
|
||||||
User.getInstance(player).sendMessage("block-limits.hit-limit", "[material]",
|
|
||||||
Util.prettifyText(e.getEntity().getType().toString()),
|
|
||||||
"[number]", String.valueOf(addon.getSettings().getLimits().getOrDefault(e.getEntity().getType(), -1)));
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkLimit(CreatureSpawnEvent e, boolean bypass) {
|
|
||||||
addon.getIslands().getIslandAt(e.getLocation()).ifPresent(island -> {
|
|
||||||
// Check if creature is allowed to spawn or not
|
|
||||||
if (!island.isSpawn() && atLimit(island, bypass, e.getEntity())) {
|
|
||||||
// Not allowed
|
|
||||||
e.setCancelled(true);
|
|
||||||
// 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)) {
|
|
||||||
for (Entity ent : e.getLocation().getWorld().getNearbyEntities(e.getLocation(), 5, 5, 5)) {
|
|
||||||
if (ent instanceof Player) {
|
|
||||||
User.getInstance(ent).sendMessage("entity-limits.hit-limit", "[entity]",
|
|
||||||
Util.prettifyText(e.getEntityType().toString()),
|
|
||||||
"[number]", String.valueOf(addon.getSettings().getLimits().get(e.getEntityType())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if new entities can be added to island
|
|
||||||
* @param island - island
|
|
||||||
* @param bypass - true if this is being done by a player with authorization to bypass limits
|
|
||||||
* @param ent - the entity
|
|
||||||
* @return true if at the limit, false if not
|
|
||||||
*/
|
|
||||||
private boolean atLimit(Island island, boolean bypass, Entity ent) {
|
|
||||||
long count = ent.getWorld().getEntities().stream()
|
|
||||||
.filter(e -> e.getType().equals(ent.getType()))
|
|
||||||
.filter(e -> island.inIslandSpace(e.getLocation())).count();
|
|
||||||
return addon.getSettings().getLimits().containsKey(ent.getType()) && count >= addon.getSettings().getLimits().get(ent.getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,151 +0,0 @@
|
||||||
package bentobox.addon.limits.listeners;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.math.NumberUtils;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.OfflinePlayer;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.EventPriority;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
|
||||||
import org.bukkit.permissions.PermissionAttachmentInfo;
|
|
||||||
|
|
||||||
import bentobox.addon.limits.Limits;
|
|
||||||
import bentobox.addon.limits.objects.IslandBlockCount;
|
|
||||||
import world.bentobox.bentobox.api.events.island.IslandEvent;
|
|
||||||
import world.bentobox.bentobox.api.events.island.IslandEvent.Reason;
|
|
||||||
import world.bentobox.bentobox.api.events.team.TeamEvent.TeamSetownerEvent;
|
|
||||||
import world.bentobox.bentobox.database.objects.Island;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets block limits based on player permission
|
|
||||||
* @author tastybento
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class JoinListener implements Listener {
|
|
||||||
|
|
||||||
private final Limits addon;
|
|
||||||
|
|
||||||
public JoinListener(Limits addon) {
|
|
||||||
this.addon = addon;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkPerms(Player player, String permissionPrefix, String islandId, String gameMode) {
|
|
||||||
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(islandId);
|
|
||||||
for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) {
|
|
||||||
if (perms.getPermission().startsWith(permissionPrefix)) {
|
|
||||||
// No wildcards
|
|
||||||
if (perms.getPermission().contains(permissionPrefix + "*")) {
|
|
||||||
logError(player.getName(), perms.getPermission(), "wildcards are not allowed.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Get the Material
|
|
||||||
String[] split = perms.getPermission().split("\\.");
|
|
||||||
if (split.length != 5) {
|
|
||||||
logError(player.getName(), perms.getPermission(), "format must be '" + permissionPrefix + "MATERIAL.NUMBER'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Material m = Material.getMaterial(split[3].toUpperCase(Locale.ENGLISH));
|
|
||||||
if (m == null) {
|
|
||||||
logError(player.getName(), perms.getPermission(), split[3].toUpperCase(Locale.ENGLISH) + " is not a valid material.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Get the max value should there be more than one
|
|
||||||
if (!NumberUtils.isDigits(split[4])) {
|
|
||||||
logError(player.getName(), perms.getPermission(), "the last part MUST be a number!");
|
|
||||||
} else {
|
|
||||||
// Set the limit
|
|
||||||
if (ibc == null) {
|
|
||||||
ibc = new IslandBlockCount(islandId, gameMode);
|
|
||||||
}
|
|
||||||
ibc.setBlockLimit(m, Math.max(ibc.getBlockLimit(m), Integer.valueOf(split[4])));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If any changes have been made then store it
|
|
||||||
if (ibc != null) {
|
|
||||||
addon.getBlockLimitListener().setIsland(islandId, ibc);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logError(String name, String perm, String error) {
|
|
||||||
addon.logError("Player " + name + " has permission: '" + perm + "' but " + error + " Ignoring...");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Event handling
|
|
||||||
*/
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
|
||||||
public void onNewIsland(IslandEvent e) {
|
|
||||||
if (!e.getReason().equals(Reason.CREATED)
|
|
||||||
&& !e.getReason().equals(Reason.RESETTED)
|
|
||||||
&& !e.getReason().equals(Reason.REGISTERED)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setOwnerPerms(e.getIsland(), e.getOwner());
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
|
||||||
public void onOwnerChange(TeamSetownerEvent e) {
|
|
||||||
removeOwnerPerms(e.getIsland());
|
|
||||||
setOwnerPerms(e.getIsland(), e.getNewOwner());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
|
||||||
public void onPlayerJoin(PlayerJoinEvent e) {
|
|
||||||
// Check if player has any islands in the game modes
|
|
||||||
addon.getGameModes().forEach(gm -> {
|
|
||||||
if (addon.getIslands().hasIsland(gm.getOverWorld(), e.getPlayer().getUniqueId())) {
|
|
||||||
String islandId = addon.getIslands().getIsland(gm.getOverWorld(), e.getPlayer().getUniqueId()).getUniqueId();
|
|
||||||
checkPerms(e.getPlayer(), gm.getPermissionPrefix() + "island.limit.", islandId, gm.getDescription().getName());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
|
||||||
public void onUnregisterIsland(IslandEvent e) {
|
|
||||||
if (!e.getReason().equals(Reason.UNREGISTERED)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
removeOwnerPerms(e.getIsland());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Utility methods
|
|
||||||
*/
|
|
||||||
|
|
||||||
private void removeOwnerPerms(Island island) {
|
|
||||||
World world = island.getWorld();
|
|
||||||
if (addon.inGameModeWorld(world)) {
|
|
||||||
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(island.getUniqueId());
|
|
||||||
if (ibc != null) {
|
|
||||||
ibc.getBlockLimits().clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setOwnerPerms(Island island, UUID ownerUUID) {
|
|
||||||
World world = island.getWorld();
|
|
||||||
if (addon.inGameModeWorld(world)) {
|
|
||||||
// Check if owner is online
|
|
||||||
OfflinePlayer owner = Bukkit.getOfflinePlayer(ownerUUID);
|
|
||||||
if (owner.isOnline()) {
|
|
||||||
// Set perm-based limits
|
|
||||||
String prefix = addon.getGameModePermPrefix(world);
|
|
||||||
String name = addon.getGameModeName(world);
|
|
||||||
if (!prefix.isEmpty() && !name.isEmpty()) {
|
|
||||||
checkPerms(owner.getPlayer(), prefix + "island.limit.", island.getUniqueId(), name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,161 +0,0 @@
|
||||||
package bentobox.addon.limits.objects;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.bukkit.Material;
|
|
||||||
|
|
||||||
import com.google.gson.annotations.Expose;
|
|
||||||
|
|
||||||
import world.bentobox.bentobox.database.objects.DataObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author tastybento
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class IslandBlockCount implements DataObject {
|
|
||||||
|
|
||||||
@Expose
|
|
||||||
private String uniqueId = "";
|
|
||||||
|
|
||||||
@Expose
|
|
||||||
private String gameMode = "";
|
|
||||||
|
|
||||||
@Expose
|
|
||||||
private Map<Material, Integer> blockCount = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Permission based limits
|
|
||||||
*/
|
|
||||||
@Expose
|
|
||||||
private Map<Material, Integer> blockLimits = new HashMap<>();
|
|
||||||
|
|
||||||
// Required for YAML database
|
|
||||||
public IslandBlockCount() {}
|
|
||||||
|
|
||||||
public IslandBlockCount(String uniqueId2, String gameMode2) {
|
|
||||||
this.uniqueId = uniqueId2;
|
|
||||||
this.gameMode = gameMode2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see world.bentobox.bentobox.database.objects.DataObject#getUniqueId()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String getUniqueId() {
|
|
||||||
return uniqueId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see world.bentobox.bentobox.database.objects.DataObject#setUniqueId(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setUniqueId(String uniqueId) {
|
|
||||||
this.uniqueId = uniqueId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the blockCount
|
|
||||||
*/
|
|
||||||
public Map<Material, Integer> getBlockCount() {
|
|
||||||
return blockCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param blockCount the blockCount to set
|
|
||||||
*/
|
|
||||||
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
|
|
||||||
* @return true if no more material can be added
|
|
||||||
*/
|
|
||||||
public boolean isAtLimit(Material m) {
|
|
||||||
// Check island limits first
|
|
||||||
return blockLimits.containsKey(m) && blockCount.getOrDefault(m, 0) >= blockLimits.get(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBlockLimited(Material m) {
|
|
||||||
return blockLimits.containsKey(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the blockLimits
|
|
||||||
*/
|
|
||||||
public Map<Material, Integer> getBlockLimits() {
|
|
||||||
return blockLimits;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param blockLimits the blockLimits to set
|
|
||||||
*/
|
|
||||||
public void setBlockLimits(Map<Material, Integer> blockLimits) {
|
|
||||||
this.blockLimits = blockLimits;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the block limit for this material for this island
|
|
||||||
* @param m - material
|
|
||||||
* @return limit or -1 for unlimited
|
|
||||||
*/
|
|
||||||
public Integer getBlockLimit(Material m) {
|
|
||||||
return blockLimits.getOrDefault(m, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
blockLimits.put(m, limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the gameMode
|
|
||||||
*/
|
|
||||||
public String getGameMode() {
|
|
||||||
return gameMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGameMode(String gameMode) {
|
|
||||||
return this.gameMode.equals(gameMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param gameMode the gameMode to set
|
|
||||||
*/
|
|
||||||
public void setGameMode(String gameMode) {
|
|
||||||
this.gameMode = gameMode;
|
|
||||||
}
|
|
||||||
}
|
|
262
src/main/java/world/bentobox/limits/Limits.java
Normal file
262
src/main/java/world/bentobox/limits/Limits.java
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
package world.bentobox.limits;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
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 world.bentobox.bentobox.api.addons.Addon;
|
||||||
|
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||||
|
import world.bentobox.bentobox.api.user.User;
|
||||||
|
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.EntityLimitListener;
|
||||||
|
import world.bentobox.limits.listeners.JoinListener;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Addon to BentoBox that monitors and enforces limits
|
||||||
|
* @author tastybento
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Limits extends Addon {
|
||||||
|
|
||||||
|
private static final String LIMIT_NOT_SET = "Limit not set";
|
||||||
|
private Settings settings;
|
||||||
|
private List<GameModeAddon> gameModes;
|
||||||
|
private BlockLimitsListener blockLimitListener;
|
||||||
|
private JoinListener joinListener;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable(){
|
||||||
|
if (blockLimitListener != null) {
|
||||||
|
blockLimitListener.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
// Load the plugin's config
|
||||||
|
saveDefaultConfig();
|
||||||
|
// Load settings
|
||||||
|
settings = new Settings(this);
|
||||||
|
// Register worlds from GameModes
|
||||||
|
gameModes = getPlugin().getAddonsManager().getGameModeAddons().stream()
|
||||||
|
.filter(gm -> settings.getGameModes().contains(gm.getDescription().getName()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
gameModes.forEach(gm ->
|
||||||
|
{
|
||||||
|
// Register commands
|
||||||
|
gm.getAdminCommand().ifPresent(a -> new AdminCommand(this, a));
|
||||||
|
gm.getPlayerCommand().ifPresent(a -> new PlayerCommand(this, a));
|
||||||
|
registerPlaceholders(gm);
|
||||||
|
log("Limits will apply to " + gm.getDescription().getName());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// Register listener
|
||||||
|
blockLimitListener = new BlockLimitsListener(this);
|
||||||
|
registerListener(blockLimitListener);
|
||||||
|
joinListener = new JoinListener(this);
|
||||||
|
registerListener(joinListener);
|
||||||
|
registerListener(new EntityLimitListener(this));
|
||||||
|
// Done
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the settings
|
||||||
|
*/
|
||||||
|
public Settings getSettings() {
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the gameModes
|
||||||
|
*/
|
||||||
|
public List<GameModeAddon> getGameModes() {
|
||||||
|
return gameModes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the blockLimitListener
|
||||||
|
*/
|
||||||
|
public BlockLimitsListener getBlockLimitListener() {
|
||||||
|
return blockLimitListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this world is covered by the activated game modes
|
||||||
|
* @param world - world
|
||||||
|
* @return true or false
|
||||||
|
*/
|
||||||
|
public boolean inGameModeWorld(World world) {
|
||||||
|
return gameModes.stream().anyMatch(gm -> gm.inWorld(world));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the game mode for this world
|
||||||
|
* @param world - world
|
||||||
|
* @return game mode name or empty string if none
|
||||||
|
*/
|
||||||
|
public String getGameModeName(World world) {
|
||||||
|
return gameModes.stream().filter(gm -> gm.inWorld(world)).findFirst().map(gm -> gm.getDescription().getName()).orElse("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the game mode for this world
|
||||||
|
* @param world - world
|
||||||
|
* @return game mode name or empty string if none
|
||||||
|
*/
|
||||||
|
public String getGameModePermPrefix(World world) {
|
||||||
|
return gameModes.stream().filter(gm -> gm.inWorld(world)).findFirst().map(GameModeAddon::getPermissionPrefix).orElse("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if any of the game modes covered have this name
|
||||||
|
* @param gameMode - name of game mode
|
||||||
|
* @return true or false
|
||||||
|
*/
|
||||||
|
public boolean isCoveredGameMode(String 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;
|
||||||
|
Arrays.stream(Material.values())
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
14
src/main/java/world/bentobox/limits/LimitsPladdon.java
Normal file
14
src/main/java/world/bentobox/limits/LimitsPladdon.java
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package world.bentobox.limits;
|
||||||
|
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.api.addons.Addon;
|
||||||
|
import world.bentobox.bentobox.api.addons.Pladdon;
|
||||||
|
|
||||||
|
|
||||||
|
public class LimitsPladdon extends Pladdon {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Addon getAddon() {
|
||||||
|
return new Limits();
|
||||||
|
}
|
||||||
|
}
|
197
src/main/java/world/bentobox/limits/Settings.java
Normal file
197
src/main/java/world/bentobox/limits/Settings.java
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
package world.bentobox.limits;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
|
public class Settings {
|
||||||
|
|
||||||
|
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 boolean asyncGolums;
|
||||||
|
private static final List<EntityType> DISALLOWED = Arrays.asList(
|
||||||
|
EntityType.PRIMED_TNT,
|
||||||
|
EntityType.EVOKER_FANGS,
|
||||||
|
EntityType.LLAMA_SPIT,
|
||||||
|
EntityType.DRAGON_FIREBALL,
|
||||||
|
EntityType.AREA_EFFECT_CLOUD,
|
||||||
|
EntityType.ENDER_SIGNAL,
|
||||||
|
EntityType.SMALL_FIREBALL,
|
||||||
|
EntityType.FIREBALL,
|
||||||
|
EntityType.THROWN_EXP_BOTTLE,
|
||||||
|
EntityType.EXPERIENCE_ORB,
|
||||||
|
EntityType.SHULKER_BULLET,
|
||||||
|
EntityType.WITHER_SKULL,
|
||||||
|
EntityType.TRIDENT,
|
||||||
|
EntityType.ARROW,
|
||||||
|
EntityType.SPECTRAL_ARROW,
|
||||||
|
EntityType.SNOWBALL,
|
||||||
|
EntityType.EGG,
|
||||||
|
EntityType.LEASH_HITCH,
|
||||||
|
EntityType.GIANT,
|
||||||
|
EntityType.ENDER_CRYSTAL,
|
||||||
|
EntityType.ENDER_PEARL,
|
||||||
|
EntityType.ENDER_DRAGON,
|
||||||
|
EntityType.ITEM_FRAME,
|
||||||
|
EntityType.PAINTING);
|
||||||
|
|
||||||
|
public Settings(Limits addon) {
|
||||||
|
|
||||||
|
// GameModes
|
||||||
|
gameModes = addon.getConfig().getStringList("gamemodes");
|
||||||
|
|
||||||
|
ConfigurationSection el = addon.getConfig().getConfigurationSection("entitylimits");
|
||||||
|
if (el != null) {
|
||||||
|
for (String key : el.getKeys(false)) {
|
||||||
|
EntityType type = getType(key);
|
||||||
|
if (type != null) {
|
||||||
|
if (DISALLOWED.contains(type)) {
|
||||||
|
addon.logError("Entity type: " + key + " is not supported - skipping...");
|
||||||
|
} else {
|
||||||
|
limits.put(type, el.getInt(key, 0));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addon.logError("Unknown entity type: " + key + " - skipping...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Async Golums
|
||||||
|
asyncGolums = addon.getConfig().getBoolean("async-golums", true);
|
||||||
|
|
||||||
|
addon.log("Entity limits:");
|
||||||
|
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");
|
||||||
|
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);
|
||||||
|
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) {
|
||||||
|
return Arrays.stream(EntityType.values()).filter(v -> v.name().equalsIgnoreCase(key)).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the entity limits
|
||||||
|
*/
|
||||||
|
public Map<EntityType, Integer> getLimits() {
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
public List<String> getGameModes() {
|
||||||
|
return gameModes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A named class representing a group of entities and their limits
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static class EntityGroup {
|
||||||
|
private final String name;
|
||||||
|
private final Set<EntityType> types;
|
||||||
|
private final int limit;
|
||||||
|
|
||||||
|
public EntityGroup(String name, Set<EntityType> types, int limit) {
|
||||||
|
this.name = name;
|
||||||
|
this.types = types;
|
||||||
|
this.limit = limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "EntityGroup{" + "name=" + name + ", types=" + types + ", limit=" + limit + '}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the asyncGolums
|
||||||
|
*/
|
||||||
|
public boolean isAsyncGolums() {
|
||||||
|
return asyncGolums;
|
||||||
|
}
|
||||||
|
}
|
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,14 +1,17 @@
|
||||||
package bentobox.addon.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 bentobox.addon.limits.Limits;
|
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.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,22 +1,25 @@
|
||||||
package bentobox.addon.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 bentobox.addon.limits.Limits;
|
|
||||||
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.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
|
||||||
|
@ -24,7 +27,7 @@ public class CalcCommand extends CompositeCommand {
|
||||||
* @param addon - addon
|
* @param addon - addon
|
||||||
*/
|
*/
|
||||||
public CalcCommand(Limits addon, CompositeCommand parent) {
|
public CalcCommand(Limits addon, CompositeCommand parent) {
|
||||||
super(parent, "calc");
|
super(parent, "calc", "recount");
|
||||||
this.addon = addon;
|
this.addon = addon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public 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,689 @@
|
||||||
|
//
|
||||||
|
// Created by BONNe
|
||||||
|
// Copyright - 2022
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package world.bentobox.limits.commands.admin;
|
||||||
|
|
||||||
|
|
||||||
|
import com.google.common.base.Enums;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.NamespacedKey;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
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.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
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.Limits;
|
||||||
|
import world.bentobox.limits.Settings.EntityGroup;
|
||||||
|
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.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
EntityType k = v.getTypes().iterator().next();
|
||||||
|
pib.name(v.getName());
|
||||||
|
String description = "";
|
||||||
|
description += "(" + prettyNames(v) + ")\n";
|
||||||
|
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, 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(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(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() {
|
||||||
|
return user.getTranslation(world, "limits.panel-title") + " " + sortBy.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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,10 +1,11 @@
|
||||||
package bentobox.addon.limits.commands;
|
package world.bentobox.limits.commands.player;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import bentobox.addon.limits.Limits;
|
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.limits.Limits;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User command for limits
|
* User command for limits
|
||||||
|
@ -45,7 +46,7 @@ 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());
|
new LimitPanel(addon).showLimits((GameModeAddon)getAddon(), user, user.getUniqueId());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,10 +1,14 @@
|
||||||
package bentobox.addon.limits.commands;
|
package world.bentobox.limits.commands.player;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import bentobox.addon.limits.Limits;
|
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.calculators.Pipeliner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -13,6 +17,7 @@ import world.bentobox.bentobox.api.user.User;
|
||||||
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.Settings.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package bentobox.addon.limits.listeners;
|
package world.bentobox.limits.listeners;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
@ -7,33 +7,29 @@ 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.entity.Player;
|
|
||||||
import org.bukkit.event.Cancellable;
|
import org.bukkit.event.Cancellable;
|
||||||
|
import org.bukkit.event.Event;
|
||||||
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.block.BlockBreakEvent;
|
import org.bukkit.event.block.*;
|
||||||
import org.bukkit.event.block.BlockBurnEvent;
|
|
||||||
import org.bukkit.event.block.BlockExplodeEvent;
|
|
||||||
import org.bukkit.event.block.BlockFadeEvent;
|
|
||||||
import org.bukkit.event.block.BlockFormEvent;
|
|
||||||
import org.bukkit.event.block.BlockFromToEvent;
|
|
||||||
import org.bukkit.event.block.BlockMultiPlaceEvent;
|
|
||||||
import org.bukkit.event.block.BlockPlaceEvent;
|
|
||||||
import org.bukkit.event.block.BlockSpreadEvent;
|
|
||||||
import org.bukkit.event.block.EntityBlockFormEvent;
|
|
||||||
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.eclipse.jdt.annotation.NonNull;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
import bentobox.addon.limits.Limits;
|
import world.bentobox.bentobox.api.events.island.IslandDeleteEvent;
|
||||||
import bentobox.addon.limits.objects.IslandBlockCount;
|
|
||||||
import world.bentobox.bentobox.api.events.island.IslandEvent.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.objects.IslandBlockCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
|
@ -63,7 +59,7 @@ public class BlockLimitsListener implements Listener {
|
||||||
private final Map<String, Integer> saveMap = new HashMap<>();
|
private final Map<String, Integer> saveMap = new HashMap<>();
|
||||||
private final Database<IslandBlockCount> handler;
|
private final Database<IslandBlockCount> handler;
|
||||||
private final Map<World, Map<Material, Integer>> worldLimitMap = new HashMap<>();
|
private final Map<World, Map<Material, Integer>> worldLimitMap = new HashMap<>();
|
||||||
private Map<Material, Integer> defaultLimitMap = new HashMap<>();
|
private Map<Material, Integer> defaultLimitMap = new EnumMap<>(Material.class);
|
||||||
|
|
||||||
public BlockLimitsListener(Limits addon) {
|
public BlockLimitsListener(Limits addon) {
|
||||||
this.addon = addon;
|
this.addon = addon;
|
||||||
|
@ -72,7 +68,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 {
|
||||||
|
@ -91,19 +87,19 @@ public class BlockLimitsListener implements Listener {
|
||||||
addon.log("Loading default limits");
|
addon.log("Loading default limits");
|
||||||
if (addon.getConfig().isConfigurationSection("blocklimits")) {
|
if (addon.getConfig().isConfigurationSection("blocklimits")) {
|
||||||
ConfigurationSection limitConfig = addon.getConfig().getConfigurationSection("blocklimits");
|
ConfigurationSection limitConfig = addon.getConfig().getConfigurationSection("blocklimits");
|
||||||
defaultLimitMap = loadLimits(limitConfig);
|
defaultLimitMap = loadLimits(Objects.requireNonNull(limitConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load specific worlds
|
// Load specific worlds
|
||||||
if (addon.getConfig().isConfigurationSection("worlds")) {
|
if (addon.getConfig().isConfigurationSection("worlds")) {
|
||||||
ConfigurationSection worlds = addon.getConfig().getConfigurationSection("worlds");
|
ConfigurationSection worlds = addon.getConfig().getConfigurationSection("worlds");
|
||||||
for (String worldName : worlds.getKeys(false)) {
|
for (String worldName : Objects.requireNonNull(worlds).getKeys(false)) {
|
||||||
World world = Bukkit.getWorld(worldName);
|
World world = Bukkit.getWorld(worldName);
|
||||||
if (world != null && addon.inGameModeWorld(world)) {
|
if (world != null && addon.inGameModeWorld(world)) {
|
||||||
addon.log("Loading limits for " + world.getName());
|
addon.log("Loading limits for " + world.getName());
|
||||||
worldLimitMap.putIfAbsent(world, new HashMap<>());
|
worldLimitMap.putIfAbsent(world, new HashMap<>());
|
||||||
ConfigurationSection matsConfig = worlds.getConfigurationSection(worldName);
|
ConfigurationSection matsConfig = worlds.getConfigurationSection(worldName);
|
||||||
worldLimitMap.put(world, loadLimits(matsConfig));
|
worldLimitMap.put(world, loadLimits(Objects.requireNonNull(matsConfig)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,7 +113,7 @@ public class BlockLimitsListener implements Listener {
|
||||||
* @return limit map
|
* @return limit map
|
||||||
*/
|
*/
|
||||||
private Map<Material, Integer> loadLimits(ConfigurationSection cs) {
|
private Map<Material, Integer> loadLimits(ConfigurationSection cs) {
|
||||||
Map<Material, Integer> mats = new HashMap<>();
|
Map<Material, Integer> mats = new EnumMap<>(Material.class);
|
||||||
for (String material : cs.getKeys(false)) {
|
for (String material : cs.getKeys(false)) {
|
||||||
Material mat = Material.getMaterial(material);
|
Material mat = Material.getMaterial(material);
|
||||||
if (mat != null && mat.isBlock() && !DO_NOT_COUNT.contains(mat)) {
|
if (mat != null && mat.isBlock() && !DO_NOT_COUNT.contains(mat)) {
|
||||||
|
@ -135,38 +131,42 @@ 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
|
||||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = false)
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
public void onBlock(BlockPlaceEvent e) {
|
public void onBlock(BlockPlaceEvent e) {
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = false)
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
public void onBlock(BlockBreakEvent e) {
|
public void onBlock(BlockBreakEvent e) {
|
||||||
handleBreak(e, e.getPlayer(), e.getBlock());
|
handleBreak(e, e.getBlock());
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleBreak(Cancellable e, Player player, Block b) {
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
Material mat = b.getType();
|
public void onTurtleEggBreak(PlayerInteractEvent e) {
|
||||||
// Special handling for crops that can break in different ways
|
if (e.getAction().equals(Action.PHYSICAL) && e.getClickedBlock() != null && e.getClickedBlock().getType().equals(Material.TURTLE_EGG)) {
|
||||||
if (mat.equals(Material.WHEAT_SEEDS)) {
|
handleBreak(e, e.getClickedBlock());
|
||||||
mat = Material.WHEAT;
|
|
||||||
} else if (mat.equals(Material.BEETROOT_SEEDS)) {
|
|
||||||
mat = Material.BEETROOT;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleBreak(Event e, Block b) {
|
||||||
|
if (!addon.inGameModeWorld(b.getWorld())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
notify(e, User.getInstance(player), process(b, false, mat), 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);
|
||||||
|
@ -190,9 +190,16 @@ 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.sendMessage("block-limits.hit-limit",
|
user.notify("block-limits.hit-limit",
|
||||||
"[material]", Util.prettifyText(m.toString()),
|
"[material]", Util.prettifyText(m.toString()),
|
||||||
TextVariables.NUMBER, String.valueOf(limit));
|
TextVariables.NUMBER, String.valueOf(limit));
|
||||||
e.setCancelled(true);
|
e.setCancelled(true);
|
||||||
|
@ -220,25 +227,26 @@ public class BlockLimitsListener implements Listener {
|
||||||
process(e.getBlock(), true);
|
process(e.getBlock(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
|
||||||
public void onBlock(BlockGrowEvent e) {
|
|
||||||
Bukkit.getLogger().info(e.getEventName());
|
|
||||||
process(e.getBlock(), true);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
public void onBlock(BlockSpreadEvent e) {
|
public void onBlock(BlockSpreadEvent e) {
|
||||||
//Bukkit.getLogger().info(e.getEventName());
|
|
||||||
process(e.getBlock(), true);
|
process(e.getBlock(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
public void onBlock(EntityBlockFormEvent e) {
|
public void onBlock(EntityBlockFormEvent e) {
|
||||||
//Bukkit.getLogger().info(e.getEventName());
|
|
||||||
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);
|
||||||
|
@ -252,39 +260,54 @@ public class BlockLimitsListener implements Listener {
|
||||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
public void onBlock(EntityChangeBlockEvent e) {
|
public void onBlock(EntityChangeBlockEvent e) {
|
||||||
process(e.getBlock(), false);
|
process(e.getBlock(), false);
|
||||||
|
if (e.getBlock().getType().equals(Material.FARMLAND)) {
|
||||||
|
process(e.getBlock().getRelative(BlockFace.UP), false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
public void onBlock(BlockFromToEvent e) {
|
public void onBlock(BlockFromToEvent e) {
|
||||||
if (e.getBlock().isLiquid()) {
|
if (e.getBlock().isLiquid()
|
||||||
if (e.getToBlock().getType() == Material.REDSTONE_WIRE || e.getToBlock().getType() == Material.REPEATER || e.getToBlock().getType() == Material.COMPARATOR || e.getToBlock().getType() == Material.REDSTONE_TORCH || e.getToBlock().getType() == Material.REDSTONE_WALL_TORCH) {
|
&& (e.getToBlock().getType() == Material.REDSTONE_WIRE
|
||||||
|
|| e.getToBlock().getType() == Material.REPEATER
|
||||||
|
|| e.getToBlock().getType() == Material.COMPARATOR
|
||||||
|
|| e.getToBlock().getType() == Material.REDSTONE_TORCH
|
||||||
|
|| e.getToBlock().getType() == Material.REDSTONE_WALL_TORCH)) {
|
||||||
process(e.getToBlock(), false);
|
process(e.getToBlock(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -292,11 +315,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
|
||||||
|
@ -307,40 +329,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
|
||||||
*
|
*
|
||||||
|
@ -351,18 +387,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;
|
||||||
|
@ -377,16 +413,21 @@ public class BlockLimitsListener implements Listener {
|
||||||
*/
|
*/
|
||||||
public Map<Material, Integer> getMaterialLimits(World w, String id) {
|
public Map<Material, Integer> getMaterialLimits(World w, String id) {
|
||||||
// Merge limits
|
// Merge limits
|
||||||
Map<Material, Integer> result = new HashMap<>();
|
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;
|
||||||
}
|
}
|
||||||
|
@ -413,7 +454,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -422,8 +463,19 @@ public class BlockLimitsListener implements Listener {
|
||||||
* @param islandId - island unique id
|
* @param islandId - island unique id
|
||||||
* @return island block count or null if there is none yet
|
* @return island block count or null if there is none yet
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
public IslandBlockCount getIsland(String islandId) {
|
public IslandBlockCount getIsland(String islandId) {
|
||||||
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()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,472 @@
|
||||||
|
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.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.Tag;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.BlockFace;
|
||||||
|
import org.bukkit.entity.Breedable;
|
||||||
|
import org.bukkit.entity.Villager;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.Cancellable;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
|
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
||||||
|
import org.bukkit.event.entity.EntityBreedEvent;
|
||||||
|
import org.bukkit.event.hanging.HangingPlaceEvent;
|
||||||
|
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.user.User;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.Settings;
|
||||||
|
import world.bentobox.limits.Settings.EntityGroup;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
|
public class EntityLimitListener implements Listener {
|
||||||
|
private static final String MOD_BYPASS = "mod.bypass";
|
||||||
|
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
|
||||||
|
* @param addon - Limits object
|
||||||
|
*/
|
||||||
|
public EntityLimitListener(Limits addon) {
|
||||||
|
this.addon = addon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles minecart placing
|
||||||
|
* @param e - event
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
|
public void onMinecart(VehicleCreateEvent e) {
|
||||||
|
// Return if not in a known world
|
||||||
|
if (!addon.inGameModeWorld(e.getVehicle().getWorld())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Debounce
|
||||||
|
if (justSpawned.contains(e.getVehicle().getUniqueId())) {
|
||||||
|
justSpawned.remove(e.getVehicle().getUniqueId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check island
|
||||||
|
addon.getIslands().getProtectedIslandAt(e.getVehicle().getLocation())
|
||||||
|
// Ignore spawn
|
||||||
|
.filter(i -> !i.isSpawn())
|
||||||
|
.ifPresent(island -> {
|
||||||
|
// Check if the player is at the limit
|
||||||
|
AtLimitResult res = atLimit(island, e.getVehicle());
|
||||||
|
if (res.hit()) {
|
||||||
|
e.setCancelled(true);
|
||||||
|
this.tellPlayers(e.getVehicle().getLocation(), e.getVehicle(), SpawnReason.MOUNT, res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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)
|
||||||
|
public void onCreatureSpawn(final CreatureSpawnEvent e) {
|
||||||
|
// Return if not in a known world
|
||||||
|
if (!addon.inGameModeWorld(e.getLocation().getWorld())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (justSpawned.contains(e.getEntity().getUniqueId())) {
|
||||||
|
justSpawned.remove(e.getEntity().getUniqueId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.getSpawnReason().equals(SpawnReason.SHOULDER_ENTITY) || (!(e.getEntity() instanceof Villager ) && e.getSpawnReason().equals(SpawnReason.BREEDING))) {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handles paintings and item frames
|
||||||
|
* @param e - event
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
|
public void onBlock(HangingPlaceEvent e) {
|
||||||
|
if (!addon.inGameModeWorld(e.getBlock().getWorld())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Player player = e.getPlayer();
|
||||||
|
if (player == null) return;
|
||||||
|
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);
|
||||||
|
// Check if entity can be hung
|
||||||
|
AtLimitResult res;
|
||||||
|
if (!bypass && !island.isSpawn() && (res = atLimit(island, e.getEntity())).hit()) {
|
||||||
|
// Not allowed
|
||||||
|
e.setCancelled(true);
|
||||||
|
if (res.getTypelimit() != null) {
|
||||||
|
User.getInstance(player).notify("block-limits.hit-limit", "[material]",
|
||||||
|
Util.prettifyText(e.getEntity().getType().toString()),
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Island island = addon.getIslands().getIslandAt(e.getLocation()).get();
|
||||||
|
// Check if creature is allowed to spawn or not
|
||||||
|
AtLimitResult res = atLimit(island, e);
|
||||||
|
if (island.isSpawn() || !res.hit()) {
|
||||||
|
// 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
|
||||||
|
tellPlayers(l, e, reason, res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void preSpawn(EntityType entityType, SpawnReason reason, Location l) {
|
||||||
|
|
||||||
|
// 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().equals(Material.CARVED_PUMPKIN)) {
|
||||||
|
// 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().equals(Material.CARVED_PUMPKIN)) {
|
||||||
|
// 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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if new entities can be added to island
|
||||||
|
* @param island - island
|
||||||
|
* @param ent - the entity
|
||||||
|
* @return true if at the limit, false if not
|
||||||
|
*/
|
||||||
|
AtLimitResult atLimit(Island island, Entity ent) {
|
||||||
|
// Check island settings first
|
||||||
|
int limitAmount = -1;
|
||||||
|
Map<Settings.EntityGroup, Integer> groupsLimits = new HashMap<>();
|
||||||
|
|
||||||
|
@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<Settings.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 (limitAmount < 0 && addon.getSettings().getLimits().containsKey(ent.getType())) {
|
||||||
|
limitAmount = addon.getSettings().getLimits().get(ent.getType());
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
if (limitAmount >= 0)
|
||||||
|
{
|
||||||
|
int count = (int) ent.getWorld().getNearbyEntities(island.getBoundingBox()).stream()
|
||||||
|
.filter(e -> e.getType().equals(ent.getType()))
|
||||||
|
.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<Settings.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
276
src/main/java/world/bentobox/limits/listeners/JoinListener.java
Normal file
276
src/main/java/world/bentobox/limits/listeners/JoinListener.java
Normal file
|
@ -0,0 +1,276 @@
|
||||||
|
package world.bentobox.limits.listeners;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
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.Reason;
|
||||||
|
import world.bentobox.bentobox.api.events.team.TeamSetownerEvent;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.Settings.EntityGroup;
|
||||||
|
import world.bentobox.limits.events.LimitsJoinPermCheckEvent;
|
||||||
|
import world.bentobox.limits.events.LimitsPermCheckEvent;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets block limits based on player permission
|
||||||
|
*
|
||||||
|
* @author tastybento
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class JoinListener implements Listener {
|
||||||
|
|
||||||
|
private final Limits addon;
|
||||||
|
|
||||||
|
public JoinListener(Limits addon) {
|
||||||
|
this.addon = addon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
// Check permissions
|
||||||
|
if (ibc != null) {
|
||||||
|
// Clear permission limits
|
||||||
|
ibc.getEntityLimits().clear();
|
||||||
|
ibc.getEntityGroupLimits().clear();
|
||||||
|
ibc.getBlockLimits().clear();
|
||||||
|
}
|
||||||
|
for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) {
|
||||||
|
if (!perms.getValue() || !perms.getPermission().startsWith(permissionPrefix)
|
||||||
|
|| badSyntaxCheck(perms, player.getName(), permissionPrefix)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Check formatting
|
||||||
|
String[] split = perms.getPermission().split("\\.");
|
||||||
|
// Entities & materials
|
||||||
|
EntityType et = Arrays.stream(EntityType.values()).filter(t -> t.name().equalsIgnoreCase(split[3]))
|
||||||
|
.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 (entgroup == null && et == null && m == null) {
|
||||||
|
logError(player.getName(), perms.getPermission(),
|
||||||
|
split[3].toUpperCase(Locale.ENGLISH) + " is not a valid material or entity type/group.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Make an ibc if required
|
||||||
|
if (ibc == null) {
|
||||||
|
ibc = new IslandBlockCount(islandId, gameMode);
|
||||||
|
}
|
||||||
|
// Get the value
|
||||||
|
int value = Integer.parseInt(split[4]);
|
||||||
|
addon.log("Setting login limit via perm for " + player.getName() + "...");
|
||||||
|
|
||||||
|
// Fire perm check event
|
||||||
|
LimitsPermCheckEvent l = new LimitsPermCheckEvent(player, islandId, ibc, entgroup, et, m, value);
|
||||||
|
Bukkit.getPluginManager().callEvent(l);
|
||||||
|
if (l.isCancelled()) {
|
||||||
|
addon.log("Permissions not set because another addon/plugin canceled setting.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logError(String name, String perm, String error) {
|
||||||
|
addon.logError("Player " + name + " has permission: '" + perm + "' but " + error + " Ignoring...");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Event handling
|
||||||
|
*/
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
|
public void onNewIsland(IslandEvent e) {
|
||||||
|
if (!e.getReason().equals(Reason.CREATED) && !e.getReason().equals(Reason.RESETTED)
|
||||||
|
&& !e.getReason().equals(Reason.REGISTERED)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setOwnerPerms(e.getIsland(), e.getOwner());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
|
public void onOwnerChange(TeamSetownerEvent e) {
|
||||||
|
removeOwnerPerms(e.getIsland());
|
||||||
|
setOwnerPerms(e.getIsland(), e.getNewOwner());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
|
public void onPlayerJoin(PlayerJoinEvent e) {
|
||||||
|
// Check if player has any islands in the game modes
|
||||||
|
addon.getGameModes().forEach(gm -> {
|
||||||
|
addon.getIslands().getIslands(gm.getOverWorld(), e.getPlayer().getUniqueId()).stream()
|
||||||
|
.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)
|
||||||
|
public void onUnregisterIsland(IslandEvent e) {
|
||||||
|
if (!e.getReason().equals(Reason.UNREGISTERED)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
removeOwnerPerms(e.getIsland());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Utility methods
|
||||||
|
*/
|
||||||
|
|
||||||
|
private void removeOwnerPerms(Island island) {
|
||||||
|
World world = island.getWorld();
|
||||||
|
if (addon.inGameModeWorld(world)) {
|
||||||
|
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(island.getUniqueId());
|
||||||
|
if (ibc != null) {
|
||||||
|
ibc.getBlockLimits().clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setOwnerPerms(Island island, UUID ownerUUID) {
|
||||||
|
World world = island.getWorld();
|
||||||
|
if (addon.inGameModeWorld(world)) {
|
||||||
|
// Check if owner is online
|
||||||
|
OfflinePlayer owner = Bukkit.getOfflinePlayer(ownerUUID);
|
||||||
|
if (owner.isOnline()) {
|
||||||
|
// Set perm-based limits
|
||||||
|
String prefix = addon.getGameModePermPrefix(world);
|
||||||
|
String name = addon.getGameModeName(world);
|
||||||
|
if (!prefix.isEmpty() && !name.isEmpty() && owner.getPlayer() != null) {
|
||||||
|
checkPerms(Objects.requireNonNull(owner.getPlayer()), prefix + "island.limit.",
|
||||||
|
island.getUniqueId(), name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package bentobox.addon.limits.objects;
|
package world.bentobox.limits.objects;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -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);
|
|
@ -0,0 +1,417 @@
|
||||||
|
package world.bentobox.limits.objects;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.database.objects.DataObject;
|
||||||
|
import world.bentobox.bentobox.database.objects.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tastybento
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Table(name = "IslandBlockCount")
|
||||||
|
public class IslandBlockCount implements DataObject {
|
||||||
|
|
||||||
|
@Expose
|
||||||
|
private Map<Material, Integer> blockCounts = new EnumMap<>(Material.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permission based limits
|
||||||
|
*/
|
||||||
|
@Expose
|
||||||
|
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
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an island block count object
|
||||||
|
*
|
||||||
|
* @param islandId - unique Island ID string
|
||||||
|
* @param gameMode - Game mode name from gm.getDescription().getName()
|
||||||
|
*/
|
||||||
|
public IslandBlockCount(String islandId, String gameMode) {
|
||||||
|
this.uniqueId = islandId;
|
||||||
|
this.gameMode = gameMode;
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a material to the count
|
||||||
|
*
|
||||||
|
* @param material - material
|
||||||
|
*/
|
||||||
|
public void add(Material material) {
|
||||||
|
getBlockCounts().merge(material, 1, Integer::sum);
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all island-specific entity group limits
|
||||||
|
*/
|
||||||
|
public void clearEntityGroupLimits() {
|
||||||
|
entityGroupLimits.clear();
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
public Map<Material, Integer> getBlockCounts() {
|
||||||
|
if (blockCounts == null) {
|
||||||
|
blockCounts = new EnumMap<>(Material.class);
|
||||||
|
}
|
||||||
|
return blockCounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the block limit for this material for this island
|
||||||
|
*
|
||||||
|
* @param m - material
|
||||||
|
* @return limit or -1 for unlimited
|
||||||
|
*/
|
||||||
|
public int getBlockLimit(Material m) {
|
||||||
|
return getBlockLimits().getOrDefault(m, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
public Map<Material, Integer> getBlockLimits() {
|
||||||
|
return Objects.requireNonNullElse(blockLimits, new EnumMap<>(Material.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the blockLimitsOffset
|
||||||
|
*/
|
||||||
|
public Map<Material, Integer> getBlockLimitsOffset() {
|
||||||
|
if (blockLimitsOffset == null) {
|
||||||
|
blockLimitsOffset = new EnumMap<>(Material.class);
|
||||||
|
}
|
||||||
|
return blockLimitsOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the limit for an entity group
|
||||||
|
*
|
||||||
|
* @param name - entity group
|
||||||
|
* @return limit or -1 for unlimited
|
||||||
|
*/
|
||||||
|
public int getEntityGroupLimit(String name) {
|
||||||
|
return getEntityGroupLimits().getOrDefault(name, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the offset for an entity group
|
||||||
|
*
|
||||||
|
* @param name - entity group
|
||||||
|
* @return offset
|
||||||
|
*/
|
||||||
|
public int getEntityGroupLimitOffset(String name) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the gameMode
|
||||||
|
*/
|
||||||
|
public String getGameMode() {
|
||||||
|
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) {
|
||||||
|
return getGameMode().equals(gameMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a material from the count
|
||||||
|
*
|
||||||
|
* @param material - material
|
||||||
|
*/
|
||||||
|
public void remove(Material material) {
|
||||||
|
getBlockCounts().put(material, getBlockCounts().getOrDefault(material, 0) - 1);
|
||||||
|
getBlockCounts().values().removeIf(v -> v <= 0);
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param blockCounts the blockCount to set
|
||||||
|
*/
|
||||||
|
public void setBlockCounts(Map<Material, Integer> blockCounts) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param entityLimits the entityLimits to set
|
||||||
|
*/
|
||||||
|
public void setEntityLimits(Map<EntityType, Integer> entityLimits) {
|
||||||
|
this.entityLimits = entityLimits;
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an offset to an entity limit. This will increase/decrease the value of
|
||||||
|
* the limit.
|
||||||
|
*
|
||||||
|
* @param type Entity Type
|
||||||
|
* @param entityLimitsOffset the entityLimitsOffset to set
|
||||||
|
*/
|
||||||
|
public void setEntityLimitsOffset(EntityType type, Integer entityLimitsOffset) {
|
||||||
|
this.getEntityLimitsOffset().put(type, entityLimitsOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param gameMode the gameMode to set
|
||||||
|
*/
|
||||||
|
public void setGameMode(String gameMode) {
|
||||||
|
this.gameMode = gameMode;
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see
|
||||||
|
* world.bentobox.bentobox.database.objects.DataObject#setUniqueId(java.lang.
|
||||||
|
* String)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setUniqueId(String uniqueId) {
|
||||||
|
this.uniqueId = uniqueId;
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,36 +1,22 @@
|
||||||
name: Limits
|
name: Limits
|
||||||
main: bentobox.addon.limits.Limits
|
main: world.bentobox.limits.Limits
|
||||||
version: ${version}${build.number}
|
version: ${version}${build.number}
|
||||||
|
api-version: 2.3.0
|
||||||
|
|
||||||
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.
|
||||||
|
@ -27,11 +34,10 @@ worlds:
|
||||||
HOPPER: 11
|
HOPPER: 11
|
||||||
|
|
||||||
# Default entity limits within a player's island space (protected area and to island limit).
|
# Default entity limits within a player's island space (protected area and to island limit).
|
||||||
# A limit of 5 will allow up to 5 entites in over world, 5 in nether and 5 in the end.
|
# A limit of 5 will allow up to 5 entities in over world, 5 in nether and 5 in the end.
|
||||||
# Affects all types of creature spawning. Also includes entities like MINECARTS.
|
# Affects all types of creature spawning. Also includes entities like MINECARTS.
|
||||||
# Note: Only the first 49 limited blocks and entities are shown in the limits GUI.
|
# Note: Only the first 49 limited blocks and entities are shown in the limits GUI.
|
||||||
entitylimits:
|
entitylimits:
|
||||||
ENDERMAN: 5
|
ENDERMAN: 5
|
||||||
CHICKEN: 10
|
CHICKEN: 10
|
||||||
pig_ZOMbIe: 15
|
|
||||||
|
|
35
src/main/resources/locales/cs.yml
Normal file
35
src/main/resources/locales/cs.yml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
###########################################################################################
|
||||||
|
# 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:
|
||||||
|
hit-limit: "&c[material] omezen na [number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&cSpawnování [entity] omezeno na [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: "Omezení ostrovů"
|
||||||
|
|
||||||
|
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<player>"
|
||||||
|
description: "ukázat omezení ostrova hráče"
|
||||||
|
calc:
|
||||||
|
parameters: "<player>"
|
||||||
|
description: "přepočítat omezení ostrova hráče"
|
||||||
|
finished: "&aPřepočítání ostrova úspěšně dokončeno!"
|
||||||
|
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: "ukázat omezení tvého ostrova"
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&cTento svět nemá žádné omezení"
|
||||||
|
recount:
|
||||||
|
description: "přepočítá omezení tvého ostrova"
|
||||||
|
|
25
src/main/resources/locales/de.yml
Normal file
25
src/main/resources/locales/de.yml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&c[material] limitiert auf [number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&c[entity] spawning limitiert auf [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: Insel Limitierungen
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<player>"
|
||||||
|
description: Zeige die Limitierungen für den Spieler an
|
||||||
|
calc:
|
||||||
|
parameters: "<player>"
|
||||||
|
description: Berechne die Insel Limitierungen für den Spieler neu
|
||||||
|
finished: "&aInselberechnung erfolgreich abgeschlossen!"
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: Zeige deine Insel Limitierungen
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&cDiese Welt hat keine Limitierungen"
|
||||||
|
recount:
|
||||||
|
description: Zählt die Limitierungen für deine Insel auf
|
|
@ -19,8 +19,33 @@ 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 sucessfully!"
|
finished: "&a Island recalc finished successfully!"
|
||||||
|
offset:
|
||||||
|
unknown: "&c Unknown material or entity [name]."
|
||||||
|
main:
|
||||||
|
parameters: ""
|
||||||
|
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"
|
||||||
|
@ -30,4 +55,7 @@ island:
|
||||||
no-limits: "&cNo limits set in this world"
|
no-limits: "&cNo limits set in this world"
|
||||||
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?"
|
||||||
|
|
||||||
|
|
55
src/main/resources/locales/es.yml
Normal file
55
src/main/resources/locales/es.yml
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
---
|
||||||
|
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: "&cNo hay limites establecidos en este mundo"
|
||||||
|
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?"
|
30
src/main/resources/locales/fr.yml
Normal file
30
src/main/resources/locales/fr.yml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
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!"
|
||||||
|
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: "affichez les limites de votre île"
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&cas de limites définies dans ce monde"
|
||||||
|
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?"
|
33
src/main/resources/locales/hu.yml
Normal file
33
src/main/resources/locales/hu.yml
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
###########################################################################################
|
||||||
|
# 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:
|
||||||
|
hit-limit: "&c[material] limitálva ennyire [number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&c[entity] idézés limitálva ennyire [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: "Sziget limitek"
|
||||||
|
|
||||||
|
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<player>"
|
||||||
|
description: "Egy játékos sziget limitjének megtekintése"
|
||||||
|
calc:
|
||||||
|
parameters: "<player>"
|
||||||
|
description: "Egy játékos sziget limitjének újraszámolása"
|
||||||
|
finished: "&aA Sziget újraszámolás sikeresen befejeződött!"
|
||||||
|
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: "Sziget limitek megtekintése"
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&cNincsenek limitek ebben a világban"
|
||||||
|
recount:
|
||||||
|
description: "Limitek újraszámolása a szigeteden"
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
---
|
---
|
||||||
|
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]!"
|
||||||
limits:
|
|
||||||
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žojumi veiskmīgi pārrēķināti!"
|
|
||||||
island:
|
island:
|
||||||
limits:
|
limits:
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
description: rāda tavas salas ierobežojumus
|
description: rāda tavas salas ierobežojumus
|
||||||
max-color: "&c"
|
max-color: "&c"
|
||||||
regular-color: "&a"
|
|
||||||
block-limit-syntax: "[number]/[limit]"
|
|
||||||
no-limits: "&cŠai pasaulei nav uzstādīti ierobežojumi"
|
no-limits: "&cŠai pasaulei nav uzstādīti ierobežojumi"
|
||||||
recount:
|
recount:
|
||||||
description: pārrēķina ierobežojumus tavai salai
|
description: pārrēķina ierobežojumus tavai salai
|
||||||
|
regular-color: "&a"
|
||||||
|
limits:
|
||||||
|
panel-title: Salas ierobežojumi
|
||||||
|
|
54
src/main/resources/locales/pl.yml
Normal file
54
src/main/resources/locales/pl.yml
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
---
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&c[material] limitowany do [number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&cSpawnowanie [entity] limitowane do [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: Limity wysp
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<gracz>"
|
||||||
|
description: pokazuje limity wysp gracza
|
||||||
|
calc:
|
||||||
|
parameters: "<gracz>"
|
||||||
|
description: ponownie oblicza limity wyspy dla gracza
|
||||||
|
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:
|
||||||
|
limits:
|
||||||
|
description: pokazuje limity twojej wyspy
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&cBrak ustawionych limitów."
|
||||||
|
recount:
|
||||||
|
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?"
|
25
src/main/resources/locales/ro.yml
Normal file
25
src/main/resources/locales/ro.yml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
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!"
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: iti arata limitele insulei
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&cNici o limita setata in aceasta lume"
|
||||||
|
recount:
|
||||||
|
description: renumara limitele insulei tale
|
|
@ -1,32 +1,25 @@
|
||||||
###########################################################################################
|
---
|
||||||
# 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:
|
|
||||||
hit-limit: "&3&LS&b&lC &8&L» &e[material] &4eşyasından &5[number] &4koyabilirsin!"
|
|
||||||
limits:
|
|
||||||
panel-title: "&3&lSon&b&lCesurlar &eAda limiti"
|
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
limits:
|
limits:
|
||||||
main:
|
|
||||||
parameters: "<player>"
|
|
||||||
description: "Oyuncu için ada limitlerini göster."
|
|
||||||
calc:
|
calc:
|
||||||
|
description: Oyuncu için ada limitlerini tekrar hesapla.
|
||||||
|
parameters: "<player>"
|
||||||
|
finished: "&aAda hesaplaması başarıyla yapıldı."
|
||||||
|
main:
|
||||||
|
description: Oyuncu için ada limitlerini göster.
|
||||||
parameters: "<player>"
|
parameters: "<player>"
|
||||||
description: "Oyuncu için ada limitlerini tekrar hesapla."
|
|
||||||
finished: "&aIsland recalc finished sucessfully!"
|
|
||||||
|
|
||||||
island:
|
island:
|
||||||
limits:
|
limits:
|
||||||
parameters: ""
|
block-limit-syntax: "&5[number]&7/&e[limit]"
|
||||||
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]"
|
no-limits: "&4Bu dünyada limitler ayarlı değil!"
|
||||||
no-limits: "&3&LS&c&lC &A&L» &4Bu dünyada limitler ayarlı değil!"
|
recount:
|
||||||
|
description: Ada limitlerini tekrardan hesaplar.
|
||||||
|
block-limits:
|
||||||
|
hit-limit: "&e[material] &4eşyasından &5[number] &4koyabilirsin!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&e[entity] &4varlığından &5[number] &4tane koyabilirsin!"
|
||||||
|
limits:
|
||||||
|
panel-title: "&eAda limiti"
|
||||||
|
|
32
src/main/resources/locales/vi.yml
Normal file
32
src/main/resources/locales/vi.yml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
###########################################################################################
|
||||||
|
# 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:
|
||||||
|
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!"
|
||||||
|
|
||||||
|
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: "&cKhông có giới hạn ở thế giới này"
|
||||||
|
recount:
|
||||||
|
description: "tính toán lại giới hạn đảo của bạn"
|
35
src/main/resources/locales/zh-CN.yml
Normal file
35
src/main/resources/locales/zh-CN.yml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
###########################################################################################
|
||||||
|
# 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:
|
||||||
|
hit-limit: "&c[material] 已限制到 [number]!"
|
||||||
|
entity-limits:
|
||||||
|
hit-limit: "&c[entity] 生成已限制到 [number]!"
|
||||||
|
limits:
|
||||||
|
panel-title: "岛屿限制"
|
||||||
|
|
||||||
|
|
||||||
|
admin:
|
||||||
|
limits:
|
||||||
|
main:
|
||||||
|
parameters: "<player>"
|
||||||
|
description: "显示玩家的限制"
|
||||||
|
calc:
|
||||||
|
parameters: "<player>"
|
||||||
|
description: "重新计算玩家的岛屿限制"
|
||||||
|
finished: "&a岛屿重计算已完成!"
|
||||||
|
|
||||||
|
island:
|
||||||
|
limits:
|
||||||
|
description: "显示您的岛屿限制"
|
||||||
|
max-color: "&c"
|
||||||
|
regular-color: "&a"
|
||||||
|
block-limit-syntax: "[number]/[limit]"
|
||||||
|
no-limits: "&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.19"
|
||||||
|
|
||||||
|
authors: [tastybento]
|
||||||
|
contributors: ["The BentoBoxWorld Community"]
|
||||||
|
website: https://bentobox.world
|
||||||
|
description: ${project.description}
|
|
@ -1,19 +1,18 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package bentobox.addon.limits.listeners;
|
package bentobox.addon.limits.listeners;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
|
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 static org.mockito.Mockito.any;
|
|
||||||
import static org.mockito.Mockito.anyString;
|
|
||||||
import static org.mockito.Mockito.eq;
|
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
@ -21,10 +20,11 @@ import java.util.UUID;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
|
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.junit.After;
|
import org.bukkit.plugin.PluginManager;
|
||||||
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,14 +34,17 @@ import org.powermock.api.mockito.PowerMockito;
|
||||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||||
import org.powermock.modules.junit4.PowerMockRunner;
|
import org.powermock.modules.junit4.PowerMockRunner;
|
||||||
|
|
||||||
import bentobox.addon.limits.Limits;
|
|
||||||
import bentobox.addon.limits.objects.IslandBlockCount;
|
|
||||||
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.Settings;
|
||||||
|
import world.bentobox.limits.listeners.BlockLimitsListener;
|
||||||
|
import world.bentobox.limits.listeners.JoinListener;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
|
@ -54,6 +57,8 @@ 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;
|
||||||
|
@ -69,21 +74,24 @@ public class JoinListenerTest {
|
||||||
private OfflinePlayer owner;
|
private OfflinePlayer owner;
|
||||||
@Mock
|
@Mock
|
||||||
private Island island;
|
private Island island;
|
||||||
|
@Mock
|
||||||
|
private PluginManager pim;
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws java.lang.Exception
|
|
||||||
*/
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() {
|
||||||
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 Settings.EntityGroup("friendly", new HashSet<>(), -1))));
|
||||||
// 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(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
|
||||||
|
@ -106,20 +114,15 @@ 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
|
// Island
|
||||||
when(island.getOwner()).thenReturn(UUID.randomUUID());
|
when(island.getOwner()).thenReturn(UUID.randomUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws java.lang.Exception
|
* Test method for
|
||||||
*/
|
* {@link world.bentobox.limits.listeners.JoinListener#onNewIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
|
||||||
@After
|
|
||||||
public void tearDown() throws Exception {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test method for {@link bentobox.addon.limits.listeners.JoinListener#onNewIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnNewIslandWrongReason() {
|
public void testOnNewIslandWrongReason() {
|
||||||
|
@ -129,7 +132,8 @@ public class JoinListenerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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() {
|
||||||
|
@ -139,7 +143,8 @@ public class JoinListenerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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() {
|
||||||
|
@ -149,7 +154,7 @@ public class JoinListenerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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 testOnNewIslandCreated() {
|
public void testOnNewIslandCreated() {
|
||||||
|
@ -157,11 +162,11 @@ public class JoinListenerTest {
|
||||||
IslandEvent e = new IslandEvent(island, null, false, null, IslandEvent.Reason.CREATED);
|
IslandEvent e = new IslandEvent(island, null, false, null, IslandEvent.Reason.CREATED);
|
||||||
jl.onNewIsland(e);
|
jl.onNewIsland(e);
|
||||||
verify(island).getWorld();
|
verify(island).getWorld();
|
||||||
verify(owner).getPlayer();
|
verify(owner, times(2)).getPlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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 testOnNewIslandCreatedOffline() {
|
public void testOnNewIslandCreatedOffline() {
|
||||||
|
@ -174,7 +179,7 @@ public class JoinListenerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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 testOnNewIslandCreatedNoNameOrPermPrefix() {
|
public void testOnNewIslandCreatedNoNameOrPermPrefix() {
|
||||||
|
@ -190,7 +195,8 @@ public class JoinListenerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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() {
|
||||||
|
@ -203,7 +209,8 @@ public class JoinListenerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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() {
|
||||||
|
@ -214,7 +221,8 @@ public class JoinListenerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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() {
|
||||||
|
@ -225,9 +233,9 @@ public class JoinListenerTest {
|
||||||
verify(bll, never()).setIsland("unique_id", ibc);
|
verify(bll, never()).setIsland("unique_id", ibc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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() {
|
||||||
|
@ -243,73 +251,87 @@ public class JoinListenerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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() {
|
||||||
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
||||||
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
||||||
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.my.perm.for.game");
|
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.my.perm.for.game");
|
||||||
|
when(permAtt.getValue()).thenReturn(true);
|
||||||
perms.add(permAtt);
|
perms.add(permAtt);
|
||||||
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' 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 bentobox.addon.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() {
|
||||||
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
||||||
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
||||||
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.mumbo.34");
|
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.mumbo.34");
|
||||||
|
when(permAtt.getValue()).thenReturn(true);
|
||||||
perms.add(permAtt);
|
perms.add(permAtt);
|
||||||
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. 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 bentobox.addon.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() {
|
||||||
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
||||||
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
||||||
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.*");
|
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.*");
|
||||||
|
when(permAtt.getValue()).thenReturn(true);
|
||||||
perms.add(permAtt);
|
perms.add(permAtt);
|
||||||
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 bentobox.addon.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() {
|
||||||
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
||||||
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
||||||
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.abc");
|
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.abc");
|
||||||
|
when(permAtt.getValue()).thenReturn(true);
|
||||||
perms.add(permAtt);
|
perms.add(permAtt);
|
||||||
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 bentobox.addon.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() {
|
||||||
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
||||||
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
||||||
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.24");
|
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.24");
|
||||||
|
when(permAtt.getValue()).thenReturn(true);
|
||||||
perms.add(permAtt);
|
perms.add(permAtt);
|
||||||
when(player.getEffectivePermissions()).thenReturn(perms);
|
when(player.getEffectivePermissions()).thenReturn(perms);
|
||||||
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
|
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
|
||||||
|
@ -319,31 +341,86 @@ public class JoinListenerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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 testOnPlayerJoinWithPermLimitsSuccessEntity() {
|
||||||
|
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
||||||
|
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
||||||
|
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.BAT.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).setEntityLimit(eq(EntityType.BAT), eq(24));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
||||||
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
||||||
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
||||||
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.24");
|
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.24");
|
||||||
|
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);
|
||||||
perms.add(permAtt2);
|
perms.add(permAtt2);
|
||||||
PermissionAttachmentInfo permAtt3 = mock(PermissionAttachmentInfo.class);
|
PermissionAttachmentInfo permAtt3 = mock(PermissionAttachmentInfo.class);
|
||||||
when(permAtt3.getPermission()).thenReturn("bskyblock.island.limit.dirt.34");
|
when(permAtt3.getPermission()).thenReturn("bskyblock.island.limit.dirt.34");
|
||||||
|
when(permAtt3.getValue()).thenReturn(true);
|
||||||
perms.add(permAtt3);
|
perms.add(permAtt3);
|
||||||
|
PermissionAttachmentInfo permAtt4 = mock(PermissionAttachmentInfo.class);
|
||||||
|
when(permAtt4.getPermission()).thenReturn("bskyblock.island.limit.chicken.34");
|
||||||
|
when(permAtt4.getValue()).thenReturn(true);
|
||||||
|
perms.add(permAtt4);
|
||||||
|
PermissionAttachmentInfo permAtt5 = mock(PermissionAttachmentInfo.class);
|
||||||
|
when(permAtt5.getPermission()).thenReturn("bskyblock.island.limit.cave_spider.4");
|
||||||
|
when(permAtt5.getValue()).thenReturn(true);
|
||||||
|
perms.add(permAtt5);
|
||||||
|
PermissionAttachmentInfo permAtt6 = mock(PermissionAttachmentInfo.class);
|
||||||
|
when(permAtt6.getPermission()).thenReturn("bskyblock.island.limit.cave_spider.4");
|
||||||
|
when(permAtt6.getValue()).thenReturn(false); // negative perm
|
||||||
|
perms.add(permAtt6);
|
||||||
|
|
||||||
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, 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.CAVE_SPIDER), eq(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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 testOnPlayerJoinWithPermLimitsMultiPermsSameMaterial() {
|
public void testOnPlayerJoinWithPermLimitsMultiPermsSameMaterial() {
|
||||||
|
@ -352,12 +429,15 @@ public class JoinListenerTest {
|
||||||
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
||||||
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
||||||
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.24");
|
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.24");
|
||||||
|
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.STONE.14");
|
when(permAtt2.getPermission()).thenReturn("bskyblock.island.limit.STONE.14");
|
||||||
|
when(permAtt2.getValue()).thenReturn(true);
|
||||||
perms.add(permAtt2);
|
perms.add(permAtt2);
|
||||||
PermissionAttachmentInfo permAtt3 = mock(PermissionAttachmentInfo.class);
|
PermissionAttachmentInfo permAtt3 = mock(PermissionAttachmentInfo.class);
|
||||||
when(permAtt3.getPermission()).thenReturn("bskyblock.island.limit.STONE.34");
|
when(permAtt3.getPermission()).thenReturn("bskyblock.island.limit.STONE.34");
|
||||||
|
when(permAtt3.getValue()).thenReturn(true);
|
||||||
perms.add(permAtt3);
|
perms.add(permAtt3);
|
||||||
when(player.getEffectivePermissions()).thenReturn(perms);
|
when(player.getEffectivePermissions()).thenReturn(perms);
|
||||||
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
|
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
|
||||||
|
@ -369,10 +449,39 @@ public class JoinListenerTest {
|
||||||
verify(ibc).setBlockLimit(eq(Material.STONE), eq(34));
|
verify(ibc).setBlockLimit(eq(Material.STONE), eq(34));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testOnPlayerJoinWithPermLimitsMultiPermsSameEntity() {
|
||||||
|
// IBC - set the entity limit for BAT to be 25 already
|
||||||
|
when(ibc.getEntityLimit(any())).thenReturn(25);
|
||||||
|
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
||||||
|
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
|
||||||
|
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.BAT.24");
|
||||||
|
when(permAtt.getValue()).thenReturn(true);
|
||||||
|
perms.add(permAtt);
|
||||||
|
PermissionAttachmentInfo permAtt2 = mock(PermissionAttachmentInfo.class);
|
||||||
|
when(permAtt2.getPermission()).thenReturn("bskyblock.island.limit.BAT.14");
|
||||||
|
when(permAtt2.getValue()).thenReturn(true);
|
||||||
|
perms.add(permAtt2);
|
||||||
|
PermissionAttachmentInfo permAtt3 = mock(PermissionAttachmentInfo.class);
|
||||||
|
when(permAtt3.getPermission()).thenReturn("bskyblock.island.limit.BAT.34");
|
||||||
|
when(permAtt3.getValue()).thenReturn(true);
|
||||||
|
perms.add(permAtt3);
|
||||||
|
when(player.getEffectivePermissions()).thenReturn(perms);
|
||||||
|
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
|
||||||
|
jl.onPlayerJoin(e);
|
||||||
|
verify(addon, never()).logError(anyString());
|
||||||
|
// Only the limit over 25 should be set
|
||||||
|
verify(ibc, never()).setEntityLimit(eq(EntityType.BAT), eq(24));
|
||||||
|
verify(ibc, never()).setEntityLimit(eq(EntityType.BAT), eq(14));
|
||||||
|
verify(ibc).setEntityLimit(eq(EntityType.BAT), eq(34));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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() {
|
||||||
|
@ -382,7 +491,8 @@ public class JoinListenerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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() {
|
||||||
|
@ -393,7 +503,8 @@ public class JoinListenerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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() {
|
||||||
|
@ -410,7 +521,7 @@ public class JoinListenerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link bentobox.addon.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 testOnUnregisterIslandInWorldNullIBC() {
|
public void testOnUnregisterIslandInWorldNullIBC() {
|
||||||
|
@ -427,6 +538,4 @@ public class JoinListenerTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
package world.bentobox.limits.commands.player;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
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.BentoBox;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.bentobox.managers.IslandWorldManager;
|
||||||
|
import world.bentobox.limits.Limits;
|
||||||
|
import world.bentobox.limits.Settings;
|
||||||
|
import world.bentobox.limits.objects.IslandBlockCount;
|
||||||
|
|
||||||
|
@RunWith(PowerMockRunner.class)
|
||||||
|
@PrepareForTest( Bukkit.class )
|
||||||
|
public class LimitTabTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Limits addon;
|
||||||
|
|
||||||
|
private LimitTab lp;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Island island;
|
||||||
|
@Mock
|
||||||
|
private World world;
|
||||||
|
@Mock
|
||||||
|
private World nether;
|
||||||
|
@Mock
|
||||||
|
private World end;
|
||||||
|
@Mock
|
||||||
|
private BentoBox plugin;
|
||||||
|
@Mock
|
||||||
|
private IslandWorldManager iwm;
|
||||||
|
@Mock
|
||||||
|
private Settings settings;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
// Island
|
||||||
|
when(island.getWorld()).thenReturn(world);
|
||||||
|
// Addon
|
||||||
|
when(addon.getPlugin()).thenReturn(plugin);
|
||||||
|
when(addon.getSettings()).thenReturn(settings);
|
||||||
|
when(settings.getLimits()).thenReturn(Collections.emptyMap());
|
||||||
|
when(plugin.getIWM()).thenReturn(iwm);
|
||||||
|
when(iwm.isNetherIslands(any())).thenReturn(true);
|
||||||
|
when(iwm.isEndIslands(any())).thenReturn(true);
|
||||||
|
when(iwm.getNetherWorld(eq(world))).thenReturn(nether);
|
||||||
|
when(iwm.getEndWorld(eq(world))).thenReturn(end);
|
||||||
|
// Worlds
|
||||||
|
Entity entity = mock(Entity.class);
|
||||||
|
when(entity.getType()).thenReturn(EntityType.BAT);
|
||||||
|
when(entity.getLocation()).thenReturn(mock(Location.class));
|
||||||
|
when(world.getEntities()).thenReturn(Collections.singletonList(entity));
|
||||||
|
when(nether.getEntities()).thenReturn(Collections.singletonList(entity));
|
||||||
|
when(end.getEntities()).thenReturn(Collections.singletonList(entity));
|
||||||
|
lp = new LimitTab(addon, new IslandBlockCount("", ""), Collections.emptyMap(), island, world, null, LimitTab.SORT_BY.A2Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void testShowLimits() {
|
||||||
|
fail("Not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetCountInIslandSpace() {
|
||||||
|
when(island.inIslandSpace(any(Location.class))).thenReturn(true);
|
||||||
|
EntityType ent = EntityType.BAT;
|
||||||
|
assertEquals(3L, lp.getCount(island, ent));
|
||||||
|
ent = EntityType.GHAST;
|
||||||
|
assertEquals(0L, lp.getCount(island, ent));
|
||||||
|
when(iwm.isEndIslands(any())).thenReturn(false);
|
||||||
|
ent = EntityType.BAT;
|
||||||
|
assertEquals(2L, lp.getCount(island, ent));
|
||||||
|
when(iwm.isNetherIslands(any())).thenReturn(false);
|
||||||
|
ent = EntityType.BAT;
|
||||||
|
assertEquals(1L, lp.getCount(island, ent));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetCountNotInIslandSpace() {
|
||||||
|
EntityType ent = EntityType.BAT;
|
||||||
|
assertEquals(0L, lp.getCount(island, ent));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
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.bukkit.util.BoundingBox;
|
||||||
|
import org.junit.After;
|
||||||
|
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());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user