Compare commits

...

74 Commits

Author SHA1 Message Date
tastybento 3a42692909 Merge branch 'develop' of https://github.com/BentoBoxWorld/Limits.git into develop 2024-04-04 09:02:53 -07:00
tastybento 894fc1347c Fix test 2024-04-04 09:02:45 -07:00
tastybento e53f11ea19
Merge pull request #182 from BentoBoxWorld/gitlocalize-28605
Update Spanish locale
2024-04-04 08:55:39 -07:00
tastybento f408d9c00b Update to use BentoBox 2.3.0 2024-04-04 08:55:12 -07:00
Santos f9ce3ede64 Translate es.yml via GitLocalize 2024-03-06 08:43:37 +00:00
mt-gitlocalize 2f01344068 Translate es.yml via GitLocalize 2024-03-06 08:43:37 +00:00
BONNe 7da342bec5 Translate es.yml via GitLocalize 2024-03-06 08:43:36 +00:00
Jaimexo 65c7e6e8d0 Translate es.yml via GitLocalize 2024-03-06 08:43:35 +00:00
tastybento ee577e210a Update to 1.20.4 2024-01-13 08:54:29 -08:00
tastybento 9ffe0c740a Refactor code to enable golem spawning. #180 2024-01-09 17:47:24 +09:00
tastybento 843aa89f6c Version 1.20.1 2024-01-09 12:01:00 +09:00
tastybento 39c630b92a Version 1.20.0
This requires BentoBox 2.0.0 to run.
2023-11-28 16:04:07 -08:00
tastybento 4323a1a53a Update to BentoBox 2.0.0 API
Check for multiple islands when the player joins.
2023-11-28 16:03:11 -08:00
tastybento 2b9b3dacf8 Add NPE protection #178 2023-11-26 08:32:30 -08:00
tastybento 2d92663cd4 BentoBox API 2.0.0 2023-11-12 13:24:15 -08:00
tastybento a35816cc80
Update pom.xml 2023-07-10 21:40:28 -07:00
tastybento 9763dc1227 Update Jacoco 2023-07-10 21:24:10 -07:00
tastybento f6b3e3b8db Added distribution required for Github Action 2023-06-24 13:56:29 -07:00
tastybento 5137c24c91 Update Github Build script 2023-06-24 13:46:08 -07:00
tastybento e16d1f1893 Java 17 for GitHub action 2023-04-15 15:44:18 -07:00
tastybento 6b770d083e Fix tests. Use world#nearbyEntities method. 2023-04-15 12:15:14 -07:00
tastybento eeafa239c4 Avoid using NumberUtils. Update dependencies. 2023-04-15 11:44:56 -07:00
tastybento 68f696a46c
Merge pull request #166 from emmanuelvlad/new-placeholders 2023-04-10 06:52:59 -07:00
BONNe 995ac6191f
Merge pull request #175 from BentoBoxWorld/return-to-plugin
Return to plugin.yml
2023-04-08 19:17:22 +03:00
BONNe d87d194c65
Update LimitsPladdon.java 2023-04-08 19:16:52 +03:00
BONNe 8dd35fabcf
Update pom.xml 2023-04-08 19:16:35 +03:00
BONNe e17245b86f
Create plugin.yml 2023-04-08 19:16:00 +03:00
tastybento f0727586a3 Update annotations 2023-03-25 09:54:26 -07:00
tastybento 69ded8fc3e Add [gamemode].mod.bypass to list of perms in addon.yml 2023-03-18 17:37:49 -04:00
tastybento 9e7a52c4d9
Merge pull request #172 from piotrskibapl/develop
Disable villagers breeding when limit is reached
2023-02-12 10:47:13 -08:00
tastybento 3b6e912bae
Add ${argLine} to get jacoco coverage 2023-02-09 15:20:32 -08:00
evlad 97006d28c1 huge performance boost for entity limit 2022-11-07 02:05:09 +01:00
Piotr Skiba 989ebcba7b disable villagers breeding when limit is reached 2022-09-28 00:08:05 +02:00
BONNe a1c75e1192
Merge pull request #171 from BentoBoxWorld/gitlocalize-20576
Update Polish translation
2022-08-27 15:14:52 +03:00
wiktorm12 fdd827a297 Translate pl.yml via GitLocalize 2022-08-27 12:13:10 +00:00
BONNe bc905adccb
Replace ' with " in FR locale
Some text strings inside fr contained '. It resulted in a locale crash. This should fix that.
2022-07-07 12:05:46 +03:00
BONNe ed2af07f07
Merge pull request #167 from organizatsiya/patch-1
Create fr.yml
2022-07-06 14:41:38 +03:00
evlad f726492eae add new placeholders and BlockGrowEvent 2022-07-05 19:35:00 +02:00
organizatsiya 26d357fd42
Create fr.yml 2022-07-05 19:10:59 +02:00
BONNe cf5be3ef30
Merge pull request #163 from BentoBoxWorld/offset-command
Implement Limits "offset" command.
2022-03-28 19:06:19 +03:00
BONNe be3554bf8c Implement Limits "offset" command.
Offset command have:
- set <player> <material|entity> <number> - sets the specific limit offset for material or entity.
- add <player> <material|entity> <number> - adds the specific limit offset for material or entity.
- remove <player> <material|entity> <number> - removes the specific limit offset for material or entity.
- reset <player> <material|entity> - resets limit offset for material or entity.
- view <player> <material|entity> - views the specific limit offset for material or entity.

Relates to the #149
2022-03-28 19:04:15 +03:00
BONNe 782150f0f4
Fixes offset duplication in Limits Panel 2022-03-27 20:12:15 +03:00
BONNe 388f973aee
Merge pull request #161 from BentoBoxWorld/placeholder-fix
Fixes wrong placeholder value #159
2022-03-27 14:45:56 +03:00
BONNe 5fd461ddea
Add limits offsets to the GUI and Placeholder
Offsets were not added to the GUI and Placeholder value. Part of #159
2022-03-27 14:45:43 +03:00
BONNe b459f8bdb5
Fixes wrong placeholder value #159
Placeholder value did not check default and world limits. It used just island limits that are assigned via permissions.

This commit should fix it.
2022-03-27 14:43:31 +03:00
BONNe baa73b76a5
Merge pull request #160 from YellowZaki/develop
Turned into a Pladdon
2022-03-25 21:18:06 +02:00
YellowZaki d7bff1df7d Turned into a Pladdon 2022-03-25 17:29:27 +01:00
tastybento 1a7d2b8f3e
Merge pull request #156 from YellowZaki/develop
Fixes Villager limit
2022-01-21 18:55:21 -08:00
tastybento e8dffc76f5 Avoid NPE with ibc
https://github.com/BentoBoxWorld/Limits/issues/157
2022-01-21 18:53:45 -08:00
Alberto fe0e935e14
Fixes Villager limit 2022-01-09 20:50:34 +01:00
tastybento 360ba0b2de Rework how offsets are used.
https://github.com/BentoBoxWorld/Upgrades/issues/40
https://github.com/BentoBoxWorld/Limits/issues/117
2022-01-02 16:31:02 -08:00
tastybento d038269f8c Add getIsland method 2022-01-02 12:45:16 -08:00
tastybento 78e02b6196 Ignore groups if they are unknown. 2022-01-02 11:55:10 -08:00
tastybento 7582aa93d7 Merge branch 'develop' of https://github.com/BentoBoxWorld/Limits.git into develop 2022-01-02 11:16:38 -08:00
tastybento 886c6c5fa5 Remove debug 2022-01-02 11:16:27 -08:00
tastybento 8d56a4078a Version 1.19.1 2022-01-02 11:16:18 -08:00
tastybento 6e9d83c348
Merge pull request #154 from katorly/patch-1 2021-12-31 08:11:24 -08:00
Katorly f635c07ead
Update zh-CN translation for Limits 2021-12-31 19:15:02 +08:00
tastybento d8c8102010 Added locale text
https://github.com/BentoBoxWorld/Limits/issues/123
2021-12-26 16:17:14 -08:00
tastybento 343d7bbbde Improved recount code. Should be accurate.
This uses the latest approach done by Level addon. This does not yet
include entity recounting!

