Add armor stand placement limits per chunk

Implemented functionality to restrict the number of armor stands a player can place in a chunk based on permissions. Added configuration support for customizable limits and proper error handling for edge cases. Registered the LimitArmorStands event listener in the plugin manager.
This commit is contained in:
Teriuihi 2025-03-22 20:33:31 +01:00
parent 10449f4a80
commit 7c04361ea5
3 changed files with 120 additions and 4 deletions

View File

@ -5,10 +5,7 @@ import com.alttd.playerutils.commands.playerutils_subcommands.RotateBlock;
import com.alttd.playerutils.config.Config;
import com.alttd.playerutils.config.KeyStorage;
import com.alttd.playerutils.config.Messages;
import com.alttd.playerutils.event_listeners.GoatHornEvent;
import com.alttd.playerutils.event_listeners.RotateBlockEvent;
import com.alttd.playerutils.event_listeners.TeleportEvent;
import com.alttd.playerutils.event_listeners.XpBottleEvent;
import com.alttd.playerutils.event_listeners.*;
import com.alttd.playerutils.util.Logger;
import org.bukkit.Bukkit;
import org.bukkit.plugin.PluginManager;
@ -44,6 +41,7 @@ public final class PlayerUtils extends JavaPlugin {
pluginManager.registerEvents(new XpBottleEvent(this, logger), this);
pluginManager.registerEvents(new TeleportEvent(), this);
pluginManager.registerEvents(new GoatHornEvent(logger), this);
pluginManager.registerEvents(new LimitArmorStands(this, logger), this);
RotateBlockEvent rotateBlockEvent = new RotateBlockEvent(logger);
pluginManager.registerEvents(rotateBlockEvent, this);

View File

@ -62,4 +62,24 @@ public class Config extends AbstractConfig{
}
}
}
public static class ARMOR_STAND_LIMIT {
private static final String prefix = "armor_stand_limit.";
public static HashMap<String, Integer> LIMIT = new HashMap<>();
@SuppressWarnings("unused")
private static void load() {
LIMIT.clear();
ConfigurationSection configurationSection = config.getConfigurationSection(prefix.substring(0, prefix.length() - 1));
if (configurationSection == null) {
config.logger.warning("No limits configured, adding default");
config.set(prefix, "default", 10);
}
Set<String> limits = configurationSection.getKeys(false);
for (String key : limits) {
LIMIT.put(key, config.getInt(prefix, key, 10));
}
}
}
}

View File

@ -0,0 +1,98 @@
package com.alttd.playerutils.event_listeners;
import com.alttd.playerutils.PlayerUtils;
import com.alttd.playerutils.config.Config;
import com.alttd.playerutils.util.Logger;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
public class LimitArmorStands implements Listener {
private final PlayerUtils playerUtils;
private final Logger logger;
public LimitArmorStands(PlayerUtils playerUtils, Logger logger) {
this.playerUtils = playerUtils;
this.logger = logger;
}
@FunctionalInterface
private interface ArmorStandCountConsumer {
void apply(int armorStandCount, @NotNull Player player, @NotNull NamespacedKey namespacedKey,
@NotNull PersistentDataContainer persistentDataContainer);
}
@EventHandler
public void onArmorStandSpawn(PlayerInteractEvent event) {
handleArmorStandPlacing(event, (armorStandCount, player, namespacedKey, persistentDataContainer) -> {
int max = 0;
for (String permission : Config.ARMOR_STAND_LIMIT.LIMIT.keySet()) {
if (player.hasPermission("pu.limit." + permission)) {
max = Math.max(max, Config.ARMOR_STAND_LIMIT.LIMIT.get(permission));
}
}
if (armorStandCount >= max) {
event.setCancelled(true);
player.sendRichMessage("<red>You can not place more than <max> armor stands in a chunk</red>",
Placeholder.parsed("max", String.valueOf(max)));
return;
}
persistentDataContainer.set(namespacedKey, PersistentDataType.INTEGER, ++armorStandCount);
});
}
private void handleArmorStandPlacing(PlayerInteractEvent event, ArmorStandCountConsumer consumer) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {
return;
}
if (!event.getMaterial().equals(Material.ARMOR_STAND)) {
return;
}
if (event.getClickedBlock() == null) {
return;
}
Player player = event.getPlayer();;
Chunk chunk = event.getClickedBlock().getChunk();
NamespacedKey namespacedKey = NamespacedKey.fromString("armor_stand_count", playerUtils);
if (namespacedKey == null) {
event.setCancelled(true);
logger.warning("Unable to retrieve name spaced key for armor stand count.");
player.sendRichMessage("<red>Something went wrong while checking the armor stand count. " +
"You will not be able to place this until this is fixed. Please contact a staff member</red>");
return;
}
PersistentDataContainer persistentDataContainer = chunk.getPersistentDataContainer();
if (!persistentDataContainer.has(namespacedKey, PersistentDataType.INTEGER)) {
persistentDataContainer.set(namespacedKey, PersistentDataType.INTEGER, countArmorStands(chunk));
}
Integer armorStandCount = persistentDataContainer.get(namespacedKey, PersistentDataType.INTEGER);
if (armorStandCount == null) {
event.setCancelled(true);
logger.warning("Unable to retrieve armor stand count.");
player.sendRichMessage("<red>Something went wrong while checking the armor stand count. " +
"You will not be able to place this until this is fixed. Please contact a staff member</red>");
return;
}
consumer.apply(armorStandCount, event.getPlayer(), namespacedKey, persistentDataContainer);
}
private int countArmorStands(Chunk chunk) {
return (int) Arrays.stream(chunk.getEntities())
.filter(entity -> entity.getType().equals(EntityType.ARMOR_STAND))
.count();
}
}