Merge pull request #152 from BentoBoxWorld/develop

Release 1.18.1
This commit is contained in:
tastybento 2021-12-25 09:18:06 -08:00 committed by GitHub
commit a5980b2386
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 306 additions and 259 deletions

24
pom.xml
View File

@ -57,14 +57,14 @@
<!-- Non-minecraft related dependencies -->
<powermock.version>2.0.9</powermock.version>
<!-- More visible way how to change dependency versions -->
<spigot.version>1.17.1-R0.1-SNAPSHOT</spigot.version>
<bentobox.version>1.17.1</bentobox.version>
<spigot.version>1.16.1-R0.1-SNAPSHOT</spigot.version>
<bentobox.version>1.18.0-SNAPSHOT</bentobox.version>
<!-- Revision variable removes warning about dynamic version -->
<revision>${build.version}-SNAPSHOT</revision>
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- This allows to change between versions. -->
<build.version>1.17.2</build.version>
<build.version>1.18.1</build.version>
<sonar.projectKey>BentoBoxWorld_Limits</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
@ -210,7 +210,23 @@
<version>3.0.0-M5</version>
<configuration>
<argLine>
--illegal-access=permit
--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
</argLine>
</configuration>
</plugin>

View File

@ -17,6 +17,7 @@ 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;
/**
@ -26,6 +27,7 @@ import world.bentobox.limits.listeners.JoinListener;
*/
public class Limits extends Addon {
private static final String LIMIT_NOT_SET = "Limit not set";
private Settings settings;
private List<GameModeAddon> gameModes;
private BlockLimitsListener blockLimitListener;
@ -134,8 +136,8 @@ public class Limits extends Addon {
private void registerPlaceholders(GameModeAddon gm) {
if (getPlugin().getPlaceholdersManager() == null) return;
Arrays.stream(Material.values())
.filter(m -> m.isBlock())
.forEach(m -> registerCountAndLimitPlaceholders(m, gm));
.filter(Material::isBlock)
.forEach(m -> registerCountAndLimitPlaceholders(m, gm));
}
/**
@ -148,8 +150,8 @@ public class Limits extends Addon {
* "Limits_bskyblock_island_hopper_count"
* "Limits_bskyblock_island_hopper_limit"
*
* @param m
* @param gm
* @param m material
* @param gm game mode
*/
private void registerCountAndLimitPlaceholders(Material m, GameModeAddon gm) {
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
@ -163,7 +165,7 @@ public class Limits extends Addon {
/**
* @param user - Used to identify the island the user belongs to
* @param m - The material we are trying to count on the island
* @param gm
* @param gm Game Mode Addon
* @return Number of blocks of the specified material on the given user's island
*/
private int getCount(@Nullable User user, Material m, GameModeAddon gm) {
@ -171,24 +173,30 @@ public class Limits extends Addon {
if (is == null) {
return 0;
}
return getBlockLimitListener().getIsland(gm.getIslands().getIsland(gm.getOverWorld(), user).getUniqueId()).
getBlockCount(m);
@Nullable IslandBlockCount ibc = getBlockLimitListener().getIsland(is.getUniqueId());
if (ibc == null) {
return 0;
}
return ibc.getBlockCount(m);
}
/**
* @param user - Used to identify the island the user belongs to
* @param m - The material whose limit we are querying
* @param gm
* @param gm Game Mode Addon
* @return The limit of the specified material on the given user's island
*/
private String getLimit(@Nullable User user, Material m, GameModeAddon gm) {
Island is = gm.getIslands().getIsland(gm.getOverWorld(), user);
if (is == null) {
return "Limit not set";
return LIMIT_NOT_SET;
}
int limit = getBlockLimitListener().getIsland(gm.getIslands().getIsland(gm.getOverWorld(), user).getUniqueId()).
getBlockLimit(m);
return limit == -1 ? "Limit not set" : String.valueOf(limit);
@Nullable IslandBlockCount ibc = getBlockLimitListener().getIsland(is.getUniqueId());
if (ibc == null) {
return LIMIT_NOT_SET;
}
int limit = ibc.getBlockLimit(m);
return limit == -1 ? LIMIT_NOT_SET : String.valueOf(limit);
}
}

View File

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

View File

@ -1,13 +1,7 @@
package world.bentobox.limits.commands;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.bukkit.Material;
@ -88,26 +82,30 @@ public class LimitTab implements Tab {
addEntityLimits(ibc, island);
addEntityGroupLimits(ibc, island);
// Sort
switch (sortBy) {
default:
Collections.sort(result, (o1, o2) -> o1.getName().compareTo(o2.getName()));
break;
case Z2A:
Collections.sort(result, (o1, o2) -> o2.getName().compareTo(o1.getName()));
break;
if (sortBy == SORT_BY.Z2A) {
result.sort((o1, o2) -> o2.getName().compareTo(o1.getName()));
} else {
result.sort(Comparator.comparing(PanelItem::getName));
}
}
private void addEntityGroupLimits(IslandBlockCount ibc, Island island) {
// Entity group limits
Map<EntityGroup, Integer> groupmap = addon.getSettings().getGroupLimitDefinitions().stream().collect(Collectors.toMap(e -> e, e -> e.getLimit()));
Map<String, EntityGroup> groupbyname = groupmap.keySet().stream().collect(Collectors.toMap(e -> e.getName(), e -> e));
Map<EntityGroup, Integer> groupMap = addon.getSettings().getGroupLimitDefinitions().stream().collect(Collectors.toMap(e -> e, EntityGroup::getLimit));
// Group by same loop up map
Map<String, EntityGroup> groupByName = groupMap.keySet().stream().collect(Collectors.toMap(EntityGroup::getName, e -> e));
// Merge in any permission-based limits
if (ibc != null) ibc.getEntityGroupLimits().entrySet().stream()
.filter(e -> groupbyname.containsKey(e.getKey()))
.forEach(e -> groupmap.put(groupbyname.get(e.getKey()), e.getValue()));
groupmap.forEach((v, limit) -> {
if (ibc == null) {
return;
}
ibc.getEntityGroupLimits().entrySet().stream()
.filter(e -> groupByName.containsKey(e.getKey()))
.forEach(e -> groupMap.put(groupByName.get(e.getKey()), e.getValue()));
// Update the group map for each group limit offset. If the value already exists add it
ibc.getEntityGroupLimitsOffset().forEach((key, value) ->
groupMap.put(groupByName.get(key), (groupMap.getOrDefault(groupByName.get(key), 0) + value)));
groupMap.forEach((v, limit) -> {
PanelItemBuilder pib = new PanelItemBuilder();
EntityType k = v.getTypes().iterator().next();
pib.name(v.getName());
@ -118,7 +116,7 @@ public class LimitTab implements Tab {
if (E2M.containsKey(k)) {
m = E2M.get(k);
} else if (k.isAlive()) {
m = Material.valueOf(k.toString() + "_SPAWN_EGG");
m = Material.valueOf(k + "_SPAWN_EGG");
} else {
// Regular material
m = Material.valueOf(k.toString());
@ -142,7 +140,11 @@ public class LimitTab implements Tab {
// Entity limits
Map<EntityType, Integer> map = new HashMap<>(addon.getSettings().getLimits());
// Merge in any permission-based limits
if (ibc != null) ibc.getEntityLimits().forEach(map::put);
if (ibc != null) {
map.putAll(ibc.getEntityLimits());
ibc.getEntityLimitsOffset().forEach((k,v) -> map.put(k, map.getOrDefault(k, 0) + v));
}
map.forEach((k,v) -> {
PanelItemBuilder pib = new PanelItemBuilder();
pib.name(Util.prettifyText(k.toString()));
@ -151,7 +153,7 @@ public class LimitTab implements Tab {
if (E2M.containsKey(k)) {
m = E2M.get(k);
} else if (k.isAlive()) {
m = Material.valueOf(k.toString() + "_SPAWN_EGG");
m = Material.valueOf(k + "_SPAWN_EGG");
} else {
// Regular material
m = Material.valueOf(k.toString());
@ -180,11 +182,12 @@ public class LimitTab implements Tab {
pib.icon(B2M.getOrDefault(en.getKey(), en.getKey()));
int count = ibc == null ? 0 : ibc.getBlockCounts().getOrDefault(en.getKey(), 0);
String color = count >= en.getValue() ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color");
int value = en.getValue() + (ibc == null ? 0 : ibc.getBlockLimitsOffset().getOrDefault(en.getKey(), 0));
String color = count >= value ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color");
pib.description(color
+ user.getTranslation("island.limits.block-limit-syntax",
TextVariables.NUMBER, String.valueOf(count),
"[limit]", String.valueOf(en.getValue())));
"[limit]", String.valueOf(value)));
result.add(pib.build());
}
}

View File

@ -37,10 +37,9 @@ public class LimitsCalc {
private IslandBlockCount ibc;
private final Map<Material, AtomicInteger> blockCount;
private final User sender;
private final Set<Pair<Integer, Integer>> chunksToScan;
private int count;
private int chunksToScanCount;
private BentoBox plugin;
private final int chunksToScanCount;
private final BentoBox plugin;
/**
@ -56,13 +55,13 @@ public class LimitsCalc {
this.addon = addon;
this.island = instance.getIslands().getIsland(world, targetPlayer);
this.bll = addon.getBlockLimitListener();
this.ibc = bll.getIsland(island.getUniqueId());
this.ibc = bll.getIsland(Objects.requireNonNull(island).getUniqueId());
blockCount = new EnumMap<>(Material.class);
this.sender = sender;
this.world = world;
// Get chunks to scan
chunksToScan = getChunksToScan(island);
Set<Pair<Integer, Integer>> chunksToScan = getChunksToScan(island);
count = 0;
boolean isNether = plugin.getIWM().isNetherGenerate(world) && plugin.getIWM().isNetherIslands(world);
@ -76,7 +75,7 @@ public class LimitsCalc {
});
}
private void asyncScan(World world2, Pair<Integer, Integer> c) {
@ -141,7 +140,7 @@ public class LimitsCalc {
private void tidyUp() {
if (ibc == null) {
ibc = new IslandBlockCount();
ibc = new IslandBlockCount(island.getUniqueId(), plugin.getIWM().getAddon(world).map(a -> a.getDescription().getName()).orElse("default"));
}
ibc.setBlockCounts(blockCount.entrySet().stream()
.collect(Collectors.toMap(

View File

@ -2,6 +2,7 @@ package world.bentobox.limits.events;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
@ -21,6 +22,16 @@ public class LimitsJoinPermCheckEvent extends BentoBoxEvent implements Cancellab
private IslandBlockCount ibc;
private boolean cancel;
private boolean ignorePerms;
private static final HandlerList handlers = new HandlerList();
@Override
public @NonNull HandlerList getHandlers() {
return getHandlerList();
}
public static HandlerList getHandlerList() {
return handlers;
}
/**
* Fired when a player joins the server and before limit settings for their island are changed based
@ -99,7 +110,7 @@ public class LimitsJoinPermCheckEvent extends BentoBoxEvent implements Cancellab
/**
* Ignore player's perms. This differs to canceling the event in that the IslandBlockCount will be used if given via
* {@link setIbc(IslandBlockCount ibc)}
* {@link #setIbc(IslandBlockCount ibc)}
* @param ignorePerms the ignorePerms to set
*/
public void setIgnorePerms(boolean ignorePerms) {

View File

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

View File

@ -164,7 +164,7 @@ public class BlockLimitsListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onTurtleEggBreak(PlayerInteractEvent e) {
if (e.getAction().equals(Action.PHYSICAL) && e.getClickedBlock().getType().equals(Material.TURTLE_EGG)) {
if (e.getAction().equals(Action.PHYSICAL) && e.getClickedBlock() != null && e.getClickedBlock().getType().equals(Material.TURTLE_EGG)) {
handleBreak(e, e.getClickedBlock());
}
}
@ -296,8 +296,8 @@ public class BlockLimitsListener implements Listener {
return Material.PLAYER_HEAD;
} else if (mat == Material.DRAGON_WALL_HEAD) {
return Material.DRAGON_HEAD;
} else if (mat != null && mat == Material.getMaterial("BAMBOO_SAPLING")) {
return Material.getMaterial("BAMBOO");
} else if (mat == Material.BAMBOO_SAPLING) {
return Material.BAMBOO;
} else if (mat == Material.PISTON_HEAD || mat == Material.MOVING_PISTON) {
TechnicalPiston tp = (TechnicalPiston) b;
if (tp.getType() == TechnicalPiston.Type.NORMAL) {
@ -314,7 +314,6 @@ public class BlockLimitsListener implements Listener {
*
* @param b - block
* @param add - true to add a block, false to remove
* @param changeTo - material this block will become
* @return limit amount if over limit, or -1 if no limitation
*/
private int process(Block b, boolean add) {
@ -329,6 +328,10 @@ public class BlockLimitsListener implements Listener {
// Invalid world
return -1;
}
// Ignore the center block - usually bedrock, but for AOneBlock it's the magic block
if (addon.getConfig().getBoolean("ignore-center-block", true) && i.getCenter().equals(b.getLocation())) {
return -1;
}
islandCountMap.putIfAbsent(id, new IslandBlockCount(id, gameMode));
if (add) {
// Check limit
@ -411,14 +414,14 @@ public class BlockLimitsListener implements Listener {
// Merge limits
Map<Material, Integer> result = new EnumMap<>(Material.class);
// Default
defaultLimitMap.forEach(result::put);
result.putAll(defaultLimitMap);
// World
if (worldLimitMap.containsKey(w)) {
worldLimitMap.get(w).forEach(result::put);
result.putAll(worldLimitMap.get(w));
}
// Island
if (islandCountMap.containsKey(id)) {
islandCountMap.get(id).getBlockLimits().forEach(result::put);
result.putAll(islandCountMap.get(id).getBlockLimits());
}
return result;
}

View File

@ -2,7 +2,6 @@ package world.bentobox.limits.listeners;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -17,6 +16,7 @@ import org.bukkit.Tag;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Animals;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
@ -27,6 +27,7 @@ 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;
@ -43,17 +44,7 @@ public class EntityLimitListener implements Listener {
private static final String MOD_BYPASS = "mod.bypass";
private final Limits addon;
private final List<UUID> justSpawned = new ArrayList<>();
private static final List<BlockFace> CARDINALS;
static {
List<BlockFace> cardinals = new ArrayList<>();
cardinals.add(BlockFace.UP);
cardinals.add(BlockFace.NORTH);
cardinals.add(BlockFace.SOUTH);
cardinals.add(BlockFace.EAST);
cardinals.add(BlockFace.WEST);
cardinals.add(BlockFace.DOWN);
CARDINALS = Collections.unmodifiableList(cardinals);
}
private static final List<BlockFace> CARDINALS = List.of(BlockFace.UP, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST, BlockFace.DOWN);
/**
* Handles entity and natural limitations
@ -61,7 +52,6 @@ public class EntityLimitListener implements Listener {
*/
public EntityLimitListener(Limits addon) {
this.addon = addon;
justSpawned.clear();
}
/**
@ -71,46 +61,39 @@ public class EntityLimitListener implements Listener {
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onMinecart(VehicleCreateEvent e) {
// Return if not in a known world
if (!addon.inGameModeWorld(e.getVehicle().getWorld())) {
return;
}
// Debounce
if (justSpawned.contains(e.getVehicle().getUniqueId())) {
justSpawned.remove(e.getVehicle().getUniqueId());
return;
}
// If someone in that area has the bypass permission, allow the spawning
for (Entity entity : Objects.requireNonNull(e.getVehicle().getLocation().getWorld()).getNearbyEntities(e.getVehicle().getLocation(), 5, 5, 5)) {
if (entity instanceof Player) {
Player player = (Player)entity;
boolean bypass = (player.isOp() || player.hasPermission(addon.getPlugin().getIWM().getPermissionPrefix(e.getVehicle().getWorld()) + MOD_BYPASS));
// Check island
addon.getIslands().getProtectedIslandAt(e.getVehicle().getLocation()).ifPresent(island -> {
// Ignore spawn
if (island.isSpawn()) {
return;
}
// Check if the player is at the limit
AtLimitResult res;
if (!bypass && (res = atLimit(island, e.getVehicle())).hit()) {
e.setCancelled(true);
for (Entity ent : e.getVehicle().getLocation().getWorld().getNearbyEntities(e.getVehicle().getLocation(), 5, 5, 5)) {
if (ent instanceof Player) {
((Player) ent).updateInventory();
if (res.getTypelimit() != null) {
User.getInstance(ent).notify("entity-limits.hit-limit", "[entity]",
Util.prettifyText(e.getVehicle().getType().toString()),
TextVariables.NUMBER, String.valueOf(res.getTypelimit().getValue()));
} else {
User.getInstance(ent).notify("entity-limits.hit-limit", "[entity]",
res.getGrouplimit().getKey().getName() + " (" + res.getGrouplimit().getKey().getTypes().stream().map(x -> Util.prettifyText(x.toString())).collect(Collectors.joining(", ")) + ")",
TextVariables.NUMBER, String.valueOf(res.getGrouplimit().getValue()));
}
}
}
}
});
// Check island
addon.getIslands().getProtectedIslandAt(e.getVehicle().getLocation())
// Ignore spawn
.filter(i -> !i.isSpawn())
.ifPresent(island -> {
// Check if the player is at the limit
AtLimitResult res = atLimit(island, e.getVehicle());
if (res.hit()) {
e.setCancelled(true);
this.tellPlayers(e.getVehicle().getLocation(), e.getVehicle(), SpawnReason.MOUNT, res);
}
});
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onBreed(final EntityBreedEvent e) {
if (addon.inGameModeWorld(e.getEntity().getWorld())
&& e.getBreeder() != null
&& e.getBreeder() instanceof Player p
&& !(p.isOp() || p.hasPermission(addon.getPlugin().getIWM().getPermissionPrefix(e.getEntity().getWorld()) + MOD_BYPASS))
&& !checkLimit(e, e.getEntity(), SpawnReason.BREEDING, false)
&& e.getFather() instanceof Animals f && e.getMother() instanceof Animals m) {
f.setLoveModeTicks(0);
m.setLoveModeTicks(0);
}
}
@ -124,53 +107,21 @@ public class EntityLimitListener implements Listener {
justSpawned.remove(e.getEntity().getUniqueId());
return;
}
boolean bypass = false;
// Check why it was spawned
switch (e.getSpawnReason()) {
// These reasons are due to a player being involved (usually) so there may be a bypass
case BREEDING:
case BUILD_IRONGOLEM:
case BUILD_SNOWMAN:
case BUILD_WITHER:
case CURED:
case EGG:
case SPAWNER_EGG:
bypass = checkByPass(e.getLocation());
break;
case SHOULDER_ENTITY:
if (e.getSpawnReason().equals(SpawnReason.SHOULDER_ENTITY) || e.getSpawnReason().equals(SpawnReason.BREEDING)) {
// Special case - do nothing - jumping around spawns parrots as they drop off player's shoulder
// Ignore breeding because it's handled in the EntityBreedEvent listener
return;
default:
// Other natural reasons
break;
}
// Some checks can be done async, some not
switch (e.getSpawnReason()) {
case BUILD_SNOWMAN:
case BUILD_IRONGOLEM:
checkLimit(e, e.getEntity(), e.getSpawnReason(), bypass, addon.getSettings().isAsyncGolums());
default:
if (e.getSpawnReason().equals(SpawnReason.BUILD_SNOWMAN) || e.getSpawnReason().equals(SpawnReason.BUILD_IRONGOLEM)) {
checkLimit(e, e.getEntity(), e.getSpawnReason(), addon.getSettings().isAsyncGolums());
} else {
// Check limit sync
checkLimit(e, e.getEntity(), e.getSpawnReason(), bypass, false);
break;
checkLimit(e, e.getEntity(), e.getSpawnReason(), false);
}
}
private boolean checkByPass(Location l) {
// If someone in that area has the bypass permission, allow the spawning
for (Entity entity : Objects.requireNonNull(l.getWorld()).getNearbyEntities(l, 5, 5, 5)) {
if (entity instanceof Player) {
Player player = (Player)entity;
if (player.isOp() || player.hasPermission(addon.getPlugin().getIWM().getPermissionPrefix(l.getWorld()) + MOD_BYPASS)) {
return true;
}
}
}
return false;
}
/**
* handles paintings and item frames
* @param e - event
@ -205,61 +156,56 @@ public class EntityLimitListener implements Listener {
/**
* Check if a creature is allowed to spawn or not
* @param e - CreatureSpawnEvent
* @param bypass - true if the player involved can bypass the checks
* @param async - true if check can be done async, false if not
* @return true if allowed or asycn, false if not.
*/
private void checkLimit(Cancellable c, LivingEntity e, SpawnReason reason, boolean bypass, boolean async) {
private boolean checkLimit(Cancellable c, LivingEntity e, SpawnReason reason, boolean async) {
Location l = e.getLocation();
if (async) {
c.setCancelled(true);
}
processIsland(c, e, l, reason, bypass, async);
return processIsland(c, e, l, reason, async);
}
private void processIsland(Cancellable c, LivingEntity e, Location l, SpawnReason reason, boolean bypass, boolean async) {
addon.getIslands().getIslandAt(e.getLocation()).ifPresent(island -> {
// Check if creature is allowed to spawn or not
AtLimitResult res = atLimit(island, e);
if (bypass || island.isSpawn() || !res.hit()) {
// Allowed
if (async) {
Bukkit.getScheduler().runTask(BentoBox.getInstance(), () -> {
l.getWorld().spawn(l, e.getClass(), entity -> preSpawn(entity, reason, l));
});
} // else do nothing
private boolean processIsland(Cancellable c, LivingEntity e, Location l, SpawnReason reason, boolean async) {
if (addon.getIslands().getIslandAt(e.getLocation()).isEmpty()) {
return true;
}
Island island = addon.getIslands().getIslandAt(e.getLocation()).get();
// Check if creature is allowed to spawn or not
AtLimitResult res = atLimit(island, e);
if (island.isSpawn() || !res.hit()) {
// Allowed
if (async) {
Bukkit.getScheduler().runTask(BentoBox.getInstance(), () -> l.getWorld().spawn(l, e.getClass(), entity -> preSpawn(entity, reason, l)));
} // else do nothing
} else {
if (async) {
e.remove();
} else {
if (async) {
e.remove();
} else {
c.setCancelled(true);
}
// If the reason is anything but because of a spawner then tell players within range
tellPlayers(c, l, e, reason, res);
c.setCancelled(true);
}
});
// If the reason is anything but because of a spawner then tell players within range
tellPlayers(l, e, reason, res);
return false;
}
return true;
}
private void preSpawn(Entity entity, SpawnReason reason, Location l) {
justSpawned.add(entity.getUniqueId());
// Check for entities that need cleanup
switch (reason) {
case BUILD_IRONGOLEM:
detectIronGolem(l);
break;
case BUILD_SNOWMAN:
detectSnowman(l);
break;
case BUILD_WITHER:
case BUILD_IRONGOLEM -> detectIronGolem(l);
case BUILD_SNOWMAN -> detectSnowman(l);
case BUILD_WITHER -> {
detectWither(l);
// Create explosion
l.getWorld().createExplosion(l, 7F, true, true, entity);
break;
default:
break;
}
default -> {
// Do nothing
}
}
}
@ -379,28 +325,37 @@ public class EntityLimitListener implements Listener {
return Tag.WITHER_SUMMON_BASE_BLOCKS.isTagged(body.getType());
}
private void tellPlayers(Cancellable e, Location l, LivingEntity entity, SpawnReason reason, AtLimitResult res) {
if (!reason.equals(SpawnReason.SPAWNER) && !reason.equals(SpawnReason.NATURAL)
&& !reason.equals(SpawnReason.INFECTION) && !reason.equals(SpawnReason.NETHER_PORTAL)
&& !reason.equals(SpawnReason.REINFORCEMENTS) && !reason.equals(SpawnReason.SLIME_SPLIT)) {
World w = l.getWorld();
if (w == null) return;
Bukkit.getScheduler().runTask(addon.getPlugin(), () -> {
for (Entity ent : w.getNearbyEntities(l, 5, 5, 5)) {
if (ent instanceof Player) {
if (res.getTypelimit() != null) {
User.getInstance(ent).notify("entity-limits.hit-limit", "[entity]",
Util.prettifyText(entity.getType().toString()),
TextVariables.NUMBER, String.valueOf(res.getTypelimit().getValue()));
} else {
User.getInstance(ent).notify("entity-limits.hit-limit", "[entity]",
res.getGrouplimit().getKey().getName() + " (" + res.getGrouplimit().getKey().getTypes().stream().map(x -> Util.prettifyText(x.toString())).collect(Collectors.joining(", ")) + ")",
TextVariables.NUMBER, String.valueOf(res.getGrouplimit().getValue()));
}
/**
* Tell players within a 5 x 5 x 5 radius that the spawning was denied. Informing happens 1 tick after event
* @param l location
* @param entity entity spawned
* @param reason reason - some reasons are not reported
* @param res at limit result
*/
private void tellPlayers(Location l, Entity entity, SpawnReason reason, AtLimitResult res) {
if (reason.equals(SpawnReason.SPAWNER) || reason.equals(SpawnReason.NATURAL)
|| reason.equals(SpawnReason.INFECTION) || reason.equals(SpawnReason.NETHER_PORTAL)
|| reason.equals(SpawnReason.REINFORCEMENTS) || reason.equals(SpawnReason.SLIME_SPLIT)) {
return;
}
World w = l.getWorld();
if (w == null) return;
Bukkit.getScheduler().runTask(addon.getPlugin(), () -> {
for (Entity ent : w.getNearbyEntities(l, 5, 5, 5)) {
if (ent instanceof Player p) {
p.updateInventory();
if (res.getTypelimit() != null) {
User.getInstance(p).notify("entity-limits.hit-limit", "[entity]",
Util.prettifyText(entity.getType().toString()),
TextVariables.NUMBER, String.valueOf(res.getTypelimit().getValue()));
} else {
User.getInstance(p).notify("entity-limits.hit-limit", "[entity]",
res.getGrouplimit().getKey().getName() + " (" + res.getGrouplimit().getKey().getTypes().stream().map(x -> Util.prettifyText(x.toString())).collect(Collectors.joining(", ")) + ")",
TextVariables.NUMBER, String.valueOf(res.getGrouplimit().getValue()));
}
}
});
}
}
});
}
@ -433,6 +388,7 @@ public class EntityLimitListener implements Listener {
.forEach(group -> groupsLimits.put(group, group.getLimit()));
}
if (limitAmount < 0 && groupsLimits.isEmpty()) return new AtLimitResult();
// We have to count the entities
if (limitAmount >= 0)
{
@ -440,18 +396,17 @@ public class EntityLimitListener implements Listener {
.filter(e -> e.getType().equals(ent.getType()))
.filter(e -> island.inIslandSpace(e.getLocation()))
.count();
if (count >= limitAmount)
if (count >= limitAmount) {
return new AtLimitResult(ent.getType(), limitAmount);
}
}
// Merge in any permission-based limits
if (addon.getBlockLimitListener().getIsland(island.getUniqueId()) != null) {
Map<String, EntityGroup> groupbyname = groupsLimits.keySet().stream().collect(Collectors.toMap(e -> e.getName(), e -> e));
Map<String, EntityGroup> groupbyname = groupsLimits.keySet().stream().collect(Collectors.toMap(EntityGroup::getName, e -> e));
addon.getBlockLimitListener().getIsland(island.getUniqueId()).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)
@ -465,7 +420,7 @@ public class EntityLimitListener implements Listener {
return new AtLimitResult();
}
class AtLimitResult {
static class AtLimitResult {
private Map.Entry<EntityType, Integer> typelimit;
private Map.Entry<EntityGroup, Integer> grouplimit;

View File

@ -20,6 +20,7 @@ import org.bukkit.permissions.PermissionAttachmentInfo;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
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;
@ -60,7 +61,7 @@ public class JoinListener implements Listener {
ibc.getBlockLimits().clear();
}
for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) {
if (!perms.getValue()
if (!perms.getValue()
|| !perms.getPermission().startsWith(permissionPrefix)
|| badSyntaxCheck(perms, player.getName(), permissionPrefix)) {
continue;
@ -93,7 +94,9 @@ public class JoinListener implements Listener {
runNullCheckAndSet(ibc, l);
}
// Check removed permissions
if (ibc == null) {
BentoBox.getInstance().logDebug("IBC is still null");
}
// 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);
}
@ -145,7 +148,7 @@ public class JoinListener implements Listener {
ibc.setEntityLimit(et, Math.max(ibc.getEntityLimit(et), value));
}
}
}
private void logError(String name, String perm, String error) {

View File

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

View File

@ -20,16 +20,16 @@ import world.bentobox.bentobox.database.objects.Table;
public class IslandBlockCount implements DataObject {
@Expose
private String uniqueId = "";
private String uniqueId;
@Expose
private String gameMode = "";
private String gameMode;
@Expose
private Map<Material, Integer> blockCounts = new EnumMap<>(Material.class);
private boolean changed;
/**
* Permission based limits
*/
@ -39,9 +39,12 @@ public class IslandBlockCount implements DataObject {
private Map<EntityType, Integer> entityLimits = new EnumMap<>(EntityType.class);
@Expose
private Map<String, Integer> entityGroupLimits = new HashMap<>();
// Required for YAML database
public IslandBlockCount() {}
@Expose
private Map<Material, Integer> blockLimitsOffset = new EnumMap<>(Material.class);
@Expose
private Map<EntityType, Integer> entityLimitsOffset = new EnumMap<>(EntityType.class);
@Expose
private Map<String, Integer> entityGroupLimitsOffset = new HashMap<>();
/**
* Create an island block count object
@ -75,6 +78,9 @@ public class IslandBlockCount implements DataObject {
* @return the blockCount
*/
public Map<Material, Integer> getBlockCounts() {
if (blockCounts == null) {
blockCounts = new EnumMap<>(Material.class);
}
return blockCounts;
}
@ -131,7 +137,7 @@ public class IslandBlockCount implements DataObject {
*/
public boolean isAtLimit(Material m) {
// Check island limits first
return blockLimits.containsKey(m) && blockCounts.getOrDefault(m, 0) >= blockLimits.get(m);
return blockLimits.containsKey(m) && blockCounts.getOrDefault(m, 0) >= getBlockLimit(m);
}
public boolean isBlockLimited(Material m) {
@ -159,7 +165,7 @@ public class IslandBlockCount implements DataObject {
* @return limit or -1 for unlimited
*/
public Integer getBlockLimit(Material m) {
return blockLimits.getOrDefault(m, -1);
return blockLimits.getOrDefault(m, -1) + getBlockLimitsOffset().getOrDefault(m, 0);
}
/**
@ -222,7 +228,7 @@ public class IslandBlockCount implements DataObject {
* @return limit or -1 for unlimited
*/
public int getEntityLimit(EntityType t) {
return entityLimits.getOrDefault(t, -1);
return entityLimits.getOrDefault(t, -1) + getEntityLimitsOffset().getOrDefault(t, 0);
}
/**
@ -232,7 +238,7 @@ public class IslandBlockCount implements DataObject {
entityLimits.clear();
setChanged();
}
/**
* @return the entityGroupLimits
*/
@ -247,7 +253,7 @@ public class IslandBlockCount implements DataObject {
this.entityGroupLimits = entityGroupLimits;
setChanged();
}
/**
* Set an island-specific entity group limit
* @param name - entity group
@ -257,16 +263,16 @@ public class IslandBlockCount implements DataObject {
entityGroupLimits.put(name, limit);
setChanged();
}
/**
* Get the limit for an entity group
* @param name - entity group
* @return limit or -1 for unlimited
*/
public int getEntityGroupLimit(String name) {
return entityGroupLimits.getOrDefault(name, -1);
return entityGroupLimits.getOrDefault(name, -1) + getEntityGroupLimitsOffset().getOrDefault(name, 0);
}
/**
* Clear all island-specific entity group limits
*/
@ -288,11 +294,68 @@ public class IslandBlockCount implements DataObject {
public void setChanged(boolean changed) {
this.changed = changed;
}
/**
* Mark changed
*/
public void setChanged() {
this.changed = true;
}
/**
* @return the blockLimitsOffset
*/
public Map<Material, Integer> getBlockLimitsOffset() {
if (blockLimitsOffset == null) {
blockLimitsOffset = new EnumMap<>(Material.class);
}
return blockLimitsOffset;
}
/**
* Set an offset to a block limit. This will increase/decrease the value of the limit.
* @param m material
* @param blockLimitsOffset the blockLimitsOffset to set
*/
public void setBlockLimitsOffset(Material m, Integer blockLimitsOffset) {
getBlockLimitsOffset().put(m, blockLimitsOffset);
}
/**
* @return the entityLimitsOffset
*/
public Map<EntityType, Integer> getEntityLimitsOffset() {
if (entityLimitsOffset == null) {
entityLimitsOffset = new EnumMap<>(EntityType.class);
}
return entityLimitsOffset;
}
/**
* Set an offset to an entity limit. This will increase/decrease the value of the limit.
* @param t Entity Type
* @param entityLimitsOffset the entityLimitsOffset to set
*/
public void setEntityLimitsOffset(EntityType t, Integer entityLimitsOffset) {
this.getEntityLimitsOffset().put(t, entityLimitsOffset);
}
/**
* @return the entityGroupLimitsOffset
*/
public Map<String, Integer> getEntityGroupLimitsOffset() {
if (entityGroupLimitsOffset == null) {
entityGroupLimitsOffset = new HashMap<>();
}
return entityGroupLimitsOffset;
}
/**
* 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);
}
}

View File

@ -1,7 +1,7 @@
name: Limits
main: world.bentobox.limits.Limits
version: ${version}${build.number}
api-version: 1.17
api-version: 1.16.5
authors: tastybento

View File

@ -4,6 +4,10 @@ gamemodes:
- BSkyBlock
- CaveBlock
# Ignore this island's center block. For most worlds, this is bedrock, but for AOneBlock it is
# the magic block, so ignoring it from limits makes sense.
ignore-center-block: true
# Permissions
# Island owners can be given permissions that override all general settings
# Format is GAME-MODE-NAME.island.limit.MATERIAL.LIMIT

View File

@ -1,7 +1,7 @@
package bentobox.addon.limits.listeners;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@ -11,12 +11,6 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
@ -86,7 +80,7 @@ public class JoinListenerTest {
when(addon.getGameModePermPrefix(any())).thenReturn("bskyblock.");
when(addon.getSettings()).thenReturn(settings);
// Settings
when(settings.getGroupLimitDefinitions()).thenReturn(new ArrayList<>(Arrays.asList(new Settings.EntityGroup("friendly", new HashSet<>(), -1))));
when(settings.getGroupLimitDefinitions()).thenReturn(new ArrayList<>(List.of(new Settings.EntityGroup("friendly", new HashSet<>(), -1))));
// Island Manager
when(im.hasIsland(any(), any(UUID.class))).thenReturn(true);
when(island.getUniqueId()).thenReturn("unique_id");

View File

@ -55,7 +55,7 @@ public class LimitTabTest {
private Settings settings;
@Before
public void setUp() throws Exception {
public void setUp() {
// Island
when(island.getWorld()).thenReturn(world);
// Addon
@ -74,11 +74,11 @@ public class LimitTabTest {
when(world.getEntities()).thenReturn(Collections.singletonList(entity));
when(nether.getEntities()).thenReturn(Collections.singletonList(entity));
when(end.getEntities()).thenReturn(Collections.singletonList(entity));
lp = new LimitTab(addon, new IslandBlockCount(), Collections.emptyMap(), island, world, null, LimitTab.SORT_BY.A2Z);
lp = new LimitTab(addon, new IslandBlockCount("", ""), Collections.emptyMap(), island, world, null, LimitTab.SORT_BY.A2Z);
}
@After
public void tearDown() throws Exception {
public void tearDown() {
}
@Test

View File

@ -45,7 +45,6 @@ public class EntityLimitListenerTest {
private LivingEntity ent;
@Mock
private BlockLimitsListener bll;
private Settings settings;
@Mock
private World world;
private List<Entity> collection;
@ -63,7 +62,7 @@ public class EntityLimitListenerTest {
when(island.getUniqueId()).thenReturn(UUID.randomUUID().toString());
when(island.inIslandSpace(any(Location.class))).thenReturn(true);
ibc = new IslandBlockCount();
ibc = new IslandBlockCount("","");
when(bll.getIsland(anyString())).thenReturn(ibc);
when(addon.getBlockLimitListener()).thenReturn(bll);
@ -71,7 +70,7 @@ public class EntityLimitListenerTest {
config.load("src/main/resources/config.yml");
// Settings
when(addon.getConfig()).thenReturn(config);
settings = new Settings(addon);
Settings settings = new Settings(addon);
when(addon.getSettings()).thenReturn(settings);
// World
@ -87,7 +86,7 @@ public class EntityLimitListenerTest {
}
@After
public void tearDown() throws Exception {
public void tearDown() {
}
/**