https://github.com/BentoBoxWorld/Limits/issues/123
2021-12-26 15:52:56 -08:00
tastybento 4cff598aee Fix bugs in map keys and groups. Fix code smells. 2021-12-25 09:10:53 -08:00
tastybento 51408751a6 Code clean up. Java updates. 2021-12-22 14:37:10 -08:00
tastybento 0248c398f6 Enable Op or mod override for breeding. 2021-12-22 13:43:45 -08:00
tastybento 38aa99b2c7 Remove debug 2021-12-22 12:18:16 -08:00
tastybento d6f12c29dc Prevent breeding using breeding event 2021-12-22 12:16:02 -08:00
tastybento 3f1465d6ef Fix NPEs 2021-12-22 10:11:59 -08:00
tastybento 14c38869df WIP try to use EntityBreedEvent to track breeding 2021-12-22 09:19:00 -08:00
tastybento 328a1a13f7 Added option to ignore the island center block from limits.
Should help with AOneBlock usage.
2021-10-17 14:48:35 -07:00
tastybento 945aae0d8b Update to BentoBox 1.18 API 2021-10-01 17:58:31 -07:00
tastybento b71700895e prevent NPE for blockCounts. 2021-09-11 07:45:09 -07:00
tastybento 390814bedb WIP fix some bugs - still more to fix. 2021-09-06 19:32:29 -07:00
tastybento 05a8a0ebbe Fix tests 2021-09-06 17:14:06 -07:00
tastybento 76c582ba1d Added API to enable offset for limits. 2021-09-06 17:11:02 -07:00
tastybento ad1f060d76 Version 1.18.0 2021-09-06 17:06:18 -07:00
35 changed files with 2631 additions and 1056 deletions

View File

@ -11,21 +11,22 @@ jobs:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 16
uses: actions/setup-java@v1
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: 16
distribution: 'adopt'
java-version: 17
- name: Cache SonarCloud packages
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Maven packages
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}

68
pom.xml
View File

@ -53,18 +53,18 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>16</java.version>
<java.version>17</java.version>
<!-- Non-minecraft related dependencies -->
<powermock.version>2.0.9</powermock.version>
<!-- More visible way how to change dependency versions -->
<spigot.version>1.17.1-R0.1-SNAPSHOT</spigot.version>
<bentobox.version>1.17.1</bentobox.version>
<spigot.version>1.20.4-R0.1-SNAPSHOT</spigot.version>
<bentobox.version>2.3.0-SNAPSHOT</bentobox.version>
<!-- Revision variable removes warning about dynamic version -->
<revision>${build.version}-SNAPSHOT</revision>
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- This allows to change between versions. -->
<build.version>1.17.2</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>
@ -196,21 +196,40 @@
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</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>
<plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<argLine>
--illegal-access=permit
${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>
@ -232,30 +251,45 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.3</version>
<version>0.8.10</version>
<configuration>
<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>
<executions>
<execution>
<id>pre-unit-test</id>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>post-unit-test</id>
<id>report</id>
<goals>
<goal>report</goal>
</goals>
<configuration>
<formats>
<format>XML</format>
</formats>
</configuration>
</execution>
</executions>
</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>
</build>

View File

@ -7,16 +7,18 @@ 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.AdminCommand;
import world.bentobox.limits.commands.PlayerCommand;
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;
/**
@ -26,6 +28,7 @@ import world.bentobox.limits.listeners.JoinListener;
*/
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;
@ -134,8 +137,11 @@ public class Limits extends Addon {
private void registerPlaceholders(GameModeAddon gm) {
if (getPlugin().getPlaceholdersManager() == null) return;
Arrays.stream(Material.values())
.filter(m -> m.isBlock())
.forEach(m -> registerCountAndLimitPlaceholders(m, gm));
.filter(Material::isBlock)
.forEach(m -> registerCountAndLimitPlaceholders(m, gm));
Arrays.stream(EntityType.values())
.forEach(e -> registerCountAndLimitPlaceholders(e, gm));
}
/**
@ -147,9 +153,11 @@ public class Limits extends Addon {
* Placeholders:
* "Limits_bskyblock_island_hopper_count"
* "Limits_bskyblock_island_hopper_limit"
* "Limits_bskyblock_island_hopper_base_limit"
* "Limits_bskyblock_island_zombie_limit"
*
* @param m
* @param gm
* @param m material
* @param gm game mode
*/
private void registerCountAndLimitPlaceholders(Material m, GameModeAddon gm) {
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
@ -158,12 +166,24 @@ public class Limits extends Addon {
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
* @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) {
@ -171,24 +191,72 @@ public class Limits extends Addon {
if (is == null) {
return 0;
}
return getBlockLimitListener().getIsland(gm.getIslands().getIsland(gm.getOverWorld(), user).getUniqueId()).
getBlockCount(m);
@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
* @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";
return LIMIT_NOT_SET;
}
int limit = getBlockLimitListener().getIsland(gm.getIslands().getIsland(gm.getOverWorld(), user).getUniqueId()).
getBlockLimit(m);
return limit == -1 ? "Limit not set" : String.valueOf(limit);
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);
}
}

View 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();
}
}

View File

@ -1,14 +1,6 @@
package world.bentobox.limits;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
import org.bukkit.configuration.ConfigurationSection;
@ -89,7 +81,7 @@ public class Settings {
addon.logError("Unknown entity type: " + s + " - skipping...");
}
return null;
}).filter(e -> e != null).collect(Collectors.toCollection(LinkedHashSet::new));
}).filter(Objects::nonNull).collect(Collectors.toCollection(LinkedHashSet::new));
if (entities.isEmpty())
continue;
EntityGroup group = new EntityGroup(name, entities, limit);
@ -102,7 +94,7 @@ public class Settings {
}
addon.log("Entity group limits:");
getGroupLimitDefinitions().stream().map(e -> "Limit " + e.getName() + " (" + e.getTypes().stream().map(x -> x.name()).collect(Collectors.joining(", ")) + ") to " + e.getLimit()).forEach(addon::log);
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) {
@ -110,7 +102,7 @@ public class Settings {
}
/**
* @return the limits
* @return the entity limits
*/
public Map<EntityType, Integer> getLimits() {
return Collections.unmodifiableMap(limits);
@ -127,7 +119,7 @@ public class Settings {
* @return the group limit definitions
*/
public List<EntityGroup> getGroupLimitDefinitions() {
return groupLimits.values().stream().flatMap(e -> e.stream()).distinct().collect(Collectors.toList());
return groupLimits.values().stream().flatMap(Collection::stream).distinct().toList();
}
/**
@ -186,9 +178,7 @@ public class Settings {
if (getClass() != obj.getClass())
return false;
final EntityGroup other = (EntityGroup) obj;
if (!Objects.equals(this.name, other.name))
return false;
return true;
return Objects.equals(this.name, other.name);
}
@Override

View 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;
}
}

View File

@ -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();
});
}
*/
}
}

View 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;
}
}

View File

@ -0,0 +1 @@
package world.bentobox.limits.calculators;

View File

