#176 adds support for custom blocks with ItemsAdder

This commit is contained in:
tastybento 2023-04-14 15:37:34 -07:00
parent 68f696a46c
commit ff813e3e53
5 changed files with 251 additions and 15 deletions

10
pom.xml
View File

@ -125,6 +125,10 @@
<id>codemc-repo</id>
<url>https://repo.codemc.org/repository/maven-public/</url>
</repository>
<repository>
<id>jitpack-repo</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
@ -160,6 +164,12 @@
<version>${bentobox.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.LoneDev6</groupId>
<artifactId>api-itemsadder</artifactId>
<version>3.4.1-r4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View File

@ -1,6 +1,14 @@
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;
@ -15,13 +23,27 @@ 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.BlockGrowEvent;
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.bukkit.plugin.Plugin;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import dev.lone.itemsadder.api.CustomBlock;
import world.bentobox.bentobox.api.events.island.IslandDeleteEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
@ -59,7 +81,10 @@ public class BlockLimitsListener implements Listener {
private final Map<String, Integer> saveMap = new HashMap<>();
private final Database<IslandBlockCount> handler;
private final Map<World, Map<Material, Integer>> worldLimitMap = new HashMap<>();
//private final Map<World, Map<String, Integer>> customWorldLimitMap = new HashMap<>();
private Map<Material, Integer> defaultLimitMap = new EnumMap<>(Material.class);
private Map<String, Integer> defaultCustomLimitMap = new HashMap<>();
private Plugin itemsAdder;
public BlockLimitsListener(Limits addon) {
this.addon = addon;
@ -89,6 +114,11 @@ public class BlockLimitsListener implements Listener {
ConfigurationSection limitConfig = addon.getConfig().getConfigurationSection("blocklimits");
defaultLimitMap = loadLimits(Objects.requireNonNull(limitConfig));
}
// Load custom blocks
if (addon.getConfig().isConfigurationSection("customblocklimits")) {
ConfigurationSection limitConfig = addon.getConfig().getConfigurationSection("customblocklimits");
defaultCustomLimitMap = loadCustomLimits(Objects.requireNonNull(limitConfig));
}
// Load specific worlds
if (addon.getConfig().isConfigurationSection("worlds")) {
@ -126,6 +156,21 @@ public class BlockLimitsListener implements Listener {
return mats;
}
/**
* Loads custom limit map from configuration section
*
* @param cs - configuration section
* @return limit map
*/
private Map<String, Integer> loadCustomLimits(ConfigurationSection cs) {
Map<String, Integer> mats = new HashMap<>();
for (String material : cs.getKeys(false)) {
mats.put(material, cs.getInt(material));
addon.log("Limit " + material + " to " + cs.getInt(material));
}
return mats;
}
/**
* Save the count database completely
@ -137,7 +182,7 @@ public class BlockLimitsListener implements Listener {
// Player-related events
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onBlock(BlockPlaceEvent e) {
notify(e, User.getInstance(e.getPlayer()), process(e.getBlock(), true), e.getBlock().getType());
notify(e, User.getInstance(e.getPlayer()), process(e.getBlock(), true), e.getBlock());
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
@ -187,7 +232,7 @@ public class BlockLimitsListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onBlock(BlockMultiPlaceEvent e) {
notify(e, User.getInstance(e.getPlayer()), process(e.getBlock(), true), e.getBlock().getType());
notify(e, User.getInstance(e.getPlayer()), process(e.getBlock(), true), e.getBlock());
}
/**
@ -197,10 +242,15 @@ public class BlockLimitsListener implements Listener {
* @param limit maximum limit allowed
* @param m material
*/
private void notify(Cancellable e, User user, int limit, Material m) {
private void notify(Cancellable e, User user, int limit, Block block) {
if (limit > -1) {
String name = Util.prettifyText(block.getType().toString());
if (this.itemsAdder != null) {
CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block);
name = customBlock.getDisplayName();
}
user.notify("block-limits.hit-limit",
"[material]", Util.prettifyText(m.toString()),
"[material]", name,
TextVariables.NUMBER, String.valueOf(limit));
e.setCancelled(true);
}
@ -318,6 +368,8 @@ public class BlockLimitsListener implements Listener {
* @return limit amount if over limit, or -1 if no limitation
*/
private int process(Block b, boolean add) {
// Check for custom block handlers
checkCustom();
if (DO_NOT_COUNT.contains(fixMaterial(b.getBlockData())) || !addon.inGameModeWorld(b.getWorld())) {
return -1;
}
@ -335,12 +387,27 @@ public class BlockLimitsListener implements Listener {
}
islandCountMap.putIfAbsent(id, new IslandBlockCount(id, gameMode));
if (add) {
// Check limit
int limit = checkLimit(b.getWorld(), fixMaterial(b.getBlockData()), id);
if (limit > -1) {
return limit;
// Check if custom block
if (itemsAdder != null ) {
CustomBlock customBlock = CustomBlock.byAlreadyPlaced(b);
if(customBlock != null) {
// Custom block
// Check limit
int limit = checkCustomLimit(b.getWorld(), b, id);
if (limit > -1) {
return limit;
}
islandCountMap.get(id).add(b);
}
} else {
// Check limit
int limit = checkLimit(b.getWorld(), fixMaterial(b.getBlockData()), id);
if (limit > -1) {
return limit;
}
islandCountMap.get(id).add(fixMaterial(b.getBlockData()));
}
islandCountMap.get(id).add(fixMaterial(b.getBlockData()));
} else {
if (islandCountMap.containsKey(id)) {
islandCountMap.get(id).remove(fixMaterial(b.getBlockData()));
@ -351,6 +418,11 @@ public class BlockLimitsListener implements Listener {
}).orElse(-1);
}
private void checkCustom() {
itemsAdder = Bukkit.getPluginManager().getPlugin("ItemsAdder");
}
/**
* Removed a block from any island limit count
* @param b - block to remove
@ -364,10 +436,17 @@ public class BlockLimitsListener implements Listener {
// Invalid world
return;
}
islandCountMap.computeIfAbsent(id, k -> new IslandBlockCount(id, gameMode)).remove(fixMaterial(b.getBlockData()));
updateSaveMap(id);
// Check for custom block
if (this.itemsAdder != null && CustomBlock.byAlreadyPlaced(b) != null) {
islandCountMap.computeIfAbsent(id, k -> new IslandBlockCount(id, gameMode)).remove(b);
updateSaveMap(id);
} else {
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) {
@ -404,6 +483,37 @@ public class BlockLimitsListener implements Listener {
return -1;
}
/**
* Check if this custom block is at its limit for world on this island
*
* @param w - world
* @param block - custom block
* @param id - island id
* @return limit amount if at limit or -1 if no limit
*/
private int checkCustomLimit(World w, Block block, String islandId) {
// Check island limits
IslandBlockCount ibc = islandCountMap.get(islandId);
if (ibc.isCustomBlockLimited(block)) {
return ibc.isAtLimit(block) ? ibc.getBlockLimit(block) : -1;
}
CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block);
String id = Objects.requireNonNull(customBlock).getNamespacedID();
/* NOT SUPPORTED YET
// Check specific world limits
if (customWorldLimitMap.containsKey(w) && customWorldLimitMap.get(w).containsKey(id)) {
// Material is overridden in world
return ibc.isAtLimit(block, worldLimitMap.get(w).get(id)) ? worldLimitMap.get(w).get(id) : -1; // TODO Add perm offset
}
*/
// Check default limit map
if (defaultCustomLimitMap.containsKey(id) && ibc.isAtLimit(block, defaultCustomLimitMap.get(id))) {
return defaultCustomLimitMap.get(id);// TODO add perm offset
}
// No limit
return -1;
}
/**
* Gets an aggregate map of the limits for this island
*
@ -427,7 +537,7 @@ public class BlockLimitsListener implements Listener {
// Add offsets to the every limit.
islandBlockCount.getBlockLimitsOffset().forEach((material, offset) ->
result.put(material, result.getOrDefault(material, 0) + offset));
result.put(material, result.getOrDefault(material, 0) + offset));
}
return result;
}

View File

@ -3,12 +3,15 @@ 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.block.Block;
import org.bukkit.entity.EntityType;
import com.google.gson.annotations.Expose;
import dev.lone.itemsadder.api.CustomBlock;
import world.bentobox.bentobox.database.objects.DataObject;
import world.bentobox.bentobox.database.objects.Table;
@ -28,6 +31,9 @@ public class IslandBlockCount implements DataObject {
@Expose
private Map<Material, Integer> blockCounts = new EnumMap<>(Material.class);
@Expose
private Map<String, Integer> customBlockCounts = new HashMap<>();
private boolean changed;
/**
@ -36,6 +42,8 @@ public class IslandBlockCount implements DataObject {
@Expose
private Map<Material, Integer> blockLimits = new EnumMap<>(Material.class);
@Expose
private Map<String, Integer> customBlockLimits = new HashMap<>();
@Expose
private Map<EntityType, Integer> entityLimits = new EnumMap<>(EntityType.class);
@Expose
private Map<String, Integer> entityGroupLimits = new HashMap<>();
@ -120,6 +128,28 @@ public class IslandBlockCount implements DataObject {
setChanged();
}
/**
* Add a custom block to the count
* @param block - custom block
*/
public void add(Block block) {
CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block);
customBlockCounts.merge(Objects.requireNonNull(customBlock).getNamespacedID(), 1, Integer::sum);
setChanged();
}
/**
* Remove a custom block from the count
* @param block - block
*/
public void remove(Block block) {
CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block);
String id = Objects.requireNonNull(customBlock).getNamespacedID();
customBlockCounts.put(id, blockCounts.getOrDefault(id, 0) - 1);
blockCounts.values().removeIf(v -> v <= 0);
setChanged();
}
/**
* Check if this material is at or over a limit
* @param material - block material
@ -129,6 +159,18 @@ public class IslandBlockCount implements DataObject {
public boolean isAtLimit(Material material, int limit) {
return blockCounts.getOrDefault(material, 0) >= limit + this.getBlockLimitOffset(material);
}
/**
* Check if this custom block is at or over a limit
* @param block - block
* @param limit - limit to check
* @return true if count is >= limit
*/
public boolean isAtLimit(Block block, int limit) {
CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block);
String id = Objects.requireNonNull(customBlock).getNamespacedID();
return customBlockCounts.getOrDefault(id, 0) >= limit; // TODO Add a permission offset
}
/**
* Check if no more of this material can be added to this island
@ -139,10 +181,28 @@ public class IslandBlockCount implements DataObject {
// Check island limits first
return blockLimits.containsKey(m) && blockCounts.getOrDefault(m, 0) >= getBlockLimit(m) + this.getBlockLimitOffset(m);
}
/**
* Check if no more of this custom block can be added to this island
* @param block - block
* @return true if no more block can be added
*/
public boolean isAtLimit(Block block) {
CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block);
String id = Objects.requireNonNull(customBlock).getNamespacedID();
// Check island limits first
return customBlockLimits.containsKey(id) && customBlockCounts.getOrDefault(id, 0) >= getBlockLimit(block); // TODO add perm offset
}
public boolean isBlockLimited(Material m) {
return blockLimits.containsKey(m);
}
public boolean isCustomBlockLimited(Block block) {
CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block);
String id = Objects.requireNonNull(customBlock).getNamespacedID();
return customBlockLimits.containsKey(id);
}
/**
* @return the blockLimits
@ -167,6 +227,17 @@ public class IslandBlockCount implements DataObject {
public int getBlockLimit(Material m) {
return blockLimits.getOrDefault(m, -1);
}
/**
* Get the custom block limit for this material for this island
* @param block - block
* @return limit or -1 for unlimited
*/
public int getBlockLimit(Block block) {
CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block);
String id = Objects.requireNonNull(customBlock).getNamespacedID();
return customBlockLimits.getOrDefault(id, -1);
}
/**
* Get the block offset for this material for this island
@ -186,6 +257,16 @@ public class IslandBlockCount implements DataObject {
blockLimits.put(m, limit);
setChanged();
}
/**
* Set the custom block limit for this island
* @param id - namespaced id
* @param limit - maximum number allowed
*/
public void setCustomBlockLimit(String id, int limit) {
customBlockLimits.put(id, limit);
setChanged();
}
/**
* @return the gameMode
@ -385,4 +466,32 @@ public class IslandBlockCount implements DataObject {
public void setEntityGroupLimitsOffset(String name, Integer entityGroupLimitsOffset) {
getEntityGroupLimitsOffset().put(name, entityGroupLimitsOffset);
}
/**
* @return the customBlockCounts
*/
public Map<String, Integer> getCustomBlockCounts() {
return customBlockCounts;
}
/**
* @param customBlockCounts the customBlockCounts to set
*/
public void setCustomBlockCounts(Map<String, Integer> customBlockCounts) {
this.customBlockCounts = customBlockCounts;
}
/**
* @return the customBlockLimits
*/
public Map<String, Integer> getCustomBlockLimits() {
return customBlockLimits;
}
/**
* @param customBlockLimits the customBlockLimits to set
*/
public void setCustomBlockLimits(Map<String, Integer> customBlockLimits) {
this.customBlockLimits = customBlockLimits;
}
}

View File

@ -26,6 +26,13 @@ async-golums: true
# These limits apply to every game mode world
blocklimits:
HOPPER: 10
# Custom block limits
# Requires use of the ItemsAdder plugin. Custom blocks are currently only supported globally and not on a per-world basis.
# Limits require the full namespace name of the custom block material
customblocklimits:
# "iafestivities:christmas/christmas_tree/green_orb": 5
# This section is for world-specific limits and overrides the general limit
# Specify each world you want to limit individually (including nether and end worlds)
# If these worlds are not covered by the game modes above, nothing will happen

View File

@ -400,7 +400,7 @@ public class JoinListenerTest {
@Test
public void testOnPlayerJoinWithPermLimitsMultiPermsSameMaterial() {
// IBC - set the block limit for STONE to be 25 already
when(ibc.getBlockLimit(any())).thenReturn(25);
when(ibc.getBlockLimit(any(Material.class))).thenReturn(25);
Set<PermissionAttachmentInfo> perms = new HashSet<>();
PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class);
when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.24");