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

View File

@ -1,6 +1,14 @@
package world.bentobox.limits.listeners; 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.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
@ -15,13 +23,27 @@ import org.bukkit.event.Event;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.*; 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.EntityChangeBlockEvent;
import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.plugin.Plugin;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable; 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.events.island.IslandDeleteEvent;
import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
@ -59,7 +81,10 @@ public class BlockLimitsListener implements Listener {
private final Map<String, Integer> saveMap = new HashMap<>(); private final Map<String, Integer> saveMap = new HashMap<>();
private final Database<IslandBlockCount> handler; private final Database<IslandBlockCount> handler;
private final Map<World, Map<Material, Integer>> worldLimitMap = new HashMap<>(); private final Map<World, Map<Material, Integer>> worldLimitMap = new HashMap<>();
//private final Map<World, Map<String, Integer>> customWorldLimitMap = new HashMap<>();
private Map<Material, Integer> defaultLimitMap = new EnumMap<>(Material.class); private Map<Material, Integer> defaultLimitMap = new EnumMap<>(Material.class);
private Map<String, Integer> defaultCustomLimitMap = new HashMap<>();
private Plugin itemsAdder;
public BlockLimitsListener(Limits addon) { public BlockLimitsListener(Limits addon) {
this.addon = addon; this.addon = addon;
@ -89,6 +114,11 @@ public class BlockLimitsListener implements Listener {
ConfigurationSection limitConfig = addon.getConfig().getConfigurationSection("blocklimits"); ConfigurationSection limitConfig = addon.getConfig().getConfigurationSection("blocklimits");
defaultLimitMap = loadLimits(Objects.requireNonNull(limitConfig)); 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 // Load specific worlds
if (addon.getConfig().isConfigurationSection("worlds")) { if (addon.getConfig().isConfigurationSection("worlds")) {
@ -126,6 +156,21 @@ public class BlockLimitsListener implements Listener {
return mats; 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 * Save the count database completely
@ -137,7 +182,7 @@ public class BlockLimitsListener implements Listener {
// Player-related events // Player-related events
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onBlock(BlockPlaceEvent e) { public void onBlock(BlockPlaceEvent e) {
notify(e, User.getInstance(e.getPlayer()), process(e.getBlock(), true), e.getBlock().getType()); notify(e, User.getInstance(e.getPlayer()), process(e.getBlock(), true), e.getBlock());
} }
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
@ -187,7 +232,7 @@ public class BlockLimitsListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onBlock(BlockMultiPlaceEvent e) { 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 limit maximum limit allowed
* @param m material * @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) { 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", user.notify("block-limits.hit-limit",
"[material]", Util.prettifyText(m.toString()), "[material]", name,
TextVariables.NUMBER, String.valueOf(limit)); TextVariables.NUMBER, String.valueOf(limit));
e.setCancelled(true); e.setCancelled(true);
} }
@ -318,6 +368,8 @@ public class BlockLimitsListener implements Listener {
* @return limit amount if over limit, or -1 if no limitation * @return limit amount if over limit, or -1 if no limitation
*/ */
private int process(Block b, boolean add) { 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())) { if (DO_NOT_COUNT.contains(fixMaterial(b.getBlockData())) || !addon.inGameModeWorld(b.getWorld())) {
return -1; return -1;
} }
@ -335,12 +387,27 @@ public class BlockLimitsListener implements Listener {
} }
islandCountMap.putIfAbsent(id, new IslandBlockCount(id, gameMode)); islandCountMap.putIfAbsent(id, new IslandBlockCount(id, gameMode));
if (add) { if (add) {
// Check limit // Check if custom block
int limit = checkLimit(b.getWorld(), fixMaterial(b.getBlockData()), id); if (itemsAdder != null ) {
if (limit > -1) { CustomBlock customBlock = CustomBlock.byAlreadyPlaced(b);
return limit; 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 { } else {
if (islandCountMap.containsKey(id)) { if (islandCountMap.containsKey(id)) {
islandCountMap.get(id).remove(fixMaterial(b.getBlockData())); islandCountMap.get(id).remove(fixMaterial(b.getBlockData()));
@ -351,6 +418,11 @@ public class BlockLimitsListener implements Listener {
}).orElse(-1); }).orElse(-1);
} }
private void checkCustom() {
itemsAdder = Bukkit.getPluginManager().getPlugin("ItemsAdder");
}
/** /**
* Removed a block from any island limit count * Removed a block from any island limit count
* @param b - block to remove * @param b - block to remove
@ -364,10 +436,17 @@ public class BlockLimitsListener implements Listener {
// Invalid world // Invalid world
return; return;
} }
islandCountMap.computeIfAbsent(id, k -> new IslandBlockCount(id, gameMode)).remove(fixMaterial(b.getBlockData())); // Check for custom block
updateSaveMap(id); 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) { private void updateSaveMap(String id) {
saveMap.putIfAbsent(id, 0); saveMap.putIfAbsent(id, 0);
if (saveMap.merge(id, 1, Integer::sum) > CHANGE_LIMIT) { if (saveMap.merge(id, 1, Integer::sum) > CHANGE_LIMIT) {
@ -404,6 +483,37 @@ public class BlockLimitsListener implements Listener {
return -1; 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 * 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. // Add offsets to the every limit.
islandBlockCount.getBlockLimitsOffset().forEach((material, offset) -> islandBlockCount.getBlockLimitsOffset().forEach((material, offset) ->
result.put(material, result.getOrDefault(material, 0) + offset)); result.put(material, result.getOrDefault(material, 0) + offset));
} }
return result; return result;
} }

View File

@ -3,12 +3,15 @@ package world.bentobox.limits.objects;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import com.google.gson.annotations.Expose; 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.DataObject;
import world.bentobox.bentobox.database.objects.Table; import world.bentobox.bentobox.database.objects.Table;
@ -28,6 +31,9 @@ public class IslandBlockCount implements DataObject {
@Expose @Expose
private Map<Material, Integer> blockCounts = new EnumMap<>(Material.class); private Map<Material, Integer> blockCounts = new EnumMap<>(Material.class);
@Expose
private Map<String, Integer> customBlockCounts = new HashMap<>();
private boolean changed; private boolean changed;
/** /**
@ -36,6 +42,8 @@ public class IslandBlockCount implements DataObject {
@Expose @Expose
private Map<Material, Integer> blockLimits = new EnumMap<>(Material.class); private Map<Material, Integer> blockLimits = new EnumMap<>(Material.class);
@Expose @Expose
private Map<String, Integer> customBlockLimits = new HashMap<>();
@Expose
private Map<EntityType, Integer> entityLimits = new EnumMap<>(EntityType.class); private Map<EntityType, Integer> entityLimits = new EnumMap<>(EntityType.class);
@Expose @Expose
private Map<String, Integer> entityGroupLimits = new HashMap<>(); private Map<String, Integer> entityGroupLimits = new HashMap<>();
@ -120,6 +128,28 @@ public class IslandBlockCount implements DataObject {
setChanged(); 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 * Check if this material is at or over a limit
* @param material - block material * @param material - block material
@ -129,6 +159,18 @@ public class IslandBlockCount implements DataObject {
public boolean isAtLimit(Material material, int limit) { public boolean isAtLimit(Material material, int limit) {
return blockCounts.getOrDefault(material, 0) >= limit + this.getBlockLimitOffset(material); 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 * 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 // Check island limits first
return blockLimits.containsKey(m) && blockCounts.getOrDefault(m, 0) >= getBlockLimit(m) + this.getBlockLimitOffset(m); 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) { public boolean isBlockLimited(Material m) {
return blockLimits.containsKey(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 * @return the blockLimits
@ -167,6 +227,17 @@ public class IslandBlockCount implements DataObject {
public int getBlockLimit(Material m) { public int getBlockLimit(Material m) {
return blockLimits.getOrDefault(m, -1); 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 * Get the block offset for this material for this island
@ -186,6 +257,16 @@ public class IslandBlockCount implements DataObject {
blockLimits.put(m, limit); blockLimits.put(m, limit);
setChanged(); 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 * @return the gameMode
@ -385,4 +466,32 @@ public class IslandBlockCount implements DataObject {
public void setEntityGroupLimitsOffset(String name, Integer entityGroupLimitsOffset) { public void setEntityGroupLimitsOffset(String name, Integer entityGroupLimitsOffset) {
getEntityGroupLimitsOffset().put(name, 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 # These limits apply to every game mode world
blocklimits: blocklimits:
HOPPER: 10 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 # 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) # 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 # If these worlds are not covered by the game modes above, nothing will happen

View File

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