Compare commits

..

No commits in common. "develop" and "1.12.1" have entirely different histories.

42 changed files with 1082 additions and 3964 deletions

33
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,33 @@
---
name: Bug report
about: Create a report to help us improve
---
**Description**
A clear and concise description of what the bug is.
**Steps to reproduce the behavior:**
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Server Information:**
[Please complete the following information:]
- Database being used (YAML, JSON, MySQL, MongoDB): []
- OS: [e.g. iOS]
- Java Version: [e.g. Java 8]
- BentoBox version: [e.g. 1.7.2.21]
- Addons installed? [Do '/bentobox version' and copy/paste from the console]
- Other plugins? [Do '/plugins' and copy/paste from the console]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -1,38 +0,0 @@
name: Build
on:
push:
branches:
- develop
- master
pull_request:
types: [opened, synchronize, reopened]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'adopt'
java-version: 17
- name: Cache SonarCloud packages
uses: actions/cache@v3
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Maven packages
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
- name: Build and analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar

22
.travis.yml Normal file
View File

@ -0,0 +1,22 @@
language: java
sudo: false
addons:
sonarcloud:
organization: "bentobox-world"
jdk:
- openjdk8
- openjdk11
matrix:
allow_failures:
- jdk: openjdk11
script:
# the following command line builds the project, runs the tests with coverage and then execute the SonarCloud analysis
- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install sonar:sonar -Dsonar.projectKey=BentoBoxWorld_Limits
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.sonar/cache'

105
pom.xml
View File

@ -53,21 +53,18 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<java.version>1.8</java.version>
<!-- Non-minecraft related dependencies -->
<powermock.version>2.0.9</powermock.version>
<powermock.version>2.0.2</powermock.version>
<!-- More visible way how to change dependency versions -->
<spigot.version>1.20.4-R0.1-SNAPSHOT</spigot.version>
<bentobox.version>2.3.0-SNAPSHOT</bentobox.version>
<spigot.version>1.15.2-R0.1-SNAPSHOT</spigot.version>
<bentobox.version>1.12.0</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.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>
<build.version>1.12.1</build.version>
</properties>
<!-- Profiles will allow to automatically change build version. -->
@ -110,6 +107,30 @@
<build.number></build.number>
</properties>
</profile>
<profile>
<id>sonar</id>
<properties>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<sonar.organization>bentobox-world</sonar.organization>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.6.0.1398</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sonar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<repositories>
@ -139,7 +160,7 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.11.1</version>
<version>3.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
@ -196,42 +217,19 @@
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<argLine>
${argLine}
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens
java.base/java.util.stream=ALL-UNNAMED
--add-opens java.base/java.text=ALL-UNNAMED
--add-opens
java.base/java.util.regex=ALL-UNNAMED
--add-opens
java.base/java.nio.channels.spi=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens
java.base/java.util.concurrent=ALL-UNNAMED
--add-opens java.base/sun.nio.fs=ALL-UNNAMED
--add-opens java.base/sun.nio.cs=ALL-UNNAMED
--add-opens java.base/java.nio.file=ALL-UNNAMED
--add-opens
java.base/java.nio.charset=ALL-UNNAMED
--add-opens
java.base/java.lang.reflect=ALL-UNNAMED
--add-opens
java.logging/java.util.logging=ALL-UNNAMED
--add-opens java.base/java.lang.ref=ALL-UNNAMED
--add-opens java.base/java.util.jar=ALL-UNNAMED
--add-opens java.base/java.util.zip=ALL-UNNAMED
</argLine>
</configuration>
<version>2.22.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -251,45 +249,30 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.10</version>
<version>0.8.3</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>prepare-agent</id>
<id>pre-unit-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<id>post-unit-test</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

@ -1,24 +1,17 @@
package world.bentobox.limits;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.limits.commands.admin.AdminCommand;
import world.bentobox.limits.commands.player.PlayerCommand;
import world.bentobox.limits.commands.AdminCommand;
import world.bentobox.limits.commands.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;
/**
@ -28,11 +21,9 @@ import world.bentobox.limits.objects.IslandBlockCount;
*/
public class Limits extends Addon {
private static final String LIMIT_NOT_SET = "Limit not set";
private Settings settings;
private List<GameModeAddon> gameModes;
private BlockLimitsListener blockLimitListener;
private JoinListener joinListener;
@Override
public void onDisable(){
@ -56,15 +47,13 @@ public class Limits extends Addon {
// Register commands
gm.getAdminCommand().ifPresent(a -> new AdminCommand(this, a));
gm.getPlayerCommand().ifPresent(a -> new PlayerCommand(this, a));
registerPlaceholders(gm);
log("Limits will apply to " + gm.getDescription().getName());
}
);
// Register listener
blockLimitListener = new BlockLimitsListener(this);
registerListener(blockLimitListener);
joinListener = new JoinListener(this);
registerListener(joinListener);
registerListener(new JoinListener(this));
registerListener(new EntityLimitListener(this));
// Done
}
@ -127,136 +116,4 @@ public class Limits extends Addon {
return gameModes.stream().anyMatch(gm -> gm.getDescription().getName().equals(gameMode));
}
/**
* @return the joinListener
*/
public JoinListener getJoinListener() {
return joinListener;
}
private void registerPlaceholders(GameModeAddon gm) {
if (getPlugin().getPlaceholdersManager() == null) return;
Arrays.stream(Material.values())
.filter(Material::isBlock)
.forEach(m -> registerCountAndLimitPlaceholders(m, gm));
Arrays.stream(EntityType.values())
.forEach(e -> registerCountAndLimitPlaceholders(e, gm));
}
/**
* Registers placeholders for the count and limit of the material
* in the format of %Limits_(gamemode prefix)_island_(lowercase material name)_count%
* and %Limits_(gamemode prefix)_island_(lowercase material name)_limit%
*
* Example: registerCountAndLimitPlaceholders("HOPPER", gm);
* Placeholders:
* "Limits_bskyblock_island_hopper_count"
* "Limits_bskyblock_island_hopper_limit"
* "Limits_bskyblock_island_hopper_base_limit"
* "Limits_bskyblock_island_zombie_limit"
*
* @param m material
* @param gm game mode
*/
private void registerCountAndLimitPlaceholders(Material m, GameModeAddon gm) {
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
gm.getDescription().getName().toLowerCase() + "_island_" + m.toString().toLowerCase() + "_count",
user -> String.valueOf(getCount(user, m, gm)));
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
gm.getDescription().getName().toLowerCase() + "_island_" + m.toString().toLowerCase() + "_limit",
user -> getLimit(user, m, gm));
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
gm.getDescription().getName().toLowerCase() + "_island_" + m.toString().toLowerCase() + "_base_limit",
user -> getBaseLimit(user, m, gm));
}
private void registerCountAndLimitPlaceholders(EntityType e, GameModeAddon gm) {
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
gm.getDescription().getName().toLowerCase() + "_island_" + e.toString().toLowerCase() + "_limit",
user -> getLimit(user, e, gm));
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
gm.getDescription().getName().toLowerCase() + "_island_" + e.toString().toLowerCase() + "_base_limit",
user -> getBaseLimit(user, e, gm));
}
/**
* @param user - Used to identify the island the user belongs to
* @param m - The material we are trying to count on the island
* @param gm Game Mode Addon
* @return Number of blocks of the specified material on the given user's island
*/
private int getCount(@Nullable User user, Material m, GameModeAddon gm) {
Island is = gm.getIslands().getIsland(gm.getOverWorld(), user);
if (is == null) {
return 0;
}
@Nullable IslandBlockCount ibc = getBlockLimitListener().getIsland(is.getUniqueId());
if (ibc == null) {
return 0;
}
return ibc.getBlockCount(m);
}
/**
* @param user - Used to identify the island the user belongs to
* @param m - The material whose limit we are querying
* @param gm Game Mode Addon
* @return The limit of the specified material on the given user's island
*/
private String getLimit(@Nullable User user, Material m, GameModeAddon gm) {
Island is = gm.getIslands().getIsland(gm.getOverWorld(), user);
if (is == null) {
return LIMIT_NOT_SET;
}
int limit = this.getBlockLimitListener().
getMaterialLimits(is.getWorld(), is.getUniqueId()).
getOrDefault(m, -1);
return limit == -1 ? LIMIT_NOT_SET : String.valueOf(limit);
}
private String getBaseLimit(@Nullable User user, Material m, GameModeAddon gm) {
Island is = gm.getIslands().getIsland(gm.getOverWorld(), user);
if (is == null) {
return LIMIT_NOT_SET;
}
int limit = this.getBlockLimitListener().
getMaterialLimits(is.getWorld(), is.getUniqueId()).
getOrDefault(m, -1);
if (limit > 0) {
limit -= this.getBlockLimitListener().getIsland(is).getBlockLimitOffset(m);
}
return limit == -1 ? LIMIT_NOT_SET : String.valueOf(limit);
}
private String getLimit(@Nullable User user, EntityType e, GameModeAddon gm) {
Island is = gm.getIslands().getIsland(gm.getOverWorld(), user);
if (is == null) {
return LIMIT_NOT_SET;
}
int limit = this.getBlockLimitListener().getIsland(is).getEntityLimit(e);
if (limit < 0 && this.getSettings().getLimits().containsKey(e)) {
limit = this.getSettings().getLimits().get(e);
}
return limit == -1 ? LIMIT_NOT_SET : String.valueOf(limit);
}
private String getBaseLimit(@Nullable User user, EntityType e, GameModeAddon gm) {
Island is = gm.getIslands().getIsland(gm.getOverWorld(), user);
if (is == null || !this.getSettings().getLimits().containsKey(e)) {
return LIMIT_NOT_SET;
}
int limit = this.getSettings().getLimits().get(e);
return limit == -1 ? LIMIT_NOT_SET : String.valueOf(limit);
}
}

View File

@ -1,14 +0,0 @@
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,7 +1,10 @@
package world.bentobox.limits;
import java.util.*;
import java.util.stream.Collectors;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.EntityType;
@ -9,9 +12,7 @@ import org.bukkit.entity.EntityType;
public class Settings {
private final Map<EntityType, Integer> limits = new EnumMap<>(EntityType.class);
private final Map<EntityType, List<EntityGroup>> groupLimits = new EnumMap<>(EntityType.class);
private final List<String> gameModes;
private final boolean asyncGolums;
private static final List<EntityType> DISALLOWED = Arrays.asList(
EntityType.PRIMED_TNT,
EntityType.EVOKER_FANGS,
@ -58,43 +59,8 @@ public class Settings {
}
}
}
// Async Golums
asyncGolums = addon.getConfig().getBoolean("async-golums", true);
addon.log("Entity limits:");
limits.entrySet().stream().map(e -> "Limit " + e.getKey().toString() + " to " + e.getValue()).forEach(addon::log);
//group limits
el = addon.getConfig().getConfigurationSection("entitygrouplimits");
if (el != null) {
for (String name : el.getKeys(false)) {
int limit = el.getInt(name + ".limit");
Set<EntityType> entities = el.getStringList(name + ".entities").stream().map(s -> {
EntityType type = getType(s);
if (type != null) {
if (DISALLOWED.contains(type)) {
addon.logError("Entity type: " + s + " is not supported - skipping...");
} else {
return type;
}
} else {
addon.logError("Unknown entity type: " + s + " - skipping...");
}
return null;
}).filter(Objects::nonNull).collect(Collectors.toCollection(LinkedHashSet::new));
if (entities.isEmpty())
continue;
EntityGroup group = new EntityGroup(name, entities, limit);
entities.forEach(e -> {
List<EntityGroup> groups = groupLimits.getOrDefault(e, new ArrayList<>());
groups.add(group);
groupLimits.put(e, groups);
});
}
}
addon.log("Entity group limits:");
getGroupLimitDefinitions().stream().map(e -> "Limit " + e.getName() + " (" + e.getTypes().stream().map(Enum::name).collect(Collectors.joining(", ")) + ") to " + e.getLimit()).forEach(addon::log);
}
private EntityType getType(String key) {
@ -102,26 +68,12 @@ public class Settings {
}
/**
* @return the entity limits
* @return the limits
*/
public Map<EntityType, Integer> getLimits() {
return Collections.unmodifiableMap(limits);
}
/**
* @return the group limits
*/
public Map<EntityType, List<EntityGroup>> getGroupLimits() {
return groupLimits;
}
/**
* @return the group limit definitions
*/
public List<EntityGroup> getGroupLimitDefinitions() {
return groupLimits.values().stream().flatMap(Collection::stream).distinct().toList();
}
/**
* @return the gameModes
*/
@ -129,69 +81,4 @@ public class Settings {
return gameModes;
}
/**
* A named class representing a group of entities and their limits
*
*/
public static class EntityGroup {
private final String name;
private final Set<EntityType> types;
private final int limit;
public EntityGroup(String name, Set<EntityType> types, int limit) {
this.name = name;
this.types = types;
this.limit = limit;
}
public boolean contains(EntityType type) {
return types.contains(type);
}
public String getName() {
return name;
}
public Set<EntityType> getTypes() {
return types;
}
public int getLimit() {
return limit;
}
@Override
public int hashCode()
{
int hash = 7;
hash = 83 * hash + Objects.hashCode(this.name);
return hash;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final EntityGroup other = (EntityGroup) obj;
return Objects.equals(this.name, other.name);
}
@Override
public String toString()
{
return "EntityGroup{" + "name=" + name + ", types=" + types + ", limit=" + limit + '}';
}
}
/**
* @return the asyncGolums
*/
public boolean isAsyncGolums() {
return asyncGolums;
}
}

View File

@ -1,151 +0,0 @@
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

@ -1,359 +0,0 @@
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

@ -1,57 +0,0 @@
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

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

View File

@ -1,17 +1,14 @@
package world.bentobox.limits.commands.admin;
package world.bentobox.limits.commands;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.util.Util;
import world.bentobox.limits.Limits;
import world.bentobox.limits.commands.player.LimitPanel;
/**
* Admin command for limits
@ -29,9 +26,7 @@ public class AdminCommand extends CompositeCommand {
public AdminCommand(Limits addon, CompositeCommand parent) {
super(parent, "limits");
this.addon = addon;
new CalcCommand(this.addon, this);
new OffsetCommand(this.addon, this);
new CalcCommand(addon, this);
}
/* (non-Javadoc)
@ -58,7 +53,7 @@ public class AdminCommand extends CompositeCommand {
user.sendMessage("general.errors.unknown-player", args.get(0));
return true;
} else {
new LimitPanel(addon).showLimits((GameModeAddon)getAddon(), user, playerUUID);
new LimitPanel(addon).showLimits(getWorld(), user, playerUUID);
}
return true;
} else {

View File

@ -1,4 +1,4 @@
package world.bentobox.limits.commands.admin;
package world.bentobox.limits.commands;
import java.util.ArrayList;
import java.util.List;
@ -7,19 +7,16 @@ 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, tastybento
* @author YellowZaki
*/
public class CalcCommand extends CompositeCommand {
private final Limits addon;
private Island island;
/**
* Admin command
@ -52,26 +49,10 @@ 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
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");
}
}
});
calcLimits(playerUUID, user);
}
return true;
} else {
showHelp(this, user);
@ -79,7 +60,13 @@ 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,158 @@
package world.bentobox.limits.commands;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import com.google.common.collect.ImmutableMap;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
import world.bentobox.limits.Limits;
import world.bentobox.limits.objects.IslandBlockCount;
/**
* Shows a panel of the blocks that are limited and their status
* @author tastybento
*
*/
public class LimitPanel {
private final Limits addon;
// This maps the entity types to the icon that should be shown in the panel
// If the icon is null, then the entity type is not covered by the addon
private static final Map<EntityType, Material> E2M = ImmutableMap.<EntityType, Material>builder()
.put(EntityType.PIG_ZOMBIE, Material.ZOMBIE_PIGMAN_SPAWN_EGG)
.put(EntityType.MUSHROOM_COW, Material.MOOSHROOM_SPAWN_EGG)
.put(EntityType.SNOWMAN, Material.SNOW_BLOCK)
.put(EntityType.IRON_GOLEM, Material.IRON_BLOCK)
.put(EntityType.ILLUSIONER, Material.VILLAGER_SPAWN_EGG)
.put(EntityType.WITHER, Material.WITHER_SKELETON_SKULL)
.put(EntityType.BOAT, Material.OAK_BOAT)
.put(EntityType.ARMOR_STAND, Material.ARMOR_STAND)
.put(EntityType.ITEM_FRAME, Material.ITEM_FRAME)
.put(EntityType.PAINTING, Material.PAINTING)
// Minecarts
.put(EntityType.MINECART_TNT, Material.TNT_MINECART)
.put(EntityType.MINECART_CHEST, Material.CHEST_MINECART)
.put(EntityType.MINECART_COMMAND, Material.COMMAND_BLOCK_MINECART)
.put(EntityType.MINECART_FURNACE, Material.FURNACE_MINECART)
.put(EntityType.MINECART_HOPPER, Material.HOPPER_MINECART)
.put(EntityType.MINECART_MOB_SPAWNER, Material.MINECART)
.build();
// This is a map of blocks to Material
private static final Map<Material, Material> B2M;
static {
ImmutableMap.Builder<Material, Material> builder = ImmutableMap.<Material, Material>builder()
.put(Material.POTATOES, Material.POTATO)
.put(Material.CARROTS, Material.CARROT)
.put(Material.BEETROOTS, Material.BEETROOT)
.put(Material.REDSTONE_WIRE, Material.REDSTONE);
// Block to Material icons
Optional.ofNullable(Material.getMaterial("SWEET_BERRY_BUSH")).ifPresent(material -> builder.put(material, Objects.requireNonNull(Material.getMaterial("SWEET_BERRIES"))));
Optional.ofNullable(Material.getMaterial("BAMBOO_SAPLING")).ifPresent(material -> builder.put(material, Objects.requireNonNull(Material.getMaterial("BAMBOO"))));
B2M = builder.build();
}
/**
* @param addon - limit addon
*/
public LimitPanel(Limits addon) {
this.addon = addon;
}
public void showLimits(World world, User user, UUID target) {
PanelBuilder pb = new PanelBuilder().name(user.getTranslation(world, "limits.panel-title")).user(user);
// Get the island for the target
Island island = addon.getIslands().getIsland(world, target);
if (island == null) {
if (user.getUniqueId().equals(target)) {
user.sendMessage("general.errors.no-island");
} else {
user.sendMessage("general.errors.player-has-no-island");
}
return;
}
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(island.getUniqueId());
Map<Material, Integer> matLimits = addon.getBlockLimitListener().getMaterialLimits(world, island.getUniqueId());
if (matLimits.isEmpty() && addon.getSettings().getLimits().isEmpty()) {
user.sendMessage("island.limits.no-limits");
return;
}
// Material limits
for (Entry<Material, Integer> en : matLimits.entrySet()) {
PanelItemBuilder pib = new PanelItemBuilder();
pib.name(Util.prettifyText(en.getKey().toString()));
// Adjust icon
pib.icon(B2M.getOrDefault(en.getKey(), en.getKey()));
int count = ibc == null ? 0 : ibc.getBlockCount().getOrDefault(en.getKey(), 0);
String color = count >= en.getValue() ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color");
pib.description(color
+ user.getTranslation("island.limits.block-limit-syntax",
TextVariables.NUMBER, String.valueOf(count),
"[limit]", String.valueOf(en.getValue())));
pb.item(pib.build());
}
// Entity limits
Map<EntityType, Integer> map = new HashMap<>(addon.getSettings().getLimits());
// Merge in any permission-based limits
if (ibc != null) ibc.getEntityLimits().forEach(map::put);
map.forEach((k,v) -> {
PanelItemBuilder pib = new PanelItemBuilder();
pib.name(Util.prettifyText(k.toString()));
Material m;
try {
if (E2M.containsKey(k)) {
m = E2M.get(k);
} else if (k.isAlive()) {
m = Material.valueOf(k.toString() + "_SPAWN_EGG");
} else {
// Regular material
m = Material.valueOf(k.toString());
}
} catch (Exception e) {
m = Material.BARRIER;
}
pib.icon(m);
long count = getCount(island, k);
String color = count >= v ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color");
pib.description(color
+ user.getTranslation("island.limits.block-limit-syntax",
TextVariables.NUMBER, String.valueOf(count),
"[limit]", String.valueOf(v)));
pb.item(pib.build());
});
pb.build();
}
long getCount(Island island, EntityType ent) {
long count = island.getWorld().getEntities().stream()
.filter(e -> e.getType().equals(ent))
.filter(e -> island.inIslandSpace(e.getLocation())).count();
// Nether
if (addon.getPlugin().getIWM().isNetherIslands(island.getWorld()) && addon.getPlugin().getIWM().getNetherWorld(island.getWorld()) != null) {
count += addon.getPlugin().getIWM().getNetherWorld(island.getWorld()).getEntities().stream()
.filter(e -> e.getType().equals(ent))
.filter(e -> island.inIslandSpace(e.getLocation())).count();
}
// End
if (addon.getPlugin().getIWM().isEndIslands(island.getWorld()) && addon.getPlugin().getIWM().getEndWorld(island.getWorld()) != null) {
count += addon.getPlugin().getIWM().getEndWorld(island.getWorld()).getEntities().stream()
.filter(e -> e.getType().equals(ent))
.filter(e -> island.inIslandSpace(e.getLocation())).count();
}
return count;
}
}

View File

@ -0,0 +1,125 @@
package world.bentobox.limits.commands;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Material;
import org.bukkit.World;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Pair;
import world.bentobox.bentobox.util.Util;
import world.bentobox.limits.Limits;
import world.bentobox.limits.listeners.BlockLimitsListener;
import world.bentobox.limits.objects.IslandBlockCount;
/**
*
* @author YellowZaki, tastybento
*/
public class LimitsCalc {
private final Limits addon;
private final World world;
private final Island island;
private final BlockLimitsListener bll;
private IslandBlockCount ibc;
private final Map<Material, AtomicInteger> blockCount;
private final User sender;
private final Set<Pair<Integer, Integer>> chunksToScan;
private int count;
LimitsCalc(World world, BentoBox instance, UUID targetPlayer, Limits addon, User sender) {
this.addon = addon;
this.island = instance.getIslands().getIsland(world, targetPlayer);
this.bll = addon.getBlockLimitListener();
this.ibc = bll.getIsland(island.getUniqueId());
blockCount = new EnumMap<>(Material.class);
this.sender = sender;
this.world = world;
// Get chunks to scan
chunksToScan = getChunksToScan(island);
count = 0;
chunksToScan.forEach(c -> Util.getChunkAtAsync(world, c.x, c.z).thenAccept(ch -> {
ChunkSnapshot snapShot = ch.getChunkSnapshot();
Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> {
this.scanChunk(snapShot);
count++;
if (count == chunksToScan.size()) {
this.tidyUp();
}
});
}));
}
private void scanChunk(ChunkSnapshot chunk) {
for (int x = 0; x < 16; x++) {
// Check if the block coordinate is inside the protection zone and if not, don't count it
if (chunk.getX() * 16 + x < island.getMinProtectedX() || chunk.getX() * 16 + x >= island.getMinProtectedX() + island.getProtectionRange() * 2) {
continue;
}
for (int z = 0; z < 16; z++) {
// Check if the block coordinate is inside the protection zone and if not, don't count it
if (chunk.getZ() * 16 + z < island.getMinProtectedZ() || chunk.getZ() * 16 + z >= island.getMinProtectedZ() + island.getProtectionRange() * 2) {
continue;
}
for (int y = 0; y < Objects.requireNonNull(island.getCenter().getWorld()).getMaxHeight(); y++) {
Material blockData = chunk.getBlockType(x, y, z);
// Air is free
if (!blockData.equals(Material.AIR)) {
checkBlock(blockData);
}
}
}
}
}
private void checkBlock(Material md) {
md = bll.fixMaterial(md);
// md is limited
if (bll.getMaterialLimits(world, island.getUniqueId()).containsKey(md)) {
if (!blockCount.containsKey(md)) {
blockCount.put(md, new AtomicInteger(1));
} else {
blockCount.get(md).getAndIncrement();
}
}
}
private Set<Pair<Integer, Integer>> getChunksToScan(Island island) {
Set<Pair<Integer, Integer>> chunkSnapshot = new HashSet<>();
for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + island.getProtectionRange() * 2 + 16); x += 16) {
for (int z = island.getMinProtectedZ(); z < (island.getMinProtectedZ() + island.getProtectionRange() * 2 + 16); z += 16) {
Pair<Integer, Integer> pair = new Pair<>(world.getBlockAt(x, 0, z).getChunk().getX(), world.getBlockAt(x, 0, z).getChunk().getZ());
chunkSnapshot.add(pair);
}
}
return chunkSnapshot;
}
private void tidyUp() {
if (ibc == null) {
ibc = new IslandBlockCount();
}
ibc.setBlockCount(blockCount.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().get())));
bll.setIsland(island.getUniqueId(), ibc);
Bukkit.getScheduler().runTask(addon.getPlugin(), () -> sender.sendMessage("admin.limits.calc.finished"));
}
}

View File

@ -1,8 +1,7 @@
package world.bentobox.limits.commands.player;
package world.bentobox.limits.commands;
import java.util.List;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.limits.Limits;
@ -46,7 +45,7 @@ public class PlayerCommand extends CompositeCommand {
showHelp(this, user);
return false;
} else {
new LimitPanel(addon).showLimits((GameModeAddon)getAddon(), user, user.getUniqueId());
new LimitPanel(addon).showLimits(getWorld(), user, user.getUniqueId());
return true;
}
}

View File