@ -1,154 +0,0 @@
package world.bentobox.limits.commands;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.data.BlockData;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Pair;
import world.bentobox.bentobox.util.Util;
import world.bentobox.limits.Limits;
import world.bentobox.limits.listeners.BlockLimitsListener;
import world.bentobox.limits.objects.IslandBlockCount;
/**
*
* @author YellowZaki, tastybento
*/
public class LimitsCalc {
private final Limits addon;
private final World world;
private final Island island;
private final BlockLimitsListener bll;
private IslandBlockCount ibc;
private final Map<Material, AtomicInteger> blockCount;
private final User sender;
private final Set<Pair<Integer, Integer>> chunksToScan;
private int count;
private int chunksToScanCount;
private BentoBox plugin;
/**
* Perform a count of all limited blocks or entities on an island
* @param world - game world to scan
* @param instance - BentoBox
* @param targetPlayer - target player's island
* @param addon - addon instance
* @param sender - requester of the count
*/
LimitsCalc(World world, BentoBox instance, UUID targetPlayer, Limits addon, User sender) {
this.plugin = instance;
this.addon = addon;
this.island = instance.getIslands().getIsland(world, targetPlayer);
this.bll = addon.getBlockLimitListener();
this.ibc = bll.getIsland(island.getUniqueId());
blockCount = new EnumMap<>(Material.class);
this.sender = sender;
this.world = world;
// Get chunks to scan
chunksToScan = getChunksToScan(island);
count = 0;
boolean isNether = plugin.getIWM().isNetherGenerate(world) && plugin.getIWM().isNetherIslands(world);
boolean isEnd = plugin.getIWM().isEndGenerate(world) && plugin.getIWM().isEndIslands(world);
// Calculate how many chunks need to be scanned
chunksToScanCount = chunksToScan.size() + (isNether ? chunksToScan.size():0) + (isEnd ? chunksToScan.size():0);
chunksToScan.forEach(c -> {
asyncScan(world, c);
if (isNether) asyncScan(plugin.getIWM().getNetherWorld(world), c);
if (isEnd) asyncScan(plugin.getIWM().getEndWorld(world), c);
});
}
private void asyncScan(World world2, Pair<Integer, Integer> c) {
Util.getChunkAtAsync(world2, c.x, c.z).thenAccept(ch -> {
ChunkSnapshot snapShot = ch.getChunkSnapshot();
Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> {
this.scanChunk(snapShot);
count++;
if (count == chunksToScanCount) {
this.tidyUp();
}
});
});
}
private void scanChunk(ChunkSnapshot chunk) {
for (int x = 0; x < 16; x++) {
// Check if the block coordinate is inside the protection zone and if not, don't count it
if (chunk.getX() * 16 + x < island.getMinProtectedX() || chunk.getX() * 16 + x >= island.getMinProtectedX() + island.getProtectionRange() * 2) {
continue;
}
for (int z = 0; z < 16; z++) {
// Check if the block coordinate is inside the protection zone and if not, don't count it
if (chunk.getZ() * 16 + z < island.getMinProtectedZ() || chunk.getZ() * 16 + z >= island.getMinProtectedZ() + island.getProtectionRange() * 2) {
continue;
}
for (int y = 0; y < Objects.requireNonNull(island.getCenter().getWorld()).getMaxHeight(); y++) {
BlockData blockData = chunk.getBlockData(x, y, z);
// Air is free
if (!blockData.getMaterial().equals(Material.AIR)) {
checkBlock(blockData);
}
}
}
}
}
private void checkBlock(BlockData b) {
Material md = bll.fixMaterial(b);
// md is limited
if (bll.getMaterialLimits(world, island.getUniqueId()).containsKey(md)) {
if (!blockCount.containsKey(md)) {
blockCount.put(md, new AtomicInteger(1));
} else {
blockCount.get(md).getAndIncrement();
}
}
}
private Set<Pair<Integer, Integer>> getChunksToScan(Island island) {
Set<Pair<Integer, Integer>> chunkSnapshot = new HashSet<>();
for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + island.getProtectionRange() * 2 + 16); x += 16) {
for (int z = island.getMinProtectedZ(); z < (island.getMinProtectedZ() + island.getProtectionRange() * 2 + 16); z += 16) {
Pair<Integer, Integer> pair = new Pair<>(world.getBlockAt(x, 0, z).getChunk().getX(), world.getBlockAt(x, 0, z).getChunk().getZ());
chunkSnapshot.add(pair);
}
}
return chunkSnapshot;
}
private void tidyUp() {
if (ibc == null) {
ibc = new IslandBlockCount();
}
ibc.setBlockCounts(blockCount.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().get())));
bll.setIsland(island.getUniqueId(), ibc);
Bukkit.getScheduler().runTask(addon.getPlugin(), () -> sender.sendMessage("admin.limits.calc.finished"));
}
}

View File

@ -1,4 +1,4 @@
package world.bentobox.limits.commands;
package world.bentobox.limits.commands.admin;
import java.util.ArrayList;
import java.util.List;
@ -10,6 +10,8 @@ import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.util.Util;
import world.bentobox.limits.Limits;
import world.bentobox.limits.commands.player.LimitPanel;
/**
* Admin command for limits
@ -27,7 +29,9 @@ public class AdminCommand extends CompositeCommand {
public AdminCommand(Limits addon, CompositeCommand parent) {
super(parent, "limits");
this.addon = addon;
new CalcCommand(addon, this);
new CalcCommand(this.addon, this);
new OffsetCommand(this.addon, this);
}
/* (non-Javadoc)

View File

@ -1,4 +1,4 @@
package world.bentobox.limits.commands;
package world.bentobox.limits.commands.admin;
import java.util.ArrayList;
import java.util.List;
@ -7,16 +7,19 @@ import java.util.UUID;
import world.bentobox.bentobox.api.commands.CompositeCommand;
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.calculators.Pipeliner;
/**
*
* @author YellowZaki
* @author YellowZaki, tastybento
*/
public class CalcCommand extends CompositeCommand {
private final Limits addon;
private Island island;
/**
* Admin command
@ -49,10 +52,26 @@ public class CalcCommand extends CompositeCommand {
if (playerUUID == null) {
user.sendMessage("general.errors.unknown-player", args.get(0));
return true;
}
island = addon.getIslands().getIsland(getWorld(), playerUUID);
if (island == null) {
user.sendMessage("general.errors.player-has-no-island");
return false;
} else {
//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;
} else {
showHelp(this, user);
@ -60,13 +79,7 @@ public class CalcCommand extends CompositeCommand {
}
}
private void calcLimits(UUID targetPlayer, User sender) {
if (addon.getIslands().getIsland(getWorld(), targetPlayer) != null) {
new LimitsCalc(getWorld(), getPlugin(), targetPlayer, addon, sender);
} else {
sender.sendMessage("general.errors.player-has-no-island");
}
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {

View File

@ -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();
}
}
}

View File

@ -1,4 +1,4 @@
package world.bentobox.limits.commands;
package world.bentobox.limits.commands.player;
import java.util.Map;
import java.util.UUID;

View File

@ -1,13 +1,7 @@
package world.bentobox.limits.commands;
package world.bentobox.limits.commands.player;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.bukkit.Material;
@ -88,26 +82,33 @@ public class LimitTab implements Tab {
addEntityLimits(ibc, island);
addEntityGroupLimits(ibc, island);
// Sort
switch (sortBy) {
default:
Collections.sort(result, (o1, o2) -> o1.getName().compareTo(o2.getName()));
break;
case Z2A:
Collections.sort(result, (o1, o2) -> o2.getName().compareTo(o1.getName()));
break;
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, e -> e.getLimit()));
Map<String, EntityGroup> groupbyname = groupmap.keySet().stream().collect(Collectors.toMap(e -> e.getName(), e -> e));
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) ibc.getEntityGroupLimits().entrySet().stream()
.filter(e -> groupbyname.containsKey(e.getKey()))
.forEach(e -> groupmap.put(groupbyname.get(e.getKey()), e.getValue()));
groupmap.forEach((v, limit) -> {
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());
@ -118,7 +119,7 @@ public class LimitTab implements Tab {
if (E2M.containsKey(k)) {
m = E2M.get(k);
} else if (k.isAlive()) {
m = Material.valueOf(k.toString() + "_SPAWN_EGG");
m = Material.valueOf(k + "_SPAWN_EGG");
} else {
// Regular material
m = Material.valueOf(k.toString());
@ -142,7 +143,11 @@ public class LimitTab implements Tab {
// Entity limits
Map<EntityType, Integer> map = new HashMap<>(addon.getSettings().getLimits());
// Merge in any permission-based limits
if (ibc != null) ibc.getEntityLimits().forEach(map::put);
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()));
@ -151,7 +156,7 @@ public class LimitTab implements Tab {
if (E2M.containsKey(k)) {
m = E2M.get(k);
} else if (k.isAlive()) {
m = Material.valueOf(k.toString() + "_SPAWN_EGG");
m = Material.valueOf(k + "_SPAWN_EGG");
} else {
// Regular material
m = Material.valueOf(k.toString());
@ -180,11 +185,12 @@ public class LimitTab implements Tab {
pib.icon(B2M.getOrDefault(en.getKey(), en.getKey()));
int count = ibc == null ? 0 : ibc.getBlockCounts().getOrDefault(en.getKey(), 0);
String color = count >= en.getValue() ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color");
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(en.getValue())));
"[limit]", String.valueOf(value)));
result.add(pib.build());
}
}

View File

@ -1,4 +1,4 @@
package world.bentobox.limits.commands;
package world.bentobox.limits.commands.player;
import java.util.List;

View File

@ -1,10 +1,14 @@
package world.bentobox.limits.commands;
package world.bentobox.limits.commands.player;
import java.util.List;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
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.limits.Limits;
public class RecountCommand extends CompositeCommand {
private final Limits addon;
private @Nullable Island island;
/**
* Player command to do a recount. Has a cooldown
@ -44,7 +49,8 @@ public class RecountCommand extends CompositeCommand {
showHelp(this, user);
return false;
}
if (addon.getIslands().getIsland(getWorld(), user) == null) {
island = addon.getIslands().getIsland(getWorld(), user);
if (island == null) {
user.sendMessage("general.errors.no-island");
return false;
}
@ -54,7 +60,17 @@ public class RecountCommand extends CompositeCommand {
public boolean execute(User user, String label, List<String> args) {
// Set cooldown
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;
}

View File

@ -2,6 +2,7 @@ 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;
@ -21,6 +22,16 @@ public class LimitsJoinPermCheckEvent extends BentoBoxEvent implements Cancellab
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
@ -99,7 +110,7 @@ public class LimitsJoinPermCheckEvent extends BentoBoxEvent implements Cancellab
/**
* Ignore player's perms. This differs to canceling the event in that the IslandBlockCount will be used if given via
* {@link setIbc(IslandBlockCount ibc)}
* {@link #setIbc(IslandBlockCount ibc)}
* @param ignorePerms the ignorePerms to set
*/
public void setIgnorePerms(boolean ignorePerms) {

View File

@ -52,7 +52,7 @@ public class LimitsPermCheckEvent extends LimitsJoinPermCheckEvent {
/**
* @return the entityGroup
*/
public EntityGroup getEntityGroup() {
public @Nullable EntityGroup getEntityGroup() {
return entityGroup;
}
@ -60,7 +60,7 @@ public class LimitsPermCheckEvent extends LimitsJoinPermCheckEvent {
/**
* @param entityGroup the entityGroup to set
*/
public void setEntityGroup(EntityGroup entityGroup) {
public void setEntityGroup(@Nullable EntityGroup entityGroup) {
this.entityGroup = entityGroup;
}
@ -68,7 +68,7 @@ public class LimitsPermCheckEvent extends LimitsJoinPermCheckEvent {
/**
* @return the entityType
*/
public EntityType getEntityType() {
public @Nullable EntityType getEntityType() {
return entityType;
}
@ -76,7 +76,7 @@ public class LimitsPermCheckEvent extends LimitsJoinPermCheckEvent {
/**
* @param entityType the entityType to set
*/
public void setEntityType(EntityType entityType) {
public void setEntityType(@Nullable EntityType entityType) {
this.entityType = entityType;
}
@ -84,7 +84,7 @@ public class LimitsPermCheckEvent extends LimitsJoinPermCheckEvent {
/**
* @return the material
*/
public Material getMaterial() {
public @Nullable Material getMaterial() {
return material;
}
@ -92,7 +92,7 @@ public class LimitsPermCheckEvent extends LimitsJoinPermCheckEvent {
/**
* @param material the material to set
*/
public void setMaterial(Material material) {
public void setMaterial(@Nullable Material material) {
this.material = material;
}

View File

@ -1,14 +1,6 @@
package world.bentobox.limits.listeners;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.*;
import org.bukkit.Bukkit;
import org.bukkit.Material;
@ -23,27 +15,18 @@ import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
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.block.*;
import org.bukkit.event.entity.EntityChangeBlockEvent;
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 world.bentobox.bentobox.api.events.island.IslandDeleteEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.Database;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
import world.bentobox.limits.Limits;
import world.bentobox.limits.objects.IslandBlockCount;
@ -164,7 +147,7 @@ public class BlockLimitsListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onTurtleEggBreak(PlayerInteractEvent e) {
if (e.getAction().equals(Action.PHYSICAL) && e.getClickedBlock().getType().equals(Material.TURTLE_EGG)) {
if (e.getAction().equals(Action.PHYSICAL) && e.getClickedBlock() != null && e.getClickedBlock().getType().equals(Material.TURTLE_EGG)) {
handleBreak(e, e.getClickedBlock());
}
}
@ -207,6 +190,13 @@ public class BlockLimitsListener implements Listener {
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) {
if (limit > -1) {
user.notify("block-limits.hit-limit",
@ -247,6 +237,16 @@ public class BlockLimitsListener implements Listener {
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)
public void onBlock(LeavesDecayEvent e) {
process(e.getBlock(), false);
@ -277,10 +277,11 @@ public class BlockLimitsListener implements Listener {
}
}
// Return equivalents. b can be Block/Material
/**
* 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();
@ -296,8 +297,8 @@ public class BlockLimitsListener implements Listener {
return Material.PLAYER_HEAD;
} else if (mat == Material.DRAGON_WALL_HEAD) {
return Material.DRAGON_HEAD;
} else if (mat != null && mat == Material.getMaterial("BAMBOO_SAPLING")) {
return Material.getMaterial("BAMBOO");
} else if (mat == Material.BAMBOO_SAPLING) {
return Material.BAMBOO;
} else if (mat == Material.PISTON_HEAD || mat == Material.MOVING_PISTON) {
TechnicalPiston tp = (TechnicalPiston) b;
if (tp.getType() == TechnicalPiston.Type.NORMAL) {
@ -314,7 +315,6 @@ public class BlockLimitsListener implements Listener {
*
* @param b - block
* @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
*/
private int process(Block b, boolean add) {
@ -329,6 +329,10 @@ public class BlockLimitsListener implements Listener {
// Invalid world
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));
if (add) {
// Check limit
@ -383,18 +387,18 @@ public class BlockLimitsListener implements Listener {
*/
private int checkLimit(World w, Material m, String id) {
// Check island limits
IslandBlockCount island = islandCountMap.get(id);
if (island.isBlockLimited(m)) {
return island.isAtLimit(m) ? island.getBlockLimit(m) : -1;
IslandBlockCount ibc = islandCountMap.get(id);
if (ibc.isBlockLimited(m)) {
return ibc.isAtLimit(m) ? ibc.getBlockLimit(m) + ibc.getBlockLimitOffset(m) : -1;
}
// Check specific world limits
if (worldLimitMap.containsKey(w) && worldLimitMap.get(w).containsKey(m)) {
// 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
if (defaultLimitMap.containsKey(m) && island.isAtLimit(m, defaultLimitMap.get(m))) {
return defaultLimitMap.get(m);
if (defaultLimitMap.containsKey(m) && ibc.isAtLimit(m, defaultLimitMap.get(m))) {
return defaultLimitMap.get(m) + ibc.getBlockLimitOffset(m);
}
// No limit
return -1;
@ -411,14 +415,19 @@ public class BlockLimitsListener implements Listener {
// Merge limits
Map<Material, Integer> result = new EnumMap<>(Material.class);
// Default
defaultLimitMap.forEach(result::put);
result.putAll(defaultLimitMap);
// World
if (worldLimitMap.containsKey(w)) {
worldLimitMap.get(w).forEach(result::put);
result.putAll(worldLimitMap.get(w));
}
// Island
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;
}
@ -459,4 +468,14 @@ public class BlockLimitsListener implements Listener {
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()));
}
}

View File

@ -2,7 +2,6 @@ package world.bentobox.limits.listeners;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -17,6 +16,8 @@ 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;
@ -27,8 +28,10 @@ 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;
@ -38,22 +41,13 @@ 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;
static {
List<BlockFace> cardinals = new ArrayList<>();
cardinals.add(BlockFace.UP);
cardinals.add(BlockFace.NORTH);
cardinals.add(BlockFace.SOUTH);
cardinals.add(BlockFace.EAST);
cardinals.add(BlockFace.WEST);
cardinals.add(BlockFace.DOWN);
CARDINALS = Collections.unmodifiableList(cardinals);
}
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
@ -61,7 +55,6 @@ public class EntityLimitListener implements Listener {
*/
public EntityLimitListener(Limits addon) {
this.addon = addon;
justSpawned.clear();
}
/**
@ -71,46 +64,39 @@ public class EntityLimitListener implements Listener {
@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;
}
// If someone in that area has the bypass permission, allow the spawning
for (Entity entity : Objects.requireNonNull(e.getVehicle().getLocation().getWorld()).getNearbyEntities(e.getVehicle().getLocation(), 5, 5, 5)) {
if (entity instanceof Player) {
Player player = (Player)entity;
boolean bypass = (player.isOp() || player.hasPermission(addon.getPlugin().getIWM().getPermissionPrefix(e.getVehicle().getWorld()) + MOD_BYPASS));
// Check island
addon.getIslands().getProtectedIslandAt(e.getVehicle().getLocation()).ifPresent(island -> {
// Ignore spawn
if (island.isSpawn()) {
return;
}
// Check if the player is at the limit
AtLimitResult res;
if (!bypass && (res = atLimit(island, e.getVehicle())).hit()) {
e.setCancelled(true);
for (Entity ent : e.getVehicle().getLocation().getWorld().getNearbyEntities(e.getVehicle().getLocation(), 5, 5, 5)) {
if (ent instanceof Player) {
((Player) ent).updateInventory();
if (res.getTypelimit() != null) {
User.getInstance(ent).notify("entity-limits.hit-limit", "[entity]",
Util.prettifyText(e.getVehicle().getType().toString()),
TextVariables.NUMBER, String.valueOf(res.getTypelimit().getValue()));
} else {
User.getInstance(ent).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()));
}
}
}
}
});
// 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);
}
}
@ -124,53 +110,21 @@ public class EntityLimitListener implements Listener {
justSpawned.remove(e.getEntity().getUniqueId());
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;
case SHOULDER_ENTITY:
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;
default:
// Other natural reasons
break;
}
// Some checks can be done async, some not
switch (e.getSpawnReason()) {
case BUILD_SNOWMAN:
case BUILD_IRONGOLEM:
checkLimit(e, e.getEntity(), e.getSpawnReason(), bypass, addon.getSettings().isAsyncGolums());
default:
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(), bypass, false);
break;
checkLimit(e, e.getEntity(), e.getSpawnReason(), false);
}
}
private boolean checkByPass(Location l) {
// If someone in that area has the bypass permission, allow the spawning
for (Entity entity : Objects.requireNonNull(l.getWorld()).getNearbyEntities(l, 5, 5, 5)) {
if (entity instanceof Player) {
Player player = (Player)entity;
if (player.isOp() || player.hasPermission(addon.getPlugin().getIWM().getPermissionPrefix(l.getWorld()) + MOD_BYPASS)) {
return true;
}
}
}
return false;
}
/**
* handles paintings and item frames
* @param e - event
@ -205,61 +159,58 @@ public class EntityLimitListener implements Listener {
/**
* Check if a creature is allowed to spawn or not
* @param e - CreatureSpawnEvent
* @param bypass - true if the player involved can bypass the checks
* @param async - true if check can be done async, false if not
* @return true if allowed or asycn, false if not.
*/
private void checkLimit(Cancellable c, LivingEntity e, SpawnReason reason, boolean bypass, boolean async) {
private boolean checkLimit(Cancellable c, LivingEntity e, SpawnReason reason, boolean async) {
Location l = e.getLocation();
if (async) {
c.setCancelled(true);
}
processIsland(c, e, l, reason, bypass, async);
return processIsland(c, e, l, reason, async);
}
private void processIsland(Cancellable c, LivingEntity e, Location l, SpawnReason reason, boolean bypass, boolean async) {
addon.getIslands().getIslandAt(e.getLocation()).ifPresent(island -> {
// Check if creature is allowed to spawn or not
AtLimitResult res = atLimit(island, e);
if (bypass || island.isSpawn() || !res.hit()) {
// Allowed
if (async) {
Bukkit.getScheduler().runTask(BentoBox.getInstance(), () -> {
l.getWorld().spawn(l, e.getClass(), entity -> preSpawn(entity, reason, l));
});
} // else do nothing
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 {
if (async) {
e.remove();
} else {
c.setCancelled(true);
}
// If the reason is anything but because of a spawner then tell players within range
tellPlayers(c, l, e, reason, res);
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(Entity entity, SpawnReason reason, Location l) {
justSpawned.add(entity.getUniqueId());
private void preSpawn(EntityType entityType, SpawnReason reason, Location l) {
// Check for entities that need cleanup
switch (reason) {
case BUILD_IRONGOLEM:
detectIronGolem(l);
break;
case BUILD_SNOWMAN:
detectSnowman(l);
break;
case BUILD_WITHER:
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);
break;
default:
break;
}
}
@ -297,7 +248,6 @@ public class EntityLimitListener implements Listener {
}
}
}
}
private void detectSnowman(Location l) {
@ -379,28 +329,37 @@ public class EntityLimitListener implements Listener {
return Tag.WITHER_SUMMON_BASE_BLOCKS.isTagged(body.getType());
}
private void tellPlayers(Cancellable e, Location l, LivingEntity 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)) {
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) {
if (res.getTypelimit() != null) {
User.getInstance(ent).notify("entity-limits.hit-limit", "[entity]",
Util.prettifyText(entity.getType().toString()),
TextVariables.NUMBER, String.valueOf(res.getTypelimit().getValue()));
} else {
User.getInstance(ent).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()));
}
/**
* 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()));
}
}
});
}
}
});
}
@ -414,11 +373,16 @@ public class EntityLimitListener implements Listener {
// Check island settings first
int limitAmount = -1;
Map<Settings.EntityGroup, Integer> groupsLimits = new HashMap<>();
if (addon.getBlockLimitListener().getIsland(island.getUniqueId()) != null) {
limitAmount = addon.getBlockLimitListener().getIsland(island.getUniqueId()).getEntityLimit(ent.getType());
@Nullable
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(island.getUniqueId());
if (ibc != null) {
// Get the limit amount for this type
limitAmount = ibc.getEntityLimit(ent.getType());
// Handle entity groups
List<Settings.EntityGroup> groupdefs = addon.getSettings().getGroupLimits().getOrDefault(ent.getType(), new ArrayList<>());
groupdefs.forEach(def -> {
int limit = addon.getBlockLimitListener().getIsland(island.getUniqueId()).getEntityGroupLimit(def.getName());
int limit = ibc.getEntityGroupLimit(def.getName());
if (limit >= 0)
groupsLimits.put(def, limit);
});
@ -427,45 +391,54 @@ public class EntityLimitListener implements Listener {
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();
if (limitAmount < 0 && groupsLimits.isEmpty()) {
return new AtLimitResult();
}
// We have to count the entities
if (limitAmount >= 0)
{
int count = (int) ent.getWorld().getEntities().stream()
int count = (int) ent.getWorld().getNearbyEntities(island.getBoundingBox()).stream()
.filter(e -> e.getType().equals(ent.getType()))
.filter(e -> island.inIslandSpace(e.getLocation()))
.count();
if (count >= limitAmount)
return new AtLimitResult(ent.getType(), limitAmount);
int max = limitAmount + (ibc == null ? 0 : ibc.getEntityLimitOffset(ent.getType()));
if (count >= max) {
return new AtLimitResult(ent.getType(), max);
}
}
// Merge in any permission-based limits
if (addon.getBlockLimitListener().getIsland(island.getUniqueId()) != null) {
Map<String, EntityGroup> groupbyname = groupsLimits.keySet().stream().collect(Collectors.toMap(e -> e.getName(), e -> e));
addon.getBlockLimitListener().getIsland(island.getUniqueId()).getEntityGroupLimits().entrySet().stream()
// 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()
// 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()))
.filter(e -> island.inIslandSpace(e.getLocation())).count();
if (count >= group.getValue())
return new AtLimitResult(group.getKey(), group.getValue());
.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();
}
class AtLimitResult {
static class AtLimitResult {
private Map.Entry<EntityType, Integer> typelimit;
private Map.Entry<EntityGroup, Integer> grouplimit;

View File

@ -5,7 +5,6 @@ import java.util.Locale;
import java.util.Objects;
import java.util.UUID;
import org.apache.commons.lang.math.NumberUtils;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
@ -18,7 +17,6 @@ import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.events.island.IslandEvent.Reason;
@ -32,6 +30,7 @@ import world.bentobox.limits.objects.IslandBlockCount;
/**
* Sets block limits based on player permission
*
* @author tastybento
*
*/
@ -40,116 +39,139 @@ public class JoinListener implements Listener {
private final Limits addon;
public JoinListener(Limits addon) {
this.addon = addon;
this.addon = addon;
}
/**
* Check and set the permissions of the player and how they affect the island limits
* @param player - player
* 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
* @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);
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]);
// Fire perm check event
LimitsPermCheckEvent l = new LimitsPermCheckEvent(player, islandId, ibc, entgroup, et, m, value);
Bukkit.getPluginManager().callEvent(l);
if (l.isCancelled()) continue;
// Use event values
ibc = l.getIbc();
// Run null checks and set ibc
runNullCheckAndSet(ibc, l);
}
// Check removed permissions
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() + "...");
// 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);
// 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
if (!NumberUtils.isDigits(split[4])) {
logError(name, perms.getPermission(), "the last part MUST be a number!");
return true;
}
return false;
// 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(@Nullable IslandBlockCount ibc, @NonNull LimitsPermCheckEvent l) {
if (ibc == null) {
return;
}
EntityGroup entgroup = l.getEntityGroup();
EntityType et = l.getEntityType();
Material m = l.getMaterial();
int value = l.getValue();
if (entgroup != null) {
// Entity group limit
ibc.setEntityGroupLimit(entgroup.getName(), Math.max(ibc.getEntityGroupLimit(entgroup.getName()), value));
} else if (et != null && m == null) {
// Entity limit
ibc.setEntityLimit(et, Math.max(ibc.getEntityLimit(et), value));
} else if (m != null && et == null) {
// Material limit
ibc.setBlockLimit(m, Math.max(ibc.getBlockLimit(m), value));
} else {
if (m != null && m.isBlock()) {
// Material limit
ibc.setBlockLimit(m, Math.max(ibc.getBlockLimit(m), value));
} else if (et != null){
// This is an entity setting
ibc.setEntityLimit(et, Math.max(ibc.getEntityLimit(et), value));
}
}
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...");
addon.logError("Player " + name + " has permission: '" + perm + "' but " + error + " Ignoring...");
}
/*
@ -158,59 +180,66 @@ public class JoinListener implements Listener {
@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());
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());
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 = Objects.requireNonNull(addon.getIslands().getIsland(gm.getOverWorld(), e.getPlayer().getUniqueId())).getUniqueId();
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(islandId);
if (joinEventCheck(e.getPlayer(), islandId, ibc)) {
return;
}
checkPerms(e.getPlayer(), gm.getPermissionPrefix() + "island.limit.", islandId, gm.getDescription().getName());
}
});
// 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;
// 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());
if (!e.getReason().equals(Reason.UNREGISTERED)) {
return;
}
removeOwnerPerms(e.getIsland());
}
/*
@ -218,29 +247,30 @@ public class JoinListener implements Listener {
*/
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();
}
}
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);
}
}
}
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);
}
}
}
}
}

View File

@ -80,10 +80,9 @@ public class EntityLimitsDO implements DataObject {
if (obj == null) {
return false;
}
if (!(obj instanceof EntityLimitsDO)) {
if (!(obj instanceof EntityLimitsDO other)) {
return false;
}
EntityLimitsDO other = (EntityLimitsDO) obj;
if (uniqueId == null) {
return other.uniqueId == null;
} else return uniqueId.equals(other.uniqueId);

View File

@ -3,6 +3,7 @@ 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;
@ -19,280 +20,398 @@ import world.bentobox.bentobox.database.objects.Table;
@Table(name = "IslandBlockCount")
public class IslandBlockCount implements DataObject {
@Expose
private String uniqueId = "";
@Expose
private String gameMode = "";
@Expose
private Map<Material, Integer> blockCounts = new EnumMap<>(Material.class);
private boolean changed;
/**
* 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<String, Integer> entityGroupLimits = new HashMap<>();
// Required for YAML database
public IslandBlockCount() {}
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();
}
/* (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;
setChanged();
}
/**
* @return the blockCount
*/
public Map<Material, Integer> getBlockCounts() {
return blockCounts;
}
/**
* @param blockCounts the blockCount to set
*/
public void setBlockCounts(Map<Material, Integer> blockCounts) {
this.blockCounts = blockCounts;
setChanged();
}
/**
* Get the block count for this material for this island
* @param m - material
* @return count
*/
public Integer getBlockCount(Material m) {
return blockCounts.getOrDefault(m, 0);
this.uniqueId = islandId;
this.gameMode = gameMode;
setChanged();
}
/**
* Add a material to the count
*
* @param material - material
*/
public void add(Material material) {
blockCounts.merge(material, 1, Integer::sum);
setChanged();
getBlockCounts().merge(material, 1, Integer::sum);
setChanged();
}
/**
* Remove a material from the count
* @param material - material
* Clear all island-specific entity group limits
*/
public void remove(Material material) {
blockCounts.put(material, blockCounts.getOrDefault(material, 0) - 1);
blockCounts.values().removeIf(v -> v <= 0);
setChanged();
}
/**
* 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 blockCounts.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) && blockCounts.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;
setChanged();
}
/**
* 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);
setChanged();
}
/**
* @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;
setChanged();
}
/**
* @return the entityLimits
*/
public Map<EntityType, Integer> getEntityLimits() {
return entityLimits;
}
/**
* @param entityLimits the entityLimits to set
*/
public void setEntityLimits(Map<EntityType, Integer> entityLimits) {
this.entityLimits = entityLimits;
setChanged();
}
/**
* Set an island-specific entity type limit
* @param t - entity type
* @param limit - limit
*/
public void setEntityLimit(EntityType t, int limit) {
entityLimits.put(t, limit);
setChanged();
}
/**
* Get the limit for an entity type
* @param t - entity type
* @return limit or -1 for unlimited
*/
public int getEntityLimit(EntityType t) {
return entityLimits.getOrDefault(t, -1);
public void clearEntityGroupLimits() {
entityGroupLimits.clear();
setChanged();
}
/**
* Clear all island-specific entity type limits
*/
public void clearEntityLimits() {
entityLimits.clear();
setChanged();
}
/**
* @return the entityGroupLimits
*/
public Map<String, Integer> getEntityGroupLimits() {
return entityGroupLimits;
entityLimits.clear();
setChanged();
}
/**
* @param entityGroupLimits the entityGroupLimits to set
* Get the block count for this material for this island
*
* @param m - material
* @return count
*/
public void setEntityGroupLimits(Map<String, Integer> entityGroupLimits) {
this.entityGroupLimits = entityGroupLimits;
setChanged();
public Integer getBlockCount(Material m) {
return getBlockCounts().getOrDefault(m, 0);
}
/**
* Set an island-specific entity group limit
* @param name - entity group
* @param limit - limit
* @return the blockCount
*/
public void setEntityGroupLimit(String name, int limit) {
entityGroupLimits.put(name, limit);
setChanged();
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 entityGroupLimits.getOrDefault(name, -1);
return getEntityGroupLimits().getOrDefault(name, -1);
}
/**
* Clear all island-specific entity group limits
* Get the offset for an entity group
*
* @param name - entity group
* @return offset
*/
public void clearEntityGroupLimits() {
entityGroupLimits.clear();
setChanged();
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;
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;
this.changed = changed;
}
/**
* Mark changed
* Set an island-specific entity group limit
*
* @param name - entity group
* @param limit - limit
*/
public void setChanged() {
this.changed = true;
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();
}
}

View File

@ -1,11 +1,11 @@
name: Limits
main: world.bentobox.limits.Limits
version: ${version}${build.number}
api-version: 1.17
api-version: 2.3.0
authors: tastybento
softdepend: AcidIsland, BSkyBlock, CaveBlock
softdepend: AcidIsland, BSkyBlock, CaveBlock, AOneBlock, SkyGrid
permissions:
'[gamemode].limits.player.limits':
@ -17,3 +17,6 @@ permissions:
'[gamemode].limits.admin.limits':
description: Player can use admin limits command
default: op
'[gamemode].mod.bypass':
description: Player can bypass limits
default: op

View File

@ -4,6 +4,10 @@ gamemodes:
- BSkyBlock
- 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
# Island owners can be given permissions that override all general settings
# Format is GAME-MODE-NAME.island.limit.MATERIAL.LIMIT

View File

@ -19,8 +19,33 @@ admin:
calc:
parameters: "<player>"
description: "recalculate the island limits for player"
finished: "&aIsland recalc finished successfully!"
finished: "&a Island recalc finished successfully!"
offset:
unknown: "&c Unknown material or entity [name]."
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:
limits:
description: "show your island limits"
@ -30,4 +55,7 @@ island:
no-limits: "&cNo limits set in this world"
recount:
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?"

View 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?"

View 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?"

View File

@ -1,27 +1,54 @@
#
# 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] limitowany do [number]!'
hit-limit: "&c[material] limitowany do [number]!"
entity-limits:
hit-limit: '&cSpawnowanie [entity] limitowane do [number]!'
hit-limit: "&cSpawnowanie [entity] limitowane do [number]!"
limits:
panel-title: Limity wysp
admin:
limits:
main:
parameters: <gracz>
parameters: "<gracz>"
description: pokazuje limity wysp gracza
calc:
parameters: <gracz>
parameters: "<gracz>"
description: ponownie oblicza limity wyspy dla gracza
finished: '&aPrzeliczanie zakończone!'
finished: "&aPrzeliczanie zakończone!"
offset:
unknown: "&c Nieznany materiał lub podmiot [name]."
main:
description: pozwala zarządzać limitów dla materiałów i podmiotów,
set:
parameters: "<gracz> <materiał|istota> <liczba>"
description: ustawia nową wartość dla limitu materiału lub encji
success: "&a Przesunięcie limitu dla [name] jest ustawione na [number]."
same: "&c Limit dla [name] jest aktualnie [number]."
add:
parameters: "<gracz> <materiał|istota> <liczba>"
description: dodaje przesunięcie dla limitu materiału lub potworów
success: "&a Przesunięcie limitu dla [name] jest zwiększone do [number]."
remove:
parameters: "<gracz> <materiał|istota> <liczba>"
description: zmniejsza offset dla limitu materiału lub podmiotu
success: "&a Limit dla bloku [name] \nzostał zmniejszony do [number]."
reset:
parameters: "<gracz> <materiał|istota>"
description: usuwa wartość dla materiału lub istoty
success: "&a Limit dla [name] jest ustawione na 0."
view:
parameters: "<gracz> <materiał|istota>"
description: displays offset for material or entity
message: "&a [name] wartość jest ustawiona na [number]."
island:
limits:
description: pokazuje limity twojej wyspy
max-color: '&c'
regular-color: '&a'
block-limit-syntax: '[number]/[limit]'
no-limits: '&cBrak ustawionych limitów.'
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?"

View File

@ -30,3 +30,6 @@ island:
no-limits: "&c此世界中无限制"
recount:
description: "重新计数岛屿限制"
now-recounting: "&b 开始重新计算. 可能需要一定的时间, 请稍等......"
in-progress: "&c 重新计算岛屿限制中. 请稍等......"
time-out: "&c 重新计算超时. 岛屿太大了吗?"

View 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}

View File

@ -1,7 +1,5 @@
package bentobox.addon.limits.listeners;
import java.util.ArrayList;
import java.util.Arrays;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@ -11,8 +9,10 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@ -51,7 +51,7 @@ import world.bentobox.limits.objects.IslandBlockCount;
*
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest( {Bukkit.class} )
@PrepareForTest({ Bukkit.class })
public class JoinListenerTest {
@Mock
@ -79,74 +79,78 @@ public class JoinListenerTest {
@Before
public void setUp() {
jl = new JoinListener(addon);
// Setup addon
when(addon.getGameModes()).thenReturn(Collections.singletonList(bskyblock));
when(addon.getGameModeName(any())).thenReturn("bskyblock");
when(addon.getGameModePermPrefix(any())).thenReturn("bskyblock.");
when(addon.getSettings()).thenReturn(settings);
// Settings
when(settings.getGroupLimitDefinitions()).thenReturn(new ArrayList<>(Arrays.asList(new Settings.EntityGroup("friendly", new HashSet<>(), -1))));
// Island Manager
when(im.hasIsland(any(), any(UUID.class))).thenReturn(true);
when(island.getUniqueId()).thenReturn("unique_id");
when(im.getIsland(any(), any(UUID.class))).thenReturn(island);
// Default is that player has island
when(addon.getIslands()).thenReturn(im);
// Player
when(player.getUniqueId()).thenReturn(UUID.randomUUID());
when(player.getName()).thenReturn("tastybento");
// No permissions by default
when(player.getEffectivePermissions()).thenReturn(Collections.emptySet());
// bsKyBlock
when(bskyblock.getPermissionPrefix()).thenReturn("bskyblock.");
AddonDescription desc = new AddonDescription.Builder("main", "BSkyBlock", "1.0").build();
when(bskyblock.getDescription()).thenReturn(desc);
jl = new JoinListener(addon);
// Setup addon
when(addon.getGameModes()).thenReturn(Collections.singletonList(bskyblock));
when(addon.getGameModeName(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
when(island.getUniqueId()).thenReturn("unique_id");
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
when(addon.getIslands()).thenReturn(im);
// Player
when(player.getUniqueId()).thenReturn(UUID.randomUUID());
when(player.getName()).thenReturn("tastybento");
// No permissions by default
when(player.getEffectivePermissions()).thenReturn(Collections.emptySet());
// bsKyBlock
when(bskyblock.getPermissionPrefix()).thenReturn("bskyblock.");
AddonDescription desc = new AddonDescription.Builder("main", "BSkyBlock", "1.0").build();
when(bskyblock.getDescription()).thenReturn(desc);
// Block limit listener
when(addon.getBlockLimitListener()).thenReturn(bll);
when(bll.getIsland(anyString())).thenReturn(ibc);
// Block limit listener
when(addon.getBlockLimitListener()).thenReturn(bll);
when(bll.getIsland(anyString())).thenReturn(ibc);
// bukkit
PowerMockito.mockStatic(Bukkit.class);
// default is that owner is online
when(owner.isOnline()).thenReturn(true);
when(owner.getPlayer()).thenReturn(player);
when(Bukkit.getOfflinePlayer(any(UUID.class))).thenReturn(owner);
when(Bukkit.getPluginManager()).thenReturn(pim);
// bukkit
PowerMockito.mockStatic(Bukkit.class);
// default is that owner is online
when(owner.isOnline()).thenReturn(true);
when(owner.getPlayer()).thenReturn(player);
when(Bukkit.getOfflinePlayer(any(UUID.class))).thenReturn(owner);
when(Bukkit.getPluginManager()).thenReturn(pim);
// Island
when(island.getOwner()).thenReturn(UUID.randomUUID());
// Island
when(island.getOwner()).thenReturn(UUID.randomUUID());
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onNewIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onNewIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
*/
@Test
public void testOnNewIslandWrongReason() {
IslandEvent e = new IslandEvent(island, null, false, null, IslandEvent.Reason.BAN);
jl.onNewIsland(e);
verify(island, never()).getWorld();
IslandEvent e = new IslandEvent(island, null, false, null, IslandEvent.Reason.BAN);
jl.onNewIsland(e);
verify(island, never()).getWorld();
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onNewIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onNewIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
*/
@Test
public void testOnNewIslandRegistered() {
IslandEvent e = new IslandEvent(island, null, false, null, IslandEvent.Reason.REGISTERED);
jl.onNewIsland(e);
verify(island).getWorld();
IslandEvent e = new IslandEvent(island, null, false, null, IslandEvent.Reason.REGISTERED);
jl.onNewIsland(e);
verify(island).getWorld();
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onNewIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onNewIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
*/
@Test
public void testOnNewIslandResetted() {
IslandEvent e = new IslandEvent(island, null, false, null, IslandEvent.Reason.RESETTED);
jl.onNewIsland(e);
verify(island).getWorld();
IslandEvent e = new IslandEvent(island, null, false, null, IslandEvent.Reason.RESETTED);
jl.onNewIsland(e);
verify(island).getWorld();
}
/**
@ -191,213 +195,228 @@ public class JoinListenerTest {
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onOwnerChange(world.bentobox.bentobox.api.events.team.TeamEvent.TeamSetownerEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onOwnerChange(world.bentobox.bentobox.api.events.team.TeamEvent.TeamSetownerEvent)}.
*/
@Test
public void testOnOwnerChange() {
TeamSetownerEvent e = mock(TeamSetownerEvent.class);
when(e.getIsland()).thenReturn(island);
when(e.getNewOwner()).thenReturn(UUID.randomUUID());
jl.onOwnerChange(e);
verify(e, Mockito.times(2)).getIsland();
verify(e).getNewOwner();
TeamSetownerEvent e = mock(TeamSetownerEvent.class);
when(e.getIsland()).thenReturn(island);
when(e.getNewOwner()).thenReturn(UUID.randomUUID());
jl.onOwnerChange(e);
verify(e, Mockito.times(2)).getIsland();
verify(e).getNewOwner();
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
*/
@Test
public void testOnPlayerJoin() {
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon).getGameModes();
verify(bll).setIsland("unique_id", ibc);
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon).getGameModes();
verify(bll).setIsland("unique_id", ibc);
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
*/
@Test
public void testOnPlayerJoinIBCNull() {
ibc = null;
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon).getGameModes();
verify(bll, never()).setIsland("unique_id", ibc);
ibc = null;
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon).getGameModes();
verify(bll, never()).setIsland("unique_id", ibc);
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
*/
@Test
public void testOnPlayerJoinWithPermNotLimits() {
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.my.perm.for.game");
perms.add(permAtt);
when(player.getEffectivePermissions()).thenReturn(perms);
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon).getGameModes();
verify(bll).setIsland("unique_id", ibc);
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.my.perm.for.game");
perms.add(permAtt);
when(player.getEffectivePermissions()).thenReturn(perms);
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon).getGameModes();
verify(bll).setIsland("unique_id", ibc);
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
*/
@Test
public void testOnPlayerJoinWithPermLimitsWrongSize() {
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.my.perm.for.game");
when(permAtt.getValue()).thenReturn(true);
perms.add(permAtt);
when(player.getEffectivePermissions()).thenReturn(perms);
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
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', 'bskyblock.island.limit.ENTITY-TYPE.NUMBER', or 'bskyblock.island.limit.ENTITY-GROUP.NUMBER' Ignoring...");
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.my.perm.for.game");
when(permAtt.getValue()).thenReturn(true);
perms.add(permAtt);
when(player.getEffectivePermissions()).thenReturn(perms);
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
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', 'bskyblock.island.limit.ENTITY-TYPE.NUMBER', or 'bskyblock.island.limit.ENTITY-GROUP.NUMBER' Ignoring...");
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
*/
@Test
public void testOnPlayerJoinWithPermLimitsInvalidMaterial() {
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.mumbo.34");
when(permAtt.getValue()).thenReturn(true);
perms.add(permAtt);
when(player.getEffectivePermissions()).thenReturn(perms);
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon).logError("Player tastybento has permission: 'bskyblock.island.limit.mumbo.34' but MUMBO is not a valid material or entity type/group. Ignoring...");
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.mumbo.34");
when(permAtt.getValue()).thenReturn(true);
perms.add(permAtt);
when(player.getEffectivePermissions()).thenReturn(perms);
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon).logError(
"Player tastybento has permission: 'bskyblock.island.limit.mumbo.34' but MUMBO is not a valid material or entity type/group. Ignoring...");
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
*/
@Test
public void testOnPlayerJoinWithPermLimitsWildcard() {
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.*");
when(permAtt.getValue()).thenReturn(true);
perms.add(permAtt);
when(player.getEffectivePermissions()).thenReturn(perms);
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon).logError("Player tastybento has permission: 'bskyblock.island.limit.*' but wildcards are not allowed. Ignoring...");
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.*");
when(permAtt.getValue()).thenReturn(true);
perms.add(permAtt);
when(player.getEffectivePermissions()).thenReturn(perms);
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon).logError(
"Player tastybento has permission: 'bskyblock.island.limit.*' but wildcards are not allowed. Ignoring...");
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
*/
@Test
public void testOnPlayerJoinWithPermLimitsNotNumber() {
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.abc");
when(permAtt.getValue()).thenReturn(true);
perms.add(permAtt);
when(player.getEffectivePermissions()).thenReturn(perms);
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon).logError("Player tastybento has permission: 'bskyblock.island.limit.STONE.abc' but the last part MUST be a number! Ignoring...");
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.abc");
when(permAtt.getValue()).thenReturn(true);
perms.add(permAtt);
when(player.getEffectivePermissions()).thenReturn(perms);
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon).logError(
"Player tastybento has permission: 'bskyblock.island.limit.STONE.abc' but the last part MUST be an integer! Ignoring...");
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
*/
@Test
public void testOnPlayerJoinWithPermLimitsSuccess() {
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.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).setBlockLimit(eq(Material.STONE), eq(24));
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.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).setBlockLimit(eq(Material.STONE), eq(24));
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
*/
@Test
public void 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));
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 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));
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 method for
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
*/
@Test
public void testOnPlayerJoinWithPermLimitsMultiPerms() {
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.24");
when(permAtt.getValue()).thenReturn(true);
perms.add(permAtt);
PermissionAttachmentInfo permAtt2 = mock(PermissionAttachmentInfo.class);
when(permAtt2.getPermission()).thenReturn("bskyblock.island.limit.grass.14");
when(permAtt2.getValue()).thenReturn(true);
perms.add(permAtt2);
PermissionAttachmentInfo permAtt3 = mock(PermissionAttachmentInfo.class);
when(permAtt3.getPermission()).thenReturn("bskyblock.island.limit.dirt.34");
when(permAtt3.getValue()).thenReturn(true);
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);
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.24");
when(permAtt.getValue()).thenReturn(true);
perms.add(permAtt);
PermissionAttachmentInfo permAtt2 = mock(PermissionAttachmentInfo.class);
when(permAtt2.getPermission()).thenReturn("bskyblock.island.limit.short_grass.14");
when(permAtt2.getValue()).thenReturn(true);
perms.add(permAtt2);
PermissionAttachmentInfo permAtt3 = mock(PermissionAttachmentInfo.class);
when(permAtt3.getPermission()).thenReturn("bskyblock.island.limit.dirt.34");
when(permAtt3.getValue()).thenReturn(true);
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);
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon, never()).logError(anyString());
verify(ibc).setBlockLimit(eq(Material.STONE), eq(24));
verify(ibc).setBlockLimit(eq(Material.GRASS), eq(14));
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));
when(player.getEffectivePermissions()).thenReturn(perms);
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon, never()).logError(anyString());
verify(ibc).setBlockLimit(eq(Material.STONE), eq(24));
verify(ibc).setBlockLimit(eq(Material.SHORT_GRASS), eq(14));
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));
}
/**
@ -461,40 +480,43 @@ public class JoinListenerTest {
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onUnregisterIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onUnregisterIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
*/
@Test
public void testOnUnregisterIslandNotUnregistered() {
IslandEvent e = new IslandEvent(island, null, false, null, IslandEvent.Reason.BAN);
jl.onUnregisterIsland(e);
verify(island, never()).getWorld();
IslandEvent e = new IslandEvent(island, null, false, null, IslandEvent.Reason.BAN);
jl.onUnregisterIsland(e);
verify(island, never()).getWorld();
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onUnregisterIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onUnregisterIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
*/
@Test
public void testOnUnregisterIslandNotInWorld() {
IslandEvent e = new IslandEvent(island, null, false, null, IslandEvent.Reason.UNREGISTERED);
jl.onUnregisterIsland(e);
verify(island).getWorld();
verify(addon, never()).getBlockLimitListener();
IslandEvent e = new IslandEvent(island, null, false, null, IslandEvent.Reason.UNREGISTERED);
jl.onUnregisterIsland(e);
verify(island).getWorld();
verify(addon, never()).getBlockLimitListener();
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onUnregisterIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onUnregisterIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.
*/
@Test
public void testOnUnregisterIslandInWorld() {
@SuppressWarnings("unchecked")
Map<Material, Integer> map = mock(Map.class);
when(ibc.getBlockLimits()).thenReturn(map);
when(addon.inGameModeWorld(any())).thenReturn(true);
IslandEvent e = new IslandEvent(island, null, false, null, IslandEvent.Reason.UNREGISTERED);
jl.onUnregisterIsland(e);
verify(island).getWorld();
verify(addon).getBlockLimitListener();
verify(map).clear();
@SuppressWarnings("unchecked")
Map<Material, Integer> map = mock(Map.class);
when(ibc.getBlockLimits()).thenReturn(map);
when(addon.inGameModeWorld(any())).thenReturn(true);
IslandEvent e = new IslandEvent(island, null, false, null, IslandEvent.Reason.UNREGISTERED);
jl.onUnregisterIsland(e);
verify(island).getWorld();
verify(addon).getBlockLimitListener();
verify(map).clear();
}
@ -516,6 +538,4 @@ public class JoinListenerTest {
}
}

View File

@ -1,4 +1,4 @@
package world.bentobox.limits.commands;
package world.bentobox.limits.commands.player;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@ -55,7 +55,7 @@ public class LimitTabTest {
private Settings settings;
@Before
public void setUp() throws Exception {
public void setUp() {
// Island
when(island.getWorld()).thenReturn(world);
// Addon
@ -74,11 +74,11 @@ public class LimitTabTest {
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);
lp = new LimitTab(addon, new IslandBlockCount("", ""), Collections.emptyMap(), island, world, null, LimitTab.SORT_BY.A2Z);
}
@After
public void tearDown() throws Exception {
public void tearDown() {
}
@Test

View File

@ -19,6 +19,7 @@ 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;
@ -45,7 +46,6 @@ public class EntityLimitListenerTest {
private LivingEntity ent;
@Mock
private BlockLimitsListener bll;
private Settings settings;
@Mock
private World world;
private List<Entity> collection;
@ -63,7 +63,7 @@ public class EntityLimitListenerTest {
when(island.getUniqueId()).thenReturn(UUID.randomUUID().toString());
when(island.inIslandSpace(any(Location.class))).thenReturn(true);
ibc = new IslandBlockCount();
ibc = new IslandBlockCount("","");
when(bll.getIsland(anyString())).thenReturn(ibc);
when(addon.getBlockLimitListener()).thenReturn(bll);
@ -71,7 +71,7 @@ public class EntityLimitListenerTest {
config.load("src/main/resources/config.yml");
// Settings
when(addon.getConfig()).thenReturn(config);
settings = new Settings(addon);
Settings settings = new Settings(addon);
when(addon.getSettings()).thenReturn(settings);
// World
@ -81,15 +81,11 @@ public class EntityLimitListenerTest {
collection.add(ent);
collection.add(ent);
collection.add(ent);
when(world.getEntities()).thenReturn(collection);
when(world.getNearbyEntities(any())).thenReturn(collection);
ell = new EntityLimitListener(addon);
}
@After
public void tearDown() throws Exception {
}
/**
* Test for {@link EntityLimitListener#atLimit(Island, Entity)}
*/