@ -1,14 +1,10 @@
package world.bentobox.limits.commands.player;
package world.bentobox.limits.commands;
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;
/**
*
@ -17,7 +13,6 @@ import world.bentobox.limits.calculators.Pipeliner;
public class RecountCommand extends CompositeCommand {
private final Limits addon;
private @Nullable Island island;
/**
* Player command to do a recount. Has a cooldown
@ -49,8 +44,7 @@ public class RecountCommand extends CompositeCommand {
showHelp(this, user);
return false;
}
island = addon.getIslands().getIsland(getWorld(), user);
if (island == null) {
if (addon.getIslands().getIsland(getWorld(), user) == null) {
user.sendMessage("general.errors.no-island");
return false;
}
@ -60,17 +54,7 @@ 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));
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");
}
}
});
new LimitsCalc(getWorld(), getPlugin(), user.getUniqueId(), addon, user);
return true;
}

View File

@ -1,689 +0,0 @@
//
// 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,78 +0,0 @@
package world.bentobox.limits.commands.player;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.Player;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.panels.builders.TabbedPanelBuilder;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.limits.Limits;
import world.bentobox.limits.objects.IslandBlockCount;
/**
* Shows a panel of the blocks that are limited and their status
* @author tastybento
*
*/
public class LimitPanel {
private final Limits addon;
/**
* @param addon - limit addon
*/
public LimitPanel(Limits addon) {
this.addon = addon;
}
/**
* Show the limits panel
* @param gm - game mode
* @param user - user asking
* @param target - target uuid
*/
public void showLimits(GameModeAddon gm, User user, UUID target) {
// Get world
World world = gm.getOverWorld();
// Get the island for the target
Island island = addon.getIslands().getIsland(world, target);
if (island == null) {
if (user.getUniqueId().equals(target)) {
user.sendMessage("general.errors.no-island");
} else {
user.sendMessage("general.errors.player-has-no-island");
}
return;
}
// See if the target is online
Player targetPlayer = Bukkit.getPlayer(target);
if (targetPlayer != null) {
// Update perms
addon.getJoinListener().checkPerms(targetPlayer, gm.getPermissionPrefix() + "island.limit.", island.getUniqueId(), gm.getDescription().getName());
}
// Get the limits for this island
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(island.getUniqueId());
Map<Material, Integer> matLimits = addon.getBlockLimitListener().getMaterialLimits(world, island.getUniqueId());
if (matLimits.isEmpty() && addon.getSettings().getLimits().isEmpty()) {
user.sendMessage("island.limits.no-limits");
return;
}
new TabbedPanelBuilder()
.user(user)
.world(world)
.tab(0, new LimitTab(addon, ibc, matLimits, island, world, user, LimitTab.SORT_BY.A2Z))
.tab(1, new LimitTab(addon, ibc, matLimits, island, world, user, LimitTab.SORT_BY.Z2A))
.startingSlot(0)
.size(54)
.build().openPanel();
}
}

View File

@ -1,269 +0,0 @@
package world.bentobox.limits.commands.player;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.eclipse.jdt.annotation.Nullable;
import com.google.common.collect.ImmutableMap;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.Tab;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
import world.bentobox.limits.Limits;
import world.bentobox.limits.Settings.EntityGroup;
import world.bentobox.limits.objects.IslandBlockCount;
/**
* @author tastybento
*
*/
public class LimitTab implements Tab {
enum SORT_BY {
A2Z,
Z2A
}
// This maps the entity types to the icon that should be shown in the panel
// If the icon is null, then the entity type is not covered by the addon
private static final Map<EntityType, Material> E2M = ImmutableMap.<EntityType, Material>builder()
.put(EntityType.MUSHROOM_COW, Material.MOOSHROOM_SPAWN_EGG)
.put(EntityType.SNOWMAN, Material.SNOW_BLOCK)
.put(EntityType.IRON_GOLEM, Material.IRON_BLOCK)
.put(EntityType.ILLUSIONER, Material.VILLAGER_SPAWN_EGG)
.put(EntityType.WITHER, Material.WITHER_SKELETON_SKULL)
.put(EntityType.BOAT, Material.OAK_BOAT)
.put(EntityType.ARMOR_STAND, Material.ARMOR_STAND)
.put(EntityType.ITEM_FRAME, Material.ITEM_FRAME)
.put(EntityType.PAINTING, Material.PAINTING)
// Minecarts
.put(EntityType.MINECART_TNT, Material.TNT_MINECART)
.put(EntityType.MINECART_CHEST, Material.CHEST_MINECART)
.put(EntityType.MINECART_COMMAND, Material.COMMAND_BLOCK_MINECART)
.put(EntityType.MINECART_FURNACE, Material.FURNACE_MINECART)
.put(EntityType.MINECART_HOPPER, Material.HOPPER_MINECART)
.put(EntityType.MINECART_MOB_SPAWNER, Material.MINECART)
.build();
// This is a map of blocks to Material
private static final Map<Material, Material> B2M;
static {
ImmutableMap.Builder<Material, Material> builder = ImmutableMap.<Material, Material>builder()
.put(Material.POTATOES, Material.POTATO)
.put(Material.CARROTS, Material.CARROT)
.put(Material.BEETROOTS, Material.BEETROOT)
.put(Material.REDSTONE_WIRE, Material.REDSTONE);
// Block to Material icons
Optional.ofNullable(Material.getMaterial("SWEET_BERRY_BUSH")).ifPresent(material -> builder.put(material, Objects.requireNonNull(Material.getMaterial("SWEET_BERRIES"))));
Optional.ofNullable(Material.getMaterial("BAMBOO_SAPLING")).ifPresent(material -> builder.put(material, Objects.requireNonNull(Material.getMaterial("BAMBOO"))));
B2M = builder.build();
}
private final World world;
private final User user;
private final Limits addon;
private final List<@Nullable PanelItem> result;
private final SORT_BY sortBy;
public LimitTab(Limits addon, IslandBlockCount ibc, Map<Material, Integer> matLimits, Island island, World world, User user, SORT_BY sortBy) {
this.addon = addon;
this.world = world;
this.user = user;
this.sortBy = sortBy;
result = new ArrayList<>();
addMaterialIcons(ibc, matLimits);
addEntityLimits(ibc, island);
addEntityGroupLimits(ibc, island);
// Sort
if (sortBy == SORT_BY.Z2A) {
result.sort((o1, o2) -> o2.getName().compareTo(o1.getName()));
} else {
result.sort(Comparator.comparing(PanelItem::getName));
}
}
private void addEntityGroupLimits(IslandBlockCount ibc, Island island) {
// Entity group limits
Map<EntityGroup, Integer> groupMap = addon.getSettings().getGroupLimitDefinitions().stream().collect(Collectors.toMap(e -> e, EntityGroup::getLimit));
// Group by same loop up map
Map<String, EntityGroup> groupByName = groupMap.keySet().stream().collect(Collectors.toMap(EntityGroup::getName, e -> e));
// Merge in any permission-based limits
if (ibc == null) {
return;
}
ibc.getEntityGroupLimits().entrySet().stream()
.filter(e -> groupByName.containsKey(e.getKey()))
.forEach(e -> groupMap.put(groupByName.get(e.getKey()), e.getValue()));
// Update the group map for each group limit offset. If the value already exists add it
ibc.getEntityGroupLimitsOffset().forEach((key, value) -> {
if (groupByName.get(key) != null) {
groupMap.put(groupByName.get(key), (groupMap.getOrDefault(groupByName.get(key), 0) + value));
}
});
groupMap.forEach((v, limit) -> {
PanelItemBuilder pib = new PanelItemBuilder();
EntityType k = v.getTypes().iterator().next();
pib.name(v.getName());
String description = "";
description += "(" + prettyNames(v) + ")\n";
Material m;
try {
if (E2M.containsKey(k)) {
m = E2M.get(k);
} else if (k.isAlive()) {
m = Material.valueOf(k + "_SPAWN_EGG");
} else {
// Regular material
m = Material.valueOf(k.toString());
}
} catch (Exception e) {
m = Material.BARRIER;
}
pib.icon(m);
long count = getCount(island, v);
String color = count >= limit ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color");
description += color
+ user.getTranslation("island.limits.block-limit-syntax",
TextVariables.NUMBER, String.valueOf(count),
"[limit]", String.valueOf(limit));
pib.description(description);
result.add(pib.build());
});
}
private void addEntityLimits(IslandBlockCount ibc, Island island) {
// Entity limits
Map<EntityType, Integer> map = new HashMap<>(addon.getSettings().getLimits());
// Merge in any permission-based limits
if (ibc != null) {
map.putAll(ibc.getEntityLimits());
ibc.getEntityLimitsOffset().forEach((k,v) -> map.put(k, map.getOrDefault(k, 0) + v));
}
map.forEach((k,v) -> {
PanelItemBuilder pib = new PanelItemBuilder();
pib.name(Util.prettifyText(k.toString()));
Material m;
try {
if (E2M.containsKey(k)) {
m = E2M.get(k);
} else if (k.isAlive()) {
m = Material.valueOf(k + "_SPAWN_EGG");
} else {
// Regular material
m = Material.valueOf(k.toString());
}
} catch (Exception e) {
m = Material.BARRIER;
}
pib.icon(m);
long count = getCount(island, k);
String color = count >= v ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color");
pib.description(color
+ user.getTranslation("island.limits.block-limit-syntax",
TextVariables.NUMBER, String.valueOf(count),
"[limit]", String.valueOf(v)));
result.add(pib.build());
});
}
private void addMaterialIcons(IslandBlockCount ibc, Map<Material, Integer> matLimits) {
// Material limits
for (Entry<Material, Integer> en : matLimits.entrySet()) {
PanelItemBuilder pib = new PanelItemBuilder();
pib.name(Util.prettifyText(en.getKey().toString()));
// Adjust icon
pib.icon(B2M.getOrDefault(en.getKey(), en.getKey()));
int count = ibc == null ? 0 : ibc.getBlockCounts().getOrDefault(en.getKey(), 0);
int value = en.getValue();
String color = count >= value ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color");
pib.description(color
+ user.getTranslation("island.limits.block-limit-syntax",
TextVariables.NUMBER, String.valueOf(count),
"[limit]", String.valueOf(value)));
result.add(pib.build());
}
}
@Override
public PanelItem getIcon() {
return new PanelItemBuilder().icon(Material.MAGENTA_GLAZED_TERRACOTTA).name(this.getName()).build();
}
@Override
public String getName() {
return user.getTranslation(world, "limits.panel-title") + " " + sortBy.name();
}
@Override
public List<@Nullable PanelItem> getPanelItems() {
return result;
}
@Override
public String getPermission() {
return "";
}
private String prettyNames(EntityGroup v) {
StringBuilder sb = new StringBuilder();
List<EntityType> l = new ArrayList<>(v.getTypes());
for(int i = 0; i < l.size(); i++)
{
sb.append(Util.prettifyText(l.get(i).toString()));
if (i + 1 < l.size())
sb.append(", ");
if((i+1) % 5 == 0)
sb.append("\n");
}
return sb.toString();
}
long getCount(Island island, EntityType ent) {
long count = island.getWorld().getEntities().stream()
.filter(e -> e.getType().equals(ent))
.filter(e -> island.inIslandSpace(e.getLocation())).count();
// Nether
if (addon.getPlugin().getIWM().isNetherIslands(island.getWorld()) && addon.getPlugin().getIWM().getNetherWorld(island.getWorld()) != null) {
count += addon.getPlugin().getIWM().getNetherWorld(island.getWorld()).getEntities().stream()
.filter(e -> e.getType().equals(ent))
.filter(e -> island.inIslandSpace(e.getLocation())).count();
}
// End
if (addon.getPlugin().getIWM().isEndIslands(island.getWorld()) && addon.getPlugin().getIWM().getEndWorld(island.getWorld()) != null) {
count += addon.getPlugin().getIWM().getEndWorld(island.getWorld()).getEntities().stream()
.filter(e -> e.getType().equals(ent))
.filter(e -> island.inIslandSpace(e.getLocation())).count();
}
return count;
}
long getCount(Island island, EntityGroup group) {
long count = island.getWorld().getEntities().stream()
.filter(e -> group.contains(e.getType()))
.filter(e -> island.inIslandSpace(e.getLocation())).count();
// Nether
if (addon.getPlugin().getIWM().isNetherIslands(island.getWorld()) && addon.getPlugin().getIWM().getNetherWorld(island.getWorld()) != null) {
count += addon.getPlugin().getIWM().getNetherWorld(island.getWorld()).getEntities().stream()
.filter(e -> group.contains(e.getType()))
.filter(e -> island.inIslandSpace(e.getLocation())).count();
}
// End
if (addon.getPlugin().getIWM().isEndIslands(island.getWorld()) && addon.getPlugin().getIWM().getEndWorld(island.getWorld()) != null) {
count += addon.getPlugin().getIWM().getEndWorld(island.getWorld()).getEntities().stream()
.filter(e -> group.contains(e.getType()))
.filter(e -> island.inIslandSpace(e.getLocation())).count();
}
return count;
}
}

View File

@ -1,121 +0,0 @@
package world.bentobox.limits.events;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.events.BentoBoxEvent;
import world.bentobox.limits.objects.IslandBlockCount;
/**
* Fired when a player joins the server and before limit settings for their island are changed based
* on the player's permissions. If cancelled, no limit settings will be made.
* @author tastybento
*
*/
public class LimitsJoinPermCheckEvent extends BentoBoxEvent implements Cancellable {
private final Player player;
private final String islandId;
private IslandBlockCount ibc;
private boolean cancel;
private boolean ignorePerms;
private static final HandlerList handlers = new HandlerList();
@Override
public @NonNull HandlerList getHandlers() {
return getHandlerList();
}
public static HandlerList getHandlerList() {
return handlers;
}
/**
* Fired when a player joins the server and before limit settings for their island are changed based
* on the player's permissions. If cancelled, no limit settings will be made.
* @param player - player joining
* @param islandId - the unique island id.
* @param ibc - IslandBlockCount object for this island
*/
public LimitsJoinPermCheckEvent(@NonNull Player player, @NonNull String islandId, @Nullable IslandBlockCount ibc) {
super();
this.player = player;
this.islandId = islandId;
this.ibc = ibc;
}
/**
* Get the player joining
* @return the player
*/
@NonNull
public Player getPlayer() {
return player;
}
/**
* Get the unique island id. Use the islands manager to obtain the island
* @return the islandId
*/
@NonNull
public String getIslandId() {
return islandId;
}
/**
* Get the island block count
* @return the ibc
*/
@Nullable
public IslandBlockCount getIbc() {
return ibc;
}
/**
* Set the island block count to a specific setting
* @param ibc the ibc to set
*/
public void setIbc(@Nullable IslandBlockCount ibc) {
this.ibc = ibc;
}
@Override
public boolean isCancelled() {
return cancel;
}
@Override
public void setCancelled(boolean cancel) {
this.cancel = cancel;
}
/**
* Check if player's perms should be considered or not
* @return the ignorePerms
*/
public boolean isIgnorePerms() {
return ignorePerms;
}
/**
* Ignore player's perms. This differs to canceling the event in that the IslandBlockCount will be used if given via
* {@link #setIbc(IslandBlockCount ibc)}
* @param ignorePerms the ignorePerms to set
*/
public void setIgnorePerms(boolean ignorePerms) {
this.ignorePerms = ignorePerms;
}
}

View File

@ -1,116 +0,0 @@
package world.bentobox.limits.events;
import org.bukkit.Material;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.limits.Settings.EntityGroup;
import world.bentobox.limits.objects.IslandBlockCount;
/**
* Fired when a player joins the server and for each perm-based limit setting.
* If cancelled, no limit settings will be made.
* Settings can be adjusted and will be used.
* @author tastybento
*
*/
public class LimitsPermCheckEvent extends LimitsJoinPermCheckEvent {
private @Nullable EntityGroup entityGroup;
private @Nullable EntityType entityType;
private @Nullable Material material;
private int value;
/**
* Fired when a player joins the server and for each perm-based limit setting.
* If cancelled, no limit settings will be made.
* Settings can be adjusted and will be used.
* @param player - player joining
* @param islandId - the unique island id.
* @param ibc - IslandBlockCount object for this island
* @param material - material being limited, or null
* @param entityType - entity type being limited, or null
* @param entgroup - entity group being limited, or null
* @param value - numeric limit given by the perm
*/
public LimitsPermCheckEvent(@NonNull Player player,
@NonNull String islandId,
@Nullable IslandBlockCount ibc,
@Nullable EntityGroup entgroup,
@Nullable EntityType entityType,
@Nullable Material material,
int value) {
super(player, islandId, ibc);
this.entityGroup = entgroup;
this.entityType = entityType;
this.material = material;
this.value = value;
}
/**
* @return the entityGroup
*/
public @Nullable EntityGroup getEntityGroup() {
return entityGroup;
}
/**
* @param entityGroup the entityGroup to set
*/
public void setEntityGroup(@Nullable EntityGroup entityGroup) {
this.entityGroup = entityGroup;
}
/**
* @return the entityType
*/
public @Nullable EntityType getEntityType() {
return entityType;
}
/**
* @param entityType the entityType to set
*/
public void setEntityType(@Nullable EntityType entityType) {
this.entityType = entityType;
}
/**
* @return the material
*/
public @Nullable Material getMaterial() {
return material;
}
/**
* @param material the material to set
*/
public void setMaterial(@Nullable Material material) {
this.material = material;
}
/**
* @return the value
*/
public int getValue() {
return value;
}
/**
* @param value the value to set
*/
public void setValue(int value) {
this.value = value;
}
}

View File

@ -1,32 +1,47 @@
package world.bentobox.limits.listeners;
import java.util.*;
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 org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.TechnicalPiston;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.Cancellable;
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.*;
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.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.events.island.IslandEvent.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;
@ -68,7 +83,7 @@ public class BlockLimitsListener implements Listener {
handler.loadObjects().forEach(ibc -> {
// Clean up
if (addon.isCoveredGameMode(ibc.getGameMode())) {
ibc.getBlockCounts().keySet().removeIf(DO_NOT_COUNT::contains);
ibc.getBlockCount().keySet().removeIf(DO_NOT_COUNT::contains);
// Store
islandCountMap.put(ibc.getUniqueId(), ibc);
} else {
@ -131,7 +146,7 @@ public class BlockLimitsListener implements Listener {
* Save the count database completely
*/
public void save() {
islandCountMap.values().stream().filter(IslandBlockCount::isChanged).forEach(handler::saveObjectAsync);
islandCountMap.values().forEach(handler::saveObject);
}
// Player-related events
@ -147,26 +162,29 @@ public class BlockLimitsListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onTurtleEggBreak(PlayerInteractEvent e) {
if (e.getAction().equals(Action.PHYSICAL) && e.getClickedBlock() != null && e.getClickedBlock().getType().equals(Material.TURTLE_EGG)) {
if (e.getAction().equals(Action.PHYSICAL) && e.getClickedBlock().getType().equals(Material.TURTLE_EGG)) {
handleBreak(e, e.getClickedBlock());
}
}
private void handleBreak(Event e, Block b) {
if (!addon.inGameModeWorld(b.getWorld())) {
return;
}
Material mat = b.getType();
// Special handling for crops that can break in different ways
if (mat.equals(Material.WHEAT_SEEDS)) {
mat = Material.WHEAT;
} else if (mat.equals(Material.BEETROOT_SEEDS)) {
mat = Material.BEETROOT;
}
// Check for stackable plants
if (STACKABLE.contains(b.getType())) {
// Check for blocks above
Block block = b;
while(block.getRelative(BlockFace.UP).getType().equals(mat) && block.getY() < b.getWorld().getMaxHeight()) {
block = block.getRelative(BlockFace.UP);
process(block, false);
process(block, false, mat);
}
}
process(b, false);
process(b, false, mat);
// Player breaks a block and there was a redstone dust/repeater/... above
if (b.getRelative(BlockFace.UP).getType() == Material.REDSTONE_WIRE || b.getRelative(BlockFace.UP).getType() == Material.REPEATER || b.getRelative(BlockFace.UP).getType() == Material.COMPARATOR || b.getRelative(BlockFace.UP).getType() == Material.REDSTONE_TORCH) {
process(b.getRelative(BlockFace.UP), false);
@ -190,13 +208,6 @@ 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",
@ -237,16 +248,6 @@ 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,37 +278,28 @@ public class BlockLimitsListener implements Listener {
}
}
/**
* 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();
private int process(Block b, boolean add) {
return process(b, add, b.getType());
}
if (mat == Material.REDSTONE_WALL_TORCH) {
// Return equivalents.
public Material fixMaterial(Material b) {
if (b == Material.REDSTONE_WALL_TORCH) {
return Material.REDSTONE_TORCH;
} else if (mat == Material.WALL_TORCH) {
} else if (b == Material.WALL_TORCH) {
return Material.TORCH;
} else if (mat == Material.ZOMBIE_WALL_HEAD) {
} else if (b == Material.ZOMBIE_WALL_HEAD) {
return Material.ZOMBIE_HEAD;
} else if (mat == Material.CREEPER_WALL_HEAD) {
} else if (b == Material.CREEPER_WALL_HEAD) {
return Material.CREEPER_HEAD;
} else if (mat == Material.PLAYER_WALL_HEAD) {
} else if (b == Material.PLAYER_WALL_HEAD) {
return Material.PLAYER_HEAD;
} else if (mat == Material.DRAGON_WALL_HEAD) {
} else if (b == Material.DRAGON_WALL_HEAD) {
return Material.DRAGON_HEAD;
} 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) {
return Material.PISTON;
} else {
return Material.STICKY_PISTON;
}
} else if (b != null && b == Material.getMaterial("BAMBOO_SAPLING")) {
return Material.getMaterial("BAMBOO");
}
return mat;
return b;
}
/**
@ -315,10 +307,11 @@ 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) {
if (DO_NOT_COUNT.contains(fixMaterial(b.getBlockData())) || !addon.inGameModeWorld(b.getWorld())) {
private int process(Block b, boolean add, Material changeTo) {
if (DO_NOT_COUNT.contains(fixMaterial(b.getType())) || !addon.inGameModeWorld(b.getWorld())) {
return -1;
}
// Check if on island
@ -329,54 +322,40 @@ 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));
saveMap.putIfAbsent(id, 0);
if (add) {
// Check limit
int limit = checkLimit(b.getWorld(), fixMaterial(b.getBlockData()), id);
int limit = checkLimit(b.getWorld(), fixMaterial(b.getType()), id);
if (limit > -1) {
return limit;
}
islandCountMap.get(id).add(fixMaterial(b.getBlockData()));
islandCountMap.get(id).add(fixMaterial(b.getType()));
saveMap.merge(id, 1, Integer::sum);
} else {
if (islandCountMap.containsKey(id)) {
islandCountMap.get(id).remove(fixMaterial(b.getBlockData()));
// Check for changes
Material fixed = fixMaterial(changeTo);
if (!fixed.equals(fixMaterial(b.getType())) && fixed.isBlock() && !DO_NOT_COUNT.contains(fixed)) {
// Check limit
int limit = checkLimit(b.getWorld(), fixed, id);
if (limit > -1) {
return limit;
}
islandCountMap.get(id).add(fixed);
}
islandCountMap.get(id).remove(fixMaterial(b.getType()));
saveMap.merge(id, 1, Integer::sum);
}
}
updateSaveMap(id);
if (saveMap.get(id) > CHANGE_LIMIT) {
handler.saveObject(islandCountMap.get(id));
saveMap.remove(id);
}
return -1;
}).orElse(-1);
}
/**
* Removed a block from any island limit count
* @param b - block to remove
*/
public void removeBlock(Block b) {
// Get island
addon.getIslands().getIslandAt(b.getLocation()).ifPresent(i -> {
String id = i.getUniqueId();
String gameMode = addon.getGameModeName(b.getWorld());
if (gameMode.isEmpty()) {
// Invalid world
return;
}
islandCountMap.computeIfAbsent(id, k -> new IslandBlockCount(id, gameMode)).remove(fixMaterial(b.getBlockData()));
updateSaveMap(id);
});
}
private void updateSaveMap(String id) {
saveMap.putIfAbsent(id, 0);
if (saveMap.merge(id, 1, Integer::sum) > CHANGE_LIMIT) {
handler.saveObjectAsync(islandCountMap.get(id));
saveMap.remove(id);
}
}
/**
* Check if this material is at its limit for world on this island
*
@ -387,18 +366,18 @@ public class BlockLimitsListener implements Listener {
*/
private int checkLimit(World w, Material m, String id) {
// Check island limits
IslandBlockCount ibc = islandCountMap.get(id);
if (ibc.isBlockLimited(m)) {
return ibc.isAtLimit(m) ? ibc.getBlockLimit(m) + ibc.getBlockLimitOffset(m) : -1;
IslandBlockCount island = islandCountMap.get(id);
if (island.isBlockLimited(m)) {
return island.isAtLimit(m) ? island.getBlockLimit(m) : -1;
}
// Check specific world limits
if (worldLimitMap.containsKey(w) && worldLimitMap.get(w).containsKey(m)) {
// Material is overridden in world
return ibc.isAtLimit(m, worldLimitMap.get(w).get(m)) ? worldLimitMap.get(w).get(m) + ibc.getBlockLimitOffset(m) : -1;
return island.isAtLimit(m, worldLimitMap.get(w).get(m)) ? worldLimitMap.get(w).get(m) : -1;
}
// Check default limit map
if (defaultLimitMap.containsKey(m) && ibc.isAtLimit(m, defaultLimitMap.get(m))) {
return defaultLimitMap.get(m) + ibc.getBlockLimitOffset(m);
if (defaultLimitMap.containsKey(m) && island.isAtLimit(m, defaultLimitMap.get(m))) {
return defaultLimitMap.get(m);
}
// No limit
return -1;
@ -415,19 +394,14 @@ public class BlockLimitsListener implements Listener {
// Merge limits
Map<Material, Integer> result = new EnumMap<>(Material.class);
// Default
result.putAll(defaultLimitMap);
defaultLimitMap.forEach(result::put);
// World
if (worldLimitMap.containsKey(w)) {
result.putAll(worldLimitMap.get(w));
worldLimitMap.get(w).forEach(result::put);
}
// Island
if (islandCountMap.containsKey(id)) {
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));
islandCountMap.get(id).getBlockLimits().forEach(result::put);
}
return result;
}
@ -454,7 +428,7 @@ public class BlockLimitsListener implements Listener {
*/
public void setIsland(String islandId, IslandBlockCount ibc) {
islandCountMap.put(islandId, ibc);
handler.saveObjectAsync(ibc);
handler.saveObject(ibc);
}
/**
@ -468,14 +442,4 @@ 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

@ -1,53 +1,28 @@
package world.bentobox.limits.listeners;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Breedable;
import org.bukkit.entity.Villager;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.event.entity.EntityBreedEvent;
import org.bukkit.event.hanging.HangingPlaceEvent;
import org.bukkit.event.vehicle.VehicleCreateEvent;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
import world.bentobox.limits.Limits;
import world.bentobox.limits.Settings;
import world.bentobox.limits.Settings.EntityGroup;
import world.bentobox.limits.objects.IslandBlockCount;
public class EntityLimitListener implements Listener {
private static final String MOD_BYPASS = "mod.bypass";
private final Limits addon;
private final List<UUID> justSpawned = new ArrayList<>();
private static final List<BlockFace> CARDINALS = List.of(BlockFace.UP, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST, BlockFace.DOWN);
/**
* Handles entity and natural limitations
@ -64,300 +39,119 @@ 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())) {
if (!addon.getPlugin().getIWM().inWorld(e.getVehicle().getWorld())) {
return;
}
// Debounce
if (justSpawned.contains(e.getVehicle().getUniqueId())) {
justSpawned.remove(e.getVehicle().getUniqueId());
return;
}
// Check island
addon.getIslands().getProtectedIslandAt(e.getVehicle().getLocation())
// Ignore spawn
.filter(i -> !i.isSpawn())
.ifPresent(island -> {
// Check if the player is at the limit
AtLimitResult res = atLimit(island, e.getVehicle());
if (res.hit()) {
e.setCancelled(true);
this.tellPlayers(e.getVehicle().getLocation(), e.getVehicle(), SpawnReason.MOUNT, res);
// 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
if (!bypass && atLimit(island, e.getVehicle())) {
e.setCancelled(true);
for (Entity ent : e.getVehicle().getLocation().getWorld().getNearbyEntities(e.getVehicle().getLocation(), 5, 5, 5)) {
if (ent instanceof Player) {
((Player) ent).updateInventory();
User.getInstance(ent).notify("entity-limits.hit-limit", "[entity]",
Util.prettifyText(e.getVehicle().getType().toString())
, TextVariables.NUMBER, String.valueOf(addon.getSettings().getLimits().get(e.getVehicle().getType())));
}
}
}
});
}
});
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onBreed(final EntityBreedEvent e) {
if (addon.inGameModeWorld(e.getEntity().getWorld())
&& e.getBreeder() != null
&& (e.getBreeder() instanceof Player p)
&& !(p.isOp() || p.hasPermission(addon.getPlugin().getIWM().getPermissionPrefix(e.getEntity().getWorld()) + MOD_BYPASS))
&& !checkLimit(e, e.getEntity(), SpawnReason.BREEDING, false)
&& e.getFather() instanceof Breedable f && e.getMother() instanceof Breedable m) {
f.setBreed(false);
m.setBreed(false);
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onCreatureSpawn(final CreatureSpawnEvent e) {
// Return if not in a known world
if (!addon.inGameModeWorld(e.getLocation().getWorld())) {
if (!addon.getPlugin().getIWM().inWorld(e.getLocation())) {
return;
}
if (justSpawned.contains(e.getEntity().getUniqueId())) {
justSpawned.remove(e.getEntity().getUniqueId());
return;
}
if (e.getSpawnReason().equals(SpawnReason.SHOULDER_ENTITY) || (!(e.getEntity() instanceof Villager ) && e.getSpawnReason().equals(SpawnReason.BREEDING))) {
// Special case - do nothing - jumping around spawns parrots as they drop off player's shoulder
// Ignore breeding because it's handled in the EntityBreedEvent listener
return;
}
// Some checks can be done async, some not
if (e.getSpawnReason().equals(SpawnReason.BUILD_SNOWMAN) || e.getSpawnReason().equals(SpawnReason.BUILD_IRONGOLEM)) {
checkLimit(e, e.getEntity(), e.getSpawnReason(), addon.getSettings().isAsyncGolums());
} else {
// Check limit sync
checkLimit(e, e.getEntity(), e.getSpawnReason(), false);
boolean bypass = false;
// Check why it was spawned
switch (e.getSpawnReason()) {
// These reasons are due to a player being involved (usually) so there may be a bypass
case BREEDING:
case BUILD_IRONGOLEM:
case BUILD_SNOWMAN:
case BUILD_WITHER:
case CURED:
case EGG:
case SPAWNER_EGG:
bypass = checkByPass(e.getLocation());
break;
default:
// Other natural reasons
break;
}
// Tag the entity with the island spawn location
checkLimit(e, bypass);
}
private boolean checkByPass(Location l) {
// If someone in that area has the bypass permission, allow the spawning
for (Entity entity : 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
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBlock(HangingPlaceEvent e) {
if (!addon.inGameModeWorld(e.getBlock().getWorld())) {
return;
}
Player player = e.getPlayer();
if (player == null) return;
addon.getIslands().getIslandAt(e.getEntity().getLocation()).ifPresent(island -> {
boolean bypass = Objects.requireNonNull(player).isOp() || player.hasPermission(addon.getPlugin().getIWM().getPermissionPrefix(e.getEntity().getWorld()) + MOD_BYPASS);
// Check if entity can be hung
AtLimitResult res;
if (!bypass && !island.isSpawn() && (res = atLimit(island, e.getEntity())).hit()) {
if (!bypass && !island.isSpawn() && atLimit(island, e.getEntity())) {
// Not allowed
e.setCancelled(true);
if (res.getTypelimit() != null) {
User.getInstance(player).notify("block-limits.hit-limit", "[material]",
Util.prettifyText(e.getEntity().getType().toString()),
TextVariables.NUMBER, String.valueOf(res.getTypelimit().getValue()));
} else {
User.getInstance(player).notify("block-limits.hit-limit", "[material]",
res.getGrouplimit().getKey().getName() + " (" + res.getGrouplimit().getKey().getTypes().stream().map(x -> Util.prettifyText(x.toString())).collect(Collectors.joining(", ")) + ")",
TextVariables.NUMBER, String.valueOf(res.getGrouplimit().getValue()));
}
User.getInstance(player).notify("block-limits.hit-limit", "[material]",
Util.prettifyText(e.getEntity().getType().toString()),
TextVariables.NUMBER, String.valueOf(addon.getSettings().getLimits().getOrDefault(e.getEntity().getType(), -1)));
}
});
}
/**
* Check if a creature is allowed to spawn or not
* @param e - CreatureSpawnEvent
* @param async - true if check can be done async, false if not
* @return true if allowed or asycn, false if not.
*/
private boolean checkLimit(Cancellable c, LivingEntity e, SpawnReason reason, boolean async) {
Location l = e.getLocation();
if (async) {
c.setCancelled(true);
}
return processIsland(c, e, l, reason, async);
}
private boolean processIsland(Cancellable c, LivingEntity e, Location l, SpawnReason reason, boolean async) {
if (addon.getIslands().getIslandAt(e.getLocation()).isEmpty()) {
return true;
}
Island island = addon.getIslands().getIslandAt(e.getLocation()).get();
// Check if creature is allowed to spawn or not
AtLimitResult res = atLimit(island, e);
if (island.isSpawn() || !res.hit()) {
// Allowed
if (async) {
Bukkit.getScheduler().runTask(BentoBox.getInstance(), () -> preSpawn(e.getType(), reason, l));
} // else do nothing
} else {
if (async) {
e.remove();
} else {
c.setCancelled(true);
}
// If the reason is anything but because of a spawner then tell players within range
tellPlayers(l, e, reason, res);
return false;
}
return true;
}
private void preSpawn(EntityType entityType, SpawnReason reason, Location l) {
// Check for entities that need cleanup
switch (reason) {
case BUILD_IRONGOLEM -> detectIronGolem(l);
case BUILD_SNOWMAN -> detectSnowman(l);
case BUILD_WITHER -> {
detectWither(l);
}
default -> throw new IllegalArgumentException("Unexpected value: " + reason);
}
Entity entity = l.getWorld().spawnEntity(l, entityType);
justSpawned.add(entity.getUniqueId());
if (reason == SpawnReason.BUILD_WITHER) {
// Create explosion
l.getWorld().createExplosion(l, 7F, true, true, entity);
}
}
private void detectIronGolem(Location l) {
Block legs = l.getBlock();
// Erase legs
addon.getBlockLimitListener().removeBlock(legs);
legs.setType(Material.AIR);
// Look around for possible constructions
for (BlockFace bf : CARDINALS) {
Block body = legs.getRelative(bf);
if (body.getType().equals(Material.IRON_BLOCK)) {
// Check for head
Block head = body.getRelative(bf);
if (head.getType().equals(Material.CARVED_PUMPKIN)) {
// Check for arms the rule is that they must be opposite and have nothing "beneath" them
for (BlockFace bf2 : CARDINALS) {
Block arm1 = body.getRelative(bf2);
Block arm2 = body.getRelative(bf2.getOppositeFace());
if (arm1.getType() == Material.IRON_BLOCK && arm2.getType() == Material.IRON_BLOCK
&& arm1.getRelative(bf.getOppositeFace()).isEmpty()
&& arm2.getRelative(bf.getOppositeFace()).isEmpty()) {
// Erase!
addon.getBlockLimitListener().removeBlock(body);
addon.getBlockLimitListener().removeBlock(arm1);
addon.getBlockLimitListener().removeBlock(arm2);
addon.getBlockLimitListener().removeBlock(head);
body.setType(Material.AIR);
arm1.setType(Material.AIR);
arm2.setType(Material.AIR);
head.setType(Material.AIR);
return;
private void checkLimit(CreatureSpawnEvent e, boolean bypass) {
addon.getIslands().getIslandAt(e.getLocation()).ifPresent(island -> {
// Check if creature is allowed to spawn or not
if (!bypass && !island.isSpawn() && atLimit(island, e.getEntity())) {
// Not allowed
e.setCancelled(true);
// If the reason is anything but because of a spawner then tell players within range
if (!e.getSpawnReason().equals(SpawnReason.SPAWNER) && !e.getSpawnReason().equals(SpawnReason.NATURAL) && !e.getSpawnReason().equals(SpawnReason.INFECTION) && !e.getSpawnReason().equals(SpawnReason.NETHER_PORTAL) && !e.getSpawnReason().equals(SpawnReason.REINFORCEMENTS) && !e.getSpawnReason().equals(SpawnReason.SLIME_SPLIT)) {
World w = e.getLocation().getWorld();
if (w == null) return;
for (Entity ent : w.getNearbyEntities(e.getLocation(), 5, 5, 5)) {
if (ent instanceof Player) {
User.getInstance(ent).notify("entity-limits.hit-limit", "[entity]",
Util.prettifyText(e.getEntityType().toString()),
TextVariables.NUMBER, String.valueOf(addon.getSettings().getLimits().get(e.getEntityType())));
}
}
}
}
}
}
private void detectSnowman(Location l) {
Block legs = l.getBlock();
// Erase legs
addon.getBlockLimitListener().removeBlock(legs);
legs.setType(Material.AIR);
// Look around for possible constructions
for (BlockFace bf : CARDINALS) {
Block body = legs.getRelative(bf);
if (body.getType().equals(Material.SNOW_BLOCK)) {
// Check for head
Block head = body.getRelative(bf);
if (head.getType().equals(Material.CARVED_PUMPKIN)) {
// Erase
addon.getBlockLimitListener().removeBlock(body);
addon.getBlockLimitListener().removeBlock(head);
body.setType(Material.AIR);
head.setType(Material.AIR);
return;
}
}
}
}
private void detectWither(Location l) {
Block legs = l.getBlock();
// Erase legs
addon.getBlockLimitListener().removeBlock(legs);
legs.setType(Material.AIR);
// Look around for possible constructions
for (BlockFace bf : CARDINALS) {
Block body = legs.getRelative(bf);
if (isWither(body)) {
// Check for head
Block head = body.getRelative(bf);
if (head.getType().equals(Material.WITHER_SKELETON_SKULL) || head.getType().equals(Material.WITHER_SKELETON_WALL_SKULL)) {
// Check for arms the rule is that they must be opposite and have nothing "beneath" them
for (BlockFace bf2 : CARDINALS) {
Block arm1 = body.getRelative(bf2);
Block arm2 = body.getRelative(bf2.getOppositeFace());
Block head2 = arm1.getRelative(bf);
Block head3 = arm2.getRelative(bf);
if (isWither(arm1)
&& isWither(arm2)
&& arm1.getRelative(bf.getOppositeFace()).isEmpty()
&& arm2.getRelative(bf.getOppositeFace()).isEmpty()
&& (head2.getType().equals(Material.WITHER_SKELETON_SKULL) || head2.getType().equals(Material.WITHER_SKELETON_WALL_SKULL))
&& (head3.getType().equals(Material.WITHER_SKELETON_SKULL) || head3.getType().equals(Material.WITHER_SKELETON_WALL_SKULL))
) {
// Erase!
addon.getBlockLimitListener().removeBlock(body);
addon.getBlockLimitListener().removeBlock(arm1);
addon.getBlockLimitListener().removeBlock(arm2);
addon.getBlockLimitListener().removeBlock(head);
addon.getBlockLimitListener().removeBlock(head2);
addon.getBlockLimitListener().removeBlock(head3);
body.setType(Material.AIR);
arm1.setType(Material.AIR);
arm2.setType(Material.AIR);
head.setType(Material.AIR);
head2.setType(Material.AIR);
head3.setType(Material.AIR);
return;
}
}
}
}
}
}
private boolean isWither(Block body) {
if (Util.getMinecraftVersion() < 16) {
return body.getType().equals(Material.SOUL_SAND);
}
return Tag.WITHER_SUMMON_BASE_BLOCKS.isTagged(body.getType());
}
/**
* Tell players within a 5 x 5 x 5 radius that the spawning was denied. Informing happens 1 tick after event
* @param l location
* @param entity entity spawned
* @param reason reason - some reasons are not reported
* @param res at limit result
*/
private void tellPlayers(Location l, Entity entity, SpawnReason reason, AtLimitResult res) {
if (reason.equals(SpawnReason.SPAWNER) || reason.equals(SpawnReason.NATURAL)
|| reason.equals(SpawnReason.INFECTION) || reason.equals(SpawnReason.NETHER_PORTAL)
|| reason.equals(SpawnReason.REINFORCEMENTS) || reason.equals(SpawnReason.SLIME_SPLIT)) {
return;
}
World w = l.getWorld();
if (w == null) return;
Bukkit.getScheduler().runTask(addon.getPlugin(), () -> {
for (Entity ent : w.getNearbyEntities(l, 5, 5, 5)) {
if (ent instanceof Player p) {
p.updateInventory();
if (res.getTypelimit() != null) {
User.getInstance(p).notify("entity-limits.hit-limit", "[entity]",
Util.prettifyText(entity.getType().toString()),
TextVariables.NUMBER, String.valueOf(res.getTypelimit().getValue()));
} else {
User.getInstance(p).notify("entity-limits.hit-limit", "[entity]",
res.getGrouplimit().getKey().getName() + " (" + res.getGrouplimit().getKey().getTypes().stream().map(x -> Util.prettifyText(x.toString())).collect(Collectors.joining(", ")) + ")",
TextVariables.NUMBER, String.valueOf(res.getGrouplimit().getValue()));
}
}
}
});
@ -369,103 +163,21 @@ public class EntityLimitListener implements Listener {
* @param ent - the entity
* @return true if at the limit, false if not
*/
AtLimitResult atLimit(Island island, Entity ent) {
private boolean atLimit(Island island, Entity ent) {
// Check island settings first
int limitAmount = -1;
Map<Settings.EntityGroup, Integer> groupsLimits = new HashMap<>();
@Nullable
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(island.getUniqueId());
if (ibc != null) {
// Get the limit amount for this type
limitAmount = ibc.getEntityLimit(ent.getType());
// Handle entity groups
List<Settings.EntityGroup> groupdefs = addon.getSettings().getGroupLimits().getOrDefault(ent.getType(), new ArrayList<>());
groupdefs.forEach(def -> {
int limit = ibc.getEntityGroupLimit(def.getName());
if (limit >= 0)
groupsLimits.put(def, limit);
});
if (addon.getBlockLimitListener().getIsland(island.getUniqueId()) != null) {
limitAmount = addon.getBlockLimitListener().getIsland(island.getUniqueId()).getEntityLimit(ent.getType());
}
// If no island settings then try global settings
if (limitAmount < 0 && addon.getSettings().getLimits().containsKey(ent.getType())) {
limitAmount = addon.getSettings().getLimits().get(ent.getType());
}
// Group limits
if (addon.getSettings().getGroupLimits().containsKey(ent.getType())) {
addon.getSettings().getGroupLimits().getOrDefault(ent.getType(), new ArrayList<>()).stream()
.filter(group -> !groupsLimits.containsKey(group) || groupsLimits.get(group) > group.getLimit())
.forEach(group -> groupsLimits.put(group, group.getLimit()));
}
if (limitAmount < 0 && groupsLimits.isEmpty()) {
return new AtLimitResult();
}
if (limitAmount < 0) return false;
// We have to count the entities
if (limitAmount >= 0)
{
int count = (int) ent.getWorld().getNearbyEntities(island.getBoundingBox()).stream()
.filter(e -> e.getType().equals(ent.getType()))
.count();
int max = limitAmount + (ibc == null ? 0 : ibc.getEntityLimitOffset(ent.getType()));
if (count >= max) {
return new AtLimitResult(ent.getType(), max);
}
}
// Group limits
if (ibc != null) {
Map<String, EntityGroup> groupbyname = groupsLimits.keySet().stream()
.collect(Collectors.toMap(EntityGroup::getName, e -> e));
ibc.getEntityGroupLimits().entrySet().stream()
.filter(e -> groupbyname.containsKey(e.getKey()))
.forEach(e -> groupsLimits.put(groupbyname.get(e.getKey()), e.getValue()));
}
// Now do the group limits
for (Map.Entry<Settings.EntityGroup, Integer> group : groupsLimits.entrySet()) { //do not use lambda
if (group.getValue() < 0)
continue;
// int count = (int) ent.getWorld().getEntities().stream()
// .filter(e -> group.getKey().contains(e.getType()))
// .filter(e -> island.inIslandSpace(e.getLocation())).count();
int count = (int) ent.getWorld().getNearbyEntities(island.getBoundingBox()).stream()
.filter(e -> group.getKey().contains(e.getType()))
.count();
int max = group.getValue() + + (ibc == null ? 0 : ibc.getEntityGroupLimitOffset(group.getKey().getName()));
if (count >= max) {
return new AtLimitResult(group.getKey(), max);
}
}
return new AtLimitResult();
}
static class AtLimitResult {
private Map.Entry<EntityType, Integer> typelimit;
private Map.Entry<EntityGroup, Integer> grouplimit;
public AtLimitResult() {}
public AtLimitResult(EntityType type, int limit) {
typelimit = new AbstractMap.SimpleEntry<>(type, limit);
}
public AtLimitResult(EntityGroup type, int limit) {
grouplimit = new AbstractMap.SimpleEntry<>(type, limit);
}
/**
* @return true if at limit
*/
public boolean hit() {
return typelimit != null || grouplimit != null;
}
public Map.Entry<EntityType, Integer> getTypelimit() {
return typelimit;
}
public Map.Entry<EntityGroup, Integer> getGrouplimit() {
return grouplimit;
}
return ent.getWorld().getEntities().stream()
.filter(e -> e.getType().equals(ent.getType()))
.filter(e -> island.inIslandSpace(e.getLocation())).count() >= limitAmount;
}
}

View File

@ -5,6 +5,7 @@ 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;
@ -16,21 +17,16 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.events.island.IslandEvent.Reason;
import world.bentobox.bentobox.api.events.team.TeamSetownerEvent;
import world.bentobox.bentobox.api.events.team.TeamEvent.TeamSetownerEvent;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.limits.Limits;
import world.bentobox.limits.Settings.EntityGroup;
import world.bentobox.limits.events.LimitsJoinPermCheckEvent;
import world.bentobox.limits.events.LimitsPermCheckEvent;
import world.bentobox.limits.objects.IslandBlockCount;
/**
* Sets block limits based on player permission
*
* @author tastybento
*
*/
@ -39,139 +35,70 @@ 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
* @param permissionPrefix - permission prefix for this game mode
* @param islandId - island string id
* @param gameMode - game mode string doing the checking
*/
public void checkPerms(Player player, String permissionPrefix, String islandId, String gameMode) {
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(islandId);
// Check permissions
if (ibc != null) {
// Clear permission limits
ibc.getEntityLimits().clear();
ibc.getEntityGroupLimits().clear();
ibc.getBlockLimits().clear();
}
for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) {
if (!perms.getValue() || !perms.getPermission().startsWith(permissionPrefix)
|| badSyntaxCheck(perms, player.getName(), permissionPrefix)) {
continue;
}
// Check formatting
String[] split = perms.getPermission().split("\\.");
// Entities & materials
EntityType et = Arrays.stream(EntityType.values()).filter(t -> t.name().equalsIgnoreCase(split[3]))
.findFirst().orElse(null);
Material m = Arrays.stream(Material.values()).filter(t -> t.name().equalsIgnoreCase(split[3])).findFirst()
.orElse(null);
EntityGroup entgroup = addon.getSettings().getGroupLimitDefinitions().stream()
.filter(t -> t.getName().equalsIgnoreCase(split[3])).findFirst().orElse(null);
private void checkPerms(Player player, String permissionPrefix, String islandId, String gameMode) {
IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(islandId);
if (ibc != null) {
// Clear permission limits
ibc.getEntityLimits().clear();
ibc.getBlockLimits().clear();
}
for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) {
if (!perms.getValue() || !perms.getPermission().startsWith(permissionPrefix)) continue;
// No wildcards
if (perms.getPermission().contains(permissionPrefix + "*")) {
logError(player.getName(), perms.getPermission(), "wildcards are not allowed.");
return;
}
// Check formatting
String[] split = perms.getPermission().split("\\.");
if (split.length != 5) {
logError(player.getName(), perms.getPermission(), "format must be '" + permissionPrefix + "MATERIAL.NUMBER' or '" + permissionPrefix + "ENTITY-TYPE.NUMBER'");
return;
}
// Check value
if (!NumberUtils.isDigits(split[4])) {
logError(player.getName(), perms.getPermission(), "the last part MUST be a number!");
return;
}
// Entities & materials
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);
if (entgroup == null && et == null && m == null) {
logError(player.getName(), perms.getPermission(),
split[3].toUpperCase(Locale.ENGLISH) + " is not a valid material or entity type/group.");
break;
}
// Make an ibc if required
if (ibc == null) {
ibc = new IslandBlockCount(islandId, gameMode);
}
// Get the value
int value = Integer.parseInt(split[4]);
addon.log("Setting login limit via perm for " + player.getName() + "...");
// Fire perm check event
LimitsPermCheckEvent l = new LimitsPermCheckEvent(player, islandId, ibc, entgroup, et, m, value);
Bukkit.getPluginManager().callEvent(l);
if (l.isCancelled()) {
addon.log("Permissions not set because another addon/plugin canceled setting.");
continue;
}
// Use event values
ibc = l.getIbc();
// Make an ibc if required
if (ibc == null) {
ibc = new IslandBlockCount(islandId, gameMode);
}
// Run null checks and set ibc
runNullCheckAndSet(ibc, l);
}
// Check removed permissions
// If any changes have been made then store it - don't make files unless they
// are needed
if (ibc != null)
addon.getBlockLimitListener().setIsland(islandId, ibc);
}
private boolean badSyntaxCheck(PermissionAttachmentInfo perms, String name, String permissionPrefix) {
// No wildcards
if (perms.getPermission().contains(permissionPrefix + "*")) {
logError(name, perms.getPermission(), "wildcards are not allowed.");
return true;
}
// Check formatting
String[] split = perms.getPermission().split("\\.");
if (split.length != 5) {
logError(name, perms.getPermission(), "format must be '" + permissionPrefix + "MATERIAL.NUMBER', '"
+ permissionPrefix + "ENTITY-TYPE.NUMBER', or '" + permissionPrefix + "ENTITY-GROUP.NUMBER'");
return true;
}
// Check value
try {
Integer.parseInt(split[4]);
} catch (Exception e) {
logError(name, perms.getPermission(), "the last part MUST be an integer!");
return true;
}
return false;
}
private void runNullCheckAndSet(@NonNull IslandBlockCount ibc, @NonNull LimitsPermCheckEvent l) {
EntityGroup entgroup = l.getEntityGroup();
EntityType et = l.getEntityType();
Material m = l.getMaterial();
int value = l.getValue();
if (entgroup != null) {
// Entity group limit
int v = Math.max(ibc.getEntityGroupLimit(entgroup.getName()), value);
ibc.setEntityGroupLimit(entgroup.getName(), v);
addon.log("Setting group limit " + entgroup.getName() + " " + v);
} else if (et != null && m == null) {
// Entity limit
int v = Math.max(ibc.getEntityLimit(et), value);
ibc.setEntityLimit(et, v);
addon.log("Setting entity limit " + et + " " + v);
} else if (m != null && et == null) {
// Block limit
int v = Math.max(ibc.getBlockLimit(m), value);
addon.log("Setting block limit " + m + " " + v);
ibc.setBlockLimit(m, v);
} else {
if (m != null && m.isBlock()) {
int v = Math.max(ibc.getBlockLimit(m), value);
addon.log("Setting block limit " + m + " " + v);
// Material limit
ibc.setBlockLimit(m, v);
} else if (et != null) {
int v = Math.max(ibc.getEntityLimit(et), value);
addon.log("Setting entity limit " + et + " " + v);
// This is an entity setting
ibc.setEntityLimit(et, v);
}
}
if (et == null && m == null) {
logError(player.getName(), perms.getPermission(), split[3].toUpperCase(Locale.ENGLISH) + " is not a valid material or entity type.");
break;
}
// Make an ibc if required
if (ibc == null) {
ibc = new IslandBlockCount(islandId, gameMode);
}
if (et != null && m == null) {
// Entity limit
ibc.setEntityLimit(et, Math.max(ibc.getEntityLimit(et), Integer.valueOf(split[4])));
} else if (m != null && et == null) {
// Material limit
ibc.setBlockLimit(m, Math.max(ibc.getBlockLimit(m), Integer.valueOf(split[4])));
} else {
if (m.isBlock()) {
// Material limit
ibc.setBlockLimit(m, Math.max(ibc.getBlockLimit(m), Integer.valueOf(split[4])));
} else {
// This is an entity setting
ibc.setEntityLimit(et, Math.max(ibc.getEntityLimit(et), Integer.valueOf(split[4])));
}
}
}
// 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 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...");
}
/*
@ -180,66 +107,38 @@ 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 -> {
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;
// Check if player has any islands in the game modes
addon.getGameModes().forEach(gm -> {
if (addon.getIslands().hasIsland(gm.getOverWorld(), e.getPlayer().getUniqueId())) {
String islandId = addon.getIslands().getIsland(gm.getOverWorld(), e.getPlayer().getUniqueId()).getUniqueId();
checkPerms(e.getPlayer(), gm.getPermissionPrefix() + "island.limit.", islandId, gm.getDescription().getName());
}
});
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onUnregisterIsland(IslandEvent e) {
if (!e.getReason().equals(Reason.UNREGISTERED)) {
return;
}
removeOwnerPerms(e.getIsland());
if (!e.getReason().equals(Reason.UNREGISTERED)) {
return;
}
removeOwnerPerms(e.getIsland());
}
/*
@ -247,30 +146,29 @@ 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

@ -7,13 +7,11 @@ import java.util.UUID;
import com.google.gson.annotations.Expose;
import world.bentobox.bentobox.database.objects.DataObject;
import world.bentobox.bentobox.database.objects.Table;
/**
* @author tastybento
*
*/
@Table(name = "EntityLimits")
public class EntityLimitsDO implements DataObject {
@Expose
@ -80,9 +78,10 @@ public class EntityLimitsDO implements DataObject {
if (obj == null) {
return false;
}
if (!(obj instanceof EntityLimitsDO other)) {
if (!(obj instanceof EntityLimitsDO)) {
return false;
}
EntityLimitsDO other = (EntityLimitsDO) obj;
if (uniqueId == null) {
return other.uniqueId == null;
} else return uniqueId.equals(other.uniqueId);

View File

@ -1,9 +1,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;
@ -11,407 +9,196 @@ import org.bukkit.entity.EntityType;
import com.google.gson.annotations.Expose;
import world.bentobox.bentobox.database.objects.DataObject;
import world.bentobox.bentobox.database.objects.Table;
/**
* @author tastybento
*
*/
@Table(name = "IslandBlockCount")
public class IslandBlockCount implements DataObject {
@Expose
private Map<Material, Integer> blockCounts = new EnumMap<>(Material.class);
private String uniqueId = "";
@Expose
private String gameMode = "";
@Expose
private Map<Material, Integer> blockCount = new EnumMap<>(Material.class);
/**
* Permission based limits
*/
@Expose
private Map<Material, Integer> blockLimits = new EnumMap<>(Material.class);
@Expose
private Map<Material, Integer> blockLimitsOffset = new EnumMap<>(Material.class);
private boolean changed;
@Expose
private Map<String, Integer> entityGroupLimits = new HashMap<>();
@Expose
private Map<String, Integer> entityGroupLimitsOffset = new HashMap<>();
@Expose
private Map<EntityType, Integer> entityLimits = new EnumMap<>(EntityType.class);
@Expose
private Map<EntityType, Integer> entityLimitsOffset = new EnumMap<>(EntityType.class);
@Expose
private String gameMode;
@Expose
private String uniqueId;
/**
* Create an island block count object
*
* @param islandId - unique Island ID string
* @param gameMode - Game mode name from gm.getDescription().getName()
*/
public IslandBlockCount(String islandId, String gameMode) {
this.uniqueId = islandId;
this.gameMode = gameMode;
setChanged();
// Required for YAML database
public IslandBlockCount() {}
public IslandBlockCount(String uniqueId2, String gameMode2) {
this.uniqueId = uniqueId2;
this.gameMode = gameMode2;
}
/**
* Add a material to the count
*
* @param material - material
/* (non-Javadoc)
* @see world.bentobox.bentobox.database.objects.DataObject#getUniqueId()
*/
public void add(Material material) {
getBlockCounts().merge(material, 1, Integer::sum);
setChanged();
@Override
public String getUniqueId() {
return uniqueId;
}
/**
* Clear all island-specific entity group limits
/* (non-Javadoc)
* @see world.bentobox.bentobox.database.objects.DataObject#setUniqueId(java.lang.String)
*/
public void clearEntityGroupLimits() {
entityGroupLimits.clear();
setChanged();
}
/**
* Clear all island-specific entity type limits
*/
public void clearEntityLimits() {
entityLimits.clear();
setChanged();
}
/**
* Get the block count for this material for this island
*
* @param m - material
* @return count
*/
public Integer getBlockCount(Material m) {
return getBlockCounts().getOrDefault(m, 0);
@Override
public void setUniqueId(String uniqueId) {
this.uniqueId = uniqueId;
}
/**
* @return the blockCount
*/
public Map<Material, Integer> getBlockCounts() {
if (blockCounts == null) {
blockCounts = new EnumMap<>(Material.class);
}
return blockCounts;
public Map<Material, Integer> getBlockCount() {
return blockCount;
}
/**
* Get the block limit for this material for this island
*
* @param m - material
* @return limit or -1 for unlimited
* @param blockCount the blockCount to set
*/
public int getBlockLimit(Material m) {
return getBlockLimits().getOrDefault(m, -1);
public void setBlockCount(Map<Material, Integer> blockCount) {
this.blockCount = blockCount;
}
/**
* Get the block offset for this material for this island
*
* @param m - material
* @return offset
* Add a material to the count
* @param material - material
*/
public int getBlockLimitOffset(Material m) {
return getBlockLimitsOffset().getOrDefault(m, 0);
public void add(Material material) {
blockCount.merge(material, 1, Integer::sum);
}
/**
* Remove a material from the count
* @param material - material
*/
public void remove(Material material) {
blockCount.put(material, blockCount.getOrDefault(material, 0) - 1);
blockCount.values().removeIf(v -> v <= 0);
}
/**
* Check if this material is at or over a limit
* @param material - block material
* @param limit - limit to check
* @return true if count is >= limit
*/
public boolean isAtLimit(Material material, int limit) {
return blockCount.getOrDefault(material, 0) >= limit;
}
/**
* Check if no more of this material can be added to this island
* @param m - material
* @return true if no more material can be added
*/
public boolean isAtLimit(Material m) {
// Check island limits first
return blockLimits.containsKey(m) && blockCount.getOrDefault(m, 0) >= blockLimits.get(m);
}
public boolean isBlockLimited(Material m) {
return blockLimits.containsKey(m);
}
/**
* @return the blockLimits
*/
public Map<Material, Integer> getBlockLimits() {
return Objects.requireNonNullElse(blockLimits, new EnumMap<>(Material.class));
}
/**
* @return the blockLimitsOffset
*/
public Map<Material, Integer> getBlockLimitsOffset() {
if (blockLimitsOffset == null) {
blockLimitsOffset = new EnumMap<>(Material.class);
}
return blockLimitsOffset;
}
/**
* Get the limit for an entity group
*
* @param name - entity group
* @return limit or -1 for unlimited
*/
public int getEntityGroupLimit(String name) {
return getEntityGroupLimits().getOrDefault(name, -1);
}
/**
* Get the offset for an entity group
*
* @param name - entity group
* @return offset
*/
public int getEntityGroupLimitOffset(String name) {
return getEntityGroupLimitsOffset().getOrDefault(name, 0);
}
/**
* @return the entityGroupLimits
*/
public Map<String, Integer> getEntityGroupLimits() {
return Objects.requireNonNullElse(entityGroupLimits, new HashMap<>());
}
/**
* @return the entityGroupLimitsOffset
*/
public Map<String, Integer> getEntityGroupLimitsOffset() {
if (entityGroupLimitsOffset == null) {
entityGroupLimitsOffset = new HashMap<>();
}
return entityGroupLimitsOffset;
}
/**
* Get the limit for an entity type
*
* @param t - entity type
* @return limit or -1 for unlimited
*/
public int getEntityLimit(EntityType t) {
return getEntityLimits().getOrDefault(t, -1);
}
/**
* Get the limit offset for an entity type
*
* @param t - entity type
* @return offset
*/
public int getEntityLimitOffset(EntityType t) {
return getEntityLimitsOffset().getOrDefault(t, 0);
}
/**
* @return the entityLimits
*/
public Map<EntityType, Integer> getEntityLimits() {
return Objects.requireNonNullElse(entityLimits, new EnumMap<>(EntityType.class));
}
/**
* @return the entityLimitsOffset
*/
public Map<EntityType, Integer> getEntityLimitsOffset() {
if (entityLimitsOffset == null) {
entityLimitsOffset = new EnumMap<>(EntityType.class);
}
return entityLimitsOffset;
}
/**
* @return the gameMode
*/
public String getGameMode() {
return gameMode;
}
/*
* (non-Javadoc)
*
* @see world.bentobox.bentobox.database.objects.DataObject#getUniqueId()
*/
@Override
public String getUniqueId() {
return uniqueId;
}
/**
* Check if no more of this material can be added to this island
*
* @param m - material
* @return true if no more material can be added
*/
public boolean isAtLimit(Material m) {
// Check island limits first
return getBlockLimits().containsKey(m)
&& getBlockCounts().getOrDefault(m, 0) >= getBlockLimit(m) + this.getBlockLimitOffset(m);
}
/**
* Check if this material is at or over a limit
*
* @param material - block material
* @param limit - limit to check
* @return true if count is >= limit
*/
public boolean isAtLimit(Material material, int limit) {
return getBlockCounts().getOrDefault(material, 0) >= limit + this.getBlockLimitOffset(material);
}
public boolean isBlockLimited(Material m) {
return getBlockLimits().containsKey(m);
}
/**
* @return the changed
*/
public boolean isChanged() {
return changed;
}
public boolean isGameMode(String gameMode) {
return getGameMode().equals(gameMode);
}
/**
* Remove a material from the count
*
* @param material - material
*/
public void remove(Material material) {
getBlockCounts().put(material, getBlockCounts().getOrDefault(material, 0) - 1);
getBlockCounts().values().removeIf(v -> v <= 0);
setChanged();
}
/**
* @param blockCounts the blockCount to set
*/
public void setBlockCounts(Map<Material, Integer> blockCounts) {
this.blockCounts = blockCounts;
setChanged();
}
/**
* Set the block limit for this material for this island
*
* @param m - material
* @param limit - maximum number allowed
*/
public void setBlockLimit(Material m, int limit) {
getBlockLimits().put(m, limit);
setChanged();
return blockLimits;
}
/**
* @param blockLimits the blockLimits to set
*/
public void setBlockLimits(Map<Material, Integer> blockLimits) {
this.blockLimits = blockLimits;
setChanged();
this.blockLimits = blockLimits;
}
/**
* 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
* Get the block limit for this material for this island
* @param m - material
* @return limit or -1 for unlimited
*/
public void setBlockLimitsOffset(Material m, Integer blockLimitsOffset) {
getBlockLimitsOffset().put(m, blockLimitsOffset);
public Integer getBlockLimit(Material m) {
return blockLimits.getOrDefault(m, -1);
}
/**
* Mark changed
* Set the block limit for this material for this island
* @param m - material
* @param limit - maximum number allowed
*/
public void setChanged() {
this.changed = true;
public void setBlockLimit(Material m, int limit) {
blockLimits.put(m, limit);
}
/**
* @param changed the changed to set
* @return the gameMode
*/
public void setChanged(boolean changed) {
this.changed = changed;
public String getGameMode() {
return gameMode;
}
/**
* Set an island-specific entity group limit
*
* @param name - entity group
* @param limit - limit
*/
public void setEntityGroupLimit(String name, int limit) {
getEntityGroupLimits().put(name, limit);
setChanged();
}
/**
* @param entityGroupLimits the entityGroupLimits to set
*/
public void setEntityGroupLimits(Map<String, Integer> entityGroupLimits) {
this.entityGroupLimits = entityGroupLimits;
setChanged();
}
/**
* Set an offset to an entity group limit. This will increase/decrease the value
* of the limit.
*
* @param name group name
* @param entityGroupLimitsOffset the entityGroupLimitsOffset to set
*/
public void setEntityGroupLimitsOffset(String name, Integer entityGroupLimitsOffset) {
getEntityGroupLimitsOffset().put(name, entityGroupLimitsOffset);
}
/**
* Set an island-specific entity type limit
*
* @param t - entity type
* @param limit - limit
*/
public void setEntityLimit(EntityType t, int limit) {
getEntityLimits().put(t, limit);
setChanged();
}
/**
* @param entityLimits the entityLimits to set
*/
public void setEntityLimits(Map<EntityType, Integer> entityLimits) {
this.entityLimits = entityLimits;
setChanged();
}
/**
* Set an offset to an entity limit. This will increase/decrease the value of
* the limit.
*
* @param type Entity Type
* @param entityLimitsOffset the entityLimitsOffset to set
*/
public void setEntityLimitsOffset(EntityType type, Integer entityLimitsOffset) {
this.getEntityLimitsOffset().put(type, entityLimitsOffset);
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();
this.gameMode = gameMode;
}
/*
* (non-Javadoc)
*
* @see
* world.bentobox.bentobox.database.objects.DataObject#setUniqueId(java.lang.
* String)
/**
* @return the entityLimits
*/
@Override
public void setUniqueId(String uniqueId) {
this.uniqueId = uniqueId;
setChanged();
public Map<EntityType, Integer> getEntityLimits() {
return entityLimits;
}
/**
* @param entityLimits the entityLimits to set
*/
public void setEntityLimits(Map<EntityType, Integer> entityLimits) {
this.entityLimits = entityLimits;
}
/**
* 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);
}
/**
* 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);
}
/**
* Clear all island-specific entity type limits
*/
public void clearEntityLimits() {
entityLimits.clear();
}
}

View File

@ -1,22 +1,37 @@
name: Limits
main: world.bentobox.limits.Limits
version: ${version}${build.number}
api-version: 2.3.0
api-version: 1.12
authors: tastybento
softdepend: AcidIsland, BSkyBlock, CaveBlock, AOneBlock, SkyGrid
softdepend: AcidIsland, BSkyBlock, CaveBlock
permissions:
'[gamemode].limits.player.limits':
acidisland.limits.player.limits:
description: Player can use limits command
default: true
'[gamemode].limits.player.recount':
acidisland.limits.player.recount:
description: Player can use recount command
default: true
'[gamemode].limits.admin.limits':
acidisland.limits.admin.limits:
description: Player can use admin limits command
default: op
'[gamemode].mod.bypass':
description: Player can bypass limits
bskyblock.limits.player.limits:
description: Player can use limits command
default: true
bskyblock.limits.player.recount:
description: Player can use recount command
default: true
bskyblock.limits.admin.limits:
description: Player can use admin limits command
default: op
caveblock.limits.player.limits:
description: Player can use limits command
default: true
caveblock.limits.player.recount:
description: Player can use recount command
default: true
caveblock.limits.admin.limits:
description: Player can use admin limits command
default: op

View File

@ -2,11 +2,7 @@
gamemodes:
- AcidIsland
- 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
- CaveBock
# Permissions
# Island owners can be given permissions that override all general settings
@ -17,9 +13,6 @@ ignore-center-block: true
# Cooldown for player recount command in seconds
cooldown: 120
# Use async checks for snowmen and iron golums. Set to false if you see problems.
async-golums: true
# General block limiting
# Use this section to limit how many blocks can be added to an island.
# 0 means the item will be blocked from placement completely.
@ -40,4 +33,5 @@ worlds:
entitylimits:
ENDERMAN: 5
CHICKEN: 10
pig_ZOMbIe: 15

View File

@ -19,33 +19,8 @@ admin:
calc:
parameters: "<player>"
description: "recalculate the island limits for player"
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]."
finished: "&aIsland recalc finished successfully!"
island:
limits:
description: "show your island limits"
@ -55,7 +30,4 @@ 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

@ -1,55 +0,0 @@
---
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

@ -1,30 +0,0 @@
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,54 +1,27 @@
---
#
# 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!"
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]."
finished: '&aPrzeliczanie zakończone!'
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

@ -1,25 +0,0 @@
---
block-limits:
hit-limit: "&c[material] limitat la [number]!"
entity-limits:
hit-limit: "&c[entity] reproducere limitata la [number]!"
limits:
panel-title: Limitele insulei
admin:
limits:
main:
parameters: "<jucator>"
description: Arata limitele insulei pentru jucator
calc:
parameters: "<jucator>"
description: recalculeaza limitele insulei pentru jocator
finished: "&aRecalcularea insulei terminata cu succes!"
island:
limits:
description: iti arata limitele insulei
max-color: "&c"
regular-color: "&a"
block-limit-syntax: "[number]/[limit]"
no-limits: "&cNici o limita setata in aceasta lume"
recount:
description: renumara limitele insulei tale

View File

@ -1,25 +1,32 @@
---
###########################################################################################
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
# the one at http://yaml-online-parser.appspot.com #
###########################################################################################
entity-limits:
hit-limit: "&3&LS&b&lC &8&L» &e[entity] &4varlığından &5[number] &4tane koyabilirsin!"
block-limits:
hit-limit: "&3&LS&b&lC &8&L» &e[material] &4eşyasından &5[number] &4koyabilirsin!"
limits:
panel-title: "&3&lSon&b&lCesurlar &eAda limiti"
admin:
limits:
calc:
description: Oyuncu için ada limitlerini tekrar hesapla.
parameters: "<player>"
finished: "&aAda hesaplaması başarıyla yapıldı."
main:
description: Oyuncu için ada limitlerini göster.
parameters: "<player>"
main:
parameters: "<player>"
description: "Oyuncu için ada limitlerini göster."
calc:
parameters: "<player>"
description: "Oyuncu için ada limitlerini tekrar hesapla."
finished: "&aIsland recalc finished sucessfully!"
island:
limits:
block-limit-syntax: "&5[number]&7/&e[limit]"
description: Ada limitlerini gör.
limits:
parameters: ""
description: "Ada limitlerini gör."
max-color: "&c"
regular-color: "&9"
no-limits: "&4Bu dünyada limitler ayarlı değil!"
recount:
description: Ada limitlerini tekrardan hesaplar.
block-limits:
hit-limit: "&e[material] &4eşyasından &5[number] &4koyabilirsin!"
entity-limits:
hit-limit: "&e[entity] &4varlığından &5[number] &4tane koyabilirsin!"
limits:
panel-title: "&eAda limiti"
block-limit-syntax: "&5[number]&7/&e[limit]"
no-limits: "&3&LS&c&lC &A&L» &4Bu dünyada limitler ayarlı değil!"

View File

@ -1,32 +0,0 @@
###########################################################################################
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
# the one at http://yaml-online-parser.appspot.com #
###########################################################################################
block-limits:
hit-limit: "&c[material] bị giới hạn trong [number] khối!"
entity-limits:
hit-limit: "&c[entity] bị giới hạn trong [number] thực thể!"
limits:
panel-title: "Giới hạn đảo"
admin:
limits:
main:
parameters: "<người chơi>"
description: "xem giới hạn đảo của người chơi"
calc:
parameters: "<người chơi>"
description: "tính toán lại giới hạn đảo của người chơi"
finished: "&aTính toán đã hoàn thành!"
island:
limits:
description: "xem giới hạn đảo của bạn"
max-color: "&c"
regular-color: "&a"
block-limit-syntax: "[number]/[limit]"
no-limits: "&cKhông có giới hạn ở thế giới này"
recount:
description: "tính toán lại giới hạn đảo của bạn"

View File

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

View File

@ -1,9 +0,0 @@
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

@ -9,10 +9,8 @@ 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;
@ -24,7 +22,6 @@ import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.PluginManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -37,11 +34,10 @@ import org.powermock.modules.junit4.PowerMockRunner;
import world.bentobox.bentobox.api.addons.AddonDescription;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.events.team.TeamSetownerEvent;
import world.bentobox.bentobox.api.events.team.TeamEvent.TeamSetownerEvent;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.IslandsManager;
import world.bentobox.limits.Limits;
import world.bentobox.limits.Settings;
import world.bentobox.limits.listeners.BlockLimitsListener;
import world.bentobox.limits.listeners.JoinListener;
import world.bentobox.limits.objects.IslandBlockCount;
@ -51,14 +47,12 @@ import world.bentobox.limits.objects.IslandBlockCount;
*
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({ Bukkit.class })
@PrepareForTest( {Bukkit.class} )
public class JoinListenerTest {
@Mock
private Limits addon;
@Mock
private Settings settings;
@Mock
private GameModeAddon bskyblock;
@Mock
private Player player;
@ -74,83 +68,73 @@ public class JoinListenerTest {
private OfflinePlayer owner;
@Mock
private Island island;
@Mock
private PluginManager pim;
@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<>(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);
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.");
// 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);
// 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);
// 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();
}
/**
@ -195,228 +179,196 @@ 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' or 'bskyblock.island.limit.ENTITY-TYPE.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. 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 an integer! 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 a 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 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
public void testOnPlayerJoinWithPermLimitsSuccessEntityGroup() {
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.friendly.24");
when(permAtt.getValue()).thenReturn(true);
perms.add(permAtt);
when(player.getEffectivePermissions()).thenReturn(perms);
PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome");
jl.onPlayerJoin(e);
verify(addon, never()).logError(anyString());
verify(ibc).setEntityGroupLimit(eq("friendly"), eq(24));
}
/**
* Test method for
* {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
* Test 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.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.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));
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);
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));
}
/**
@ -448,7 +400,7 @@ public class JoinListenerTest {
verify(ibc, never()).setBlockLimit(eq(Material.STONE), eq(14));
verify(ibc).setBlockLimit(eq(Material.STONE), eq(34));
}
/**
* Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}.
*/
@ -480,43 +432,40 @@ 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();
}
@ -538,4 +487,6 @@ public class JoinListenerTest {
}
}

View File

@ -1,4 +1,4 @@
package world.bentobox.limits.commands.player;
package world.bentobox.limits.commands;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@ -27,17 +27,15 @@ import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.limits.Limits;
import world.bentobox.limits.Settings;
import world.bentobox.limits.objects.IslandBlockCount;
@RunWith(PowerMockRunner.class)
@PrepareForTest( Bukkit.class )
public class LimitTabTest {
public class LimitPanelTest {
@Mock
private Limits addon;
private LimitTab lp;
private LimitPanel lp;
@Mock
private Island island;
@ -49,36 +47,32 @@ public class LimitTabTest {
private World end;
@Mock
private BentoBox plugin;
@Mock
@Mock
private IslandWorldManager iwm;
@Mock
private Settings settings;
@Before
public void setUp() {
public void setUp() throws Exception {
// Island
when(island.getWorld()).thenReturn(world);
// Addon
when(addon.getPlugin()).thenReturn(plugin);
when(addon.getSettings()).thenReturn(settings);
when(settings.getLimits()).thenReturn(Collections.emptyMap());
when(plugin.getIWM()).thenReturn(iwm);
when(iwm.isNetherIslands(any())).thenReturn(true);
when(iwm.isEndIslands(any())).thenReturn(true);
when(iwm.getNetherWorld(eq(world))).thenReturn(nether);
when(iwm.getEndWorld(eq(world))).thenReturn(end);
// Worlds
// Worlds
Entity entity = mock(Entity.class);
when(entity.getType()).thenReturn(EntityType.BAT);
when(entity.getLocation()).thenReturn(mock(Location.class));
when(world.getEntities()).thenReturn(Collections.singletonList(entity));
when(nether.getEntities()).thenReturn(Collections.singletonList(entity));
when(end.getEntities()).thenReturn(Collections.singletonList(entity));
lp = new LimitTab(addon, new IslandBlockCount("", ""), Collections.emptyMap(), island, world, null, LimitTab.SORT_BY.A2Z);
lp = new LimitPanel(addon);
}
@After
public void tearDown() {
public void tearDown() throws Exception {
}
@Test
@ -101,7 +95,7 @@ public class LimitTabTest {
ent = EntityType.BAT;
assertEquals(1L, lp.getCount(island, ent));
}
@Test
public void testGetCountNotInIslandSpace() {
EntityType ent = EntityType.BAT;

View File

@ -1,148 +0,0 @@
package world.bentobox.limits.listeners;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.util.BoundingBox;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.limits.Limits;
import world.bentobox.limits.Settings;
import world.bentobox.limits.listeners.EntityLimitListener.AtLimitResult;
import world.bentobox.limits.objects.IslandBlockCount;
@RunWith(PowerMockRunner.class)
@PrepareForTest( Bukkit.class )
public class EntityLimitListenerTest {
@Mock
private Limits addon;
private EntityLimitListener ell;
@Mock
private Island island;
@Mock
private LivingEntity ent;
@Mock
private BlockLimitsListener bll;
@Mock
private World world;
private List<Entity> collection;
@Mock
private Location location;
private IslandBlockCount ibc;
@Before
public void setUp() throws Exception {
// Entity
when(ent.getType()).thenReturn(EntityType.ENDERMAN);
when(ent.getLocation()).thenReturn(location);
// Island
when(island.getUniqueId()).thenReturn(UUID.randomUUID().toString());
when(island.inIslandSpace(any(Location.class))).thenReturn(true);
ibc = new IslandBlockCount("","");
when(bll.getIsland(anyString())).thenReturn(ibc);
when(addon.getBlockLimitListener()).thenReturn(bll);
FileConfiguration config = new YamlConfiguration();
config.load("src/main/resources/config.yml");
// Settings
when(addon.getConfig()).thenReturn(config);
Settings settings = new Settings(addon);
when(addon.getSettings()).thenReturn(settings);
// World
when(ent.getWorld()).thenReturn(world);
collection = new ArrayList<>();
collection.add(ent);
collection.add(ent);
collection.add(ent);
collection.add(ent);
when(world.getNearbyEntities(any())).thenReturn(collection);
ell = new EntityLimitListener(addon);
}
/**
* Test for {@link EntityLimitListener#atLimit(Island, Entity)}
*/
@Test
public void testAtLimitUnderLimit() {
AtLimitResult result = ell.atLimit(island, ent);
assertFalse(result.hit());
}
/**
* Test for {@link EntityLimitListener#atLimit(Island, Entity)}
*/
@Test
public void testAtLimitAtLimit() {
collection.add(ent);
AtLimitResult result = ell.atLimit(island, ent);
assertTrue(result.hit());
assertEquals(EntityType.ENDERMAN, result.getTypelimit().getKey());
assertEquals(Integer.valueOf(5), result.getTypelimit().getValue());
}
/**
* Test for {@link EntityLimitListener#atLimit(Island, Entity)}
*/
@Test
public void testAtLimitUnderLimitIslandLimit() {
ibc.setEntityLimit(EntityType.ENDERMAN, 6);
AtLimitResult result = ell.atLimit(island, ent);
assertFalse(result.hit());
}
/**
* Test for {@link EntityLimitListener#atLimit(Island, Entity)}
*/
@Test
public void testAtLimitAtLimitIslandLimitNotAtLimit() {
ibc.setEntityLimit(EntityType.ENDERMAN, 6);
collection.add(ent);
AtLimitResult result = ell.atLimit(island, ent);
assertFalse(result.hit());
}
/**
* Test for {@link EntityLimitListener#atLimit(Island, Entity)}
*/
@Test
public void testAtLimitAtLimitIslandLimit() {
ibc.setEntityLimit(EntityType.ENDERMAN, 6);
collection.add(ent);
collection.add(ent);
AtLimitResult result = ell.atLimit(island, ent);
assertTrue(result.hit());
assertEquals(EntityType.ENDERMAN, result.getTypelimit().getKey());
assertEquals(Integer.valueOf(6), result.getTypelimit().getValue());
}
}