Add GiveSpawner command and enhance spawner functionalities

Implemented the GiveSpawner command to allow players to receive specific mob spawners. Added validation and namespaced key handling for mob types and entities to ensure proper functionality when placing spawners. Refactored existing code to use the new EntityTypeChecker utility.
This commit is contained in:
Teriuihi 2024-10-19 19:10:37 +02:00
parent 8d804c95ba
commit 9d39e4cc7a
7 changed files with 234 additions and 26 deletions

View File

@ -6,6 +6,7 @@ import com.alttd.custommobs.config.Messages;
import com.alttd.custommobs.config.MobTypes;
import com.alttd.custommobs.listeners.SpawnerListener;
import lombok.extern.slf4j.Slf4j;
import org.bukkit.NamespacedKey;
import org.bukkit.plugin.java.JavaPlugin;
@Slf4j
@ -15,16 +16,20 @@ public class Main extends JavaPlugin {
public void onEnable() {
log.info("Plugin enabled!");
reloadConfigs();
registerEvents();
registerCommands();
NamespacedKey mobTypeKey = new NamespacedKey(this, "mob-type");
NamespacedKey entityKey = new NamespacedKey(this, "entity-type");
registerEvents(mobTypeKey, entityKey);
registerCommands(mobTypeKey, entityKey);
}
private void registerCommands() {
new CommandManager(this);
private void registerCommands(NamespacedKey mobTypeKey, NamespacedKey entityKey) {
new CommandManager(this, mobTypeKey, entityKey);
}
private void registerEvents() {
getServer().getPluginManager().registerEvents(new SpawnerListener(this), this);
private void registerEvents(NamespacedKey mobTypeKey, NamespacedKey entityKey) {
getServer().getPluginManager().registerEvents(new SpawnerListener(this, mobTypeKey, entityKey), this);
}
public void reloadConfigs() {

View File

@ -1,10 +1,12 @@
package com.alttd.custommobs.commands;
import com.alttd.custommobs.Main;
import com.alttd.custommobs.commands.subcommands.GiveSpawner;
import com.alttd.custommobs.commands.subcommands.Spawn;
import com.alttd.custommobs.config.Messages;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.bukkit.NamespacedKey;
import org.bukkit.command.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -19,7 +21,7 @@ import java.util.stream.Collectors;
public class CommandManager implements CommandExecutor, TabExecutor {
private final List<SubCommand> subCommands;
public CommandManager(Main main) {
public CommandManager(Main main, @NotNull NamespacedKey mobTypeKey, @NotNull NamespacedKey entityKey) {
PluginCommand command = main.getCommand("custommobs");
if (command == null) {
subCommands = null;
@ -31,7 +33,8 @@ public class CommandManager implements CommandExecutor, TabExecutor {
command.setAliases(List.of("cm"));
subCommands = Arrays.asList(
new Spawn(main)
new Spawn(main),
new GiveSpawner(main, mobTypeKey, entityKey)
);
}

View File

@ -0,0 +1,105 @@
package com.alttd.custommobs.commands.subcommands;
import com.alttd.custommobs.Main;
import com.alttd.custommobs.abilities.MobType;
import com.alttd.custommobs.commands.SubCommand;
import com.alttd.custommobs.config.Messages;
import com.alttd.custommobs.config.MobTypes;
import com.alttd.custommobs.utility.EntityTypeChecker;
import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@Slf4j
public class GiveSpawner extends SubCommand {
private final Main main;
private final NamespacedKey mobTypeKey;
private final NamespacedKey entityKey;
public GiveSpawner(Main main, @NotNull NamespacedKey mobTypeKey, @NotNull NamespacedKey entityKey) {
this.main = main;
this.mobTypeKey = mobTypeKey;
this.entityKey = entityKey;
}
@Override
public boolean onCommand(CommandSender commandSender, String[] args) {
if (!(commandSender instanceof Player player)) {
commandSender.sendRichMessage(Messages.GENERIC.PLAYER_ONLY);
return true;
}
if (args.length != 3) {
return false;
}
Optional<MobType> optionalMobType = MobTypes.MOB_TYPES.get(args[1]);
if (optionalMobType.isEmpty()) {
return false;
}
try {
EntityType entityType = EntityType.valueOf(args[2]);
if (!entityType.isSpawnable() || !EntityTypeChecker.isMob(entityType)) {
return false;
}
} catch (IllegalArgumentException e) {
log.info("DEBUG: Invalid mob type {}", args[2]);
return false;
}
ItemStack spawner = new ItemStack(Material.SPAWNER);
ItemMeta meta = spawner.getItemMeta();
meta.displayName(MiniMessage.miniMessage().deserialize(Messages.GIVE_SPAWNER.SPAWNER_NAME, TagResolver.resolver(
Placeholder.parsed("entity", args[2]),
Placeholder.parsed("mob_type", args[1])
)));
PersistentDataContainer persistentDataContainer = meta.getPersistentDataContainer();
persistentDataContainer.set(mobTypeKey, PersistentDataType.STRING, args[1]);
persistentDataContainer.set(entityKey, PersistentDataType.STRING, args[2]);
spawner.setItemMeta(meta);
if (!player.getInventory().addItem(spawner).isEmpty()) {
player.sendRichMessage(Messages.GIVE_SPAWNER.FULL_INVENTORY);
return true;
}
player.sendRichMessage(Messages.GIVE_SPAWNER.GIVEN, TagResolver.resolver(
Placeholder.parsed("entity", args[2]),
Placeholder.parsed("mob_type", args[1])
));
return true;
}
@Override
public String getName() {
return "give_spawner";
}
@Override
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
return switch (args.length) {
case 2 -> MobTypes.MOB_TYPES.getAllMobTypes();
case 3 -> Arrays.stream(EntityType.values()).map(EntityType::name).toList();
default -> List.of();
};
}
@Override
public String getHelpMessage() {
return Messages.HELP.GIVE_SPAWNER;
}
}

View File

@ -6,6 +6,7 @@ import com.alttd.custommobs.abilities.MobTypeApplier;
import com.alttd.custommobs.commands.SubCommand;
import com.alttd.custommobs.config.Messages;
import com.alttd.custommobs.config.MobTypes;
import com.alttd.custommobs.utility.EntityTypeChecker;
import com.alttd.custommobs.utility.SpawnableTargetBlock;
import lombok.extern.slf4j.Slf4j;
import org.bukkit.Bukkit;
@ -46,7 +47,7 @@ public class Spawn extends SubCommand {
log.info("DEBUG: Invalid mob type {}", args[2]);
return false;
}
if (!entityType.isSpawnable() || !isMob(entityType)) {
if (!entityType.isSpawnable() || !EntityTypeChecker.isMob(entityType)) {
return false;
}
@ -87,22 +88,6 @@ public class Spawn extends SubCommand {
}
}
public boolean isMob(EntityType entityType) {
if (entityType == null) {
return false;
}
try {
Class<? extends Entity> entityClass = entityType.getEntityClass();
if (entityClass == null) {
return false;
}
Class<?> mobEntityClass = Class.forName("org.bukkit.entity." + entityClass.getSimpleName());
return Mob.class.isAssignableFrom(mobEntityClass);
} catch (ClassNotFoundException e) {
return false;
}
}
private void spawnMob(World world, Location location, EntityType entityType, MobType mobType) {
if (entityType.getEntityClass() == null) {
log.warn("Tried to spawn entity with null entity class {}", entityType.name());

View File

@ -1,6 +1,7 @@
package com.alttd.custommobs.config;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import java.io.File;
@ -29,12 +30,14 @@ public class Messages extends AbstractConfig {
public static String HELP_MESSAGE_WRAPPER = "<gold>Main help:\n<commands></gold>";
public static String HELP_MESSAGE = "<green>Show this menu: <gold>/cm help</gold></green>";
public static String SPAWN = "<green>Spawn a mob of a specific type: <gold>/cm spawn <mob_type> <mob> [<world> <x> <y> <z>]</gold></green>";
public static String GIVE_SPAWNER = "<green>Give a mob spawner for a mob of a specific type: <gold>/cm givespawner <mob_type> <mob></green>";
@SuppressWarnings("unused")
private static void load() {
HELP_MESSAGE_WRAPPER = config.getString(prefix, "help-wrapper", HELP_MESSAGE_WRAPPER);
HELP_MESSAGE = config.getString(prefix, "help", HELP_MESSAGE);
SPAWN = config.getString(prefix, "spawn", SPAWN);
GIVE_SPAWNER = config.getString(prefix, "give-spawner", GIVE_SPAWNER);
}
}
@ -52,4 +55,31 @@ public class Messages extends AbstractConfig {
PLAYER_NOT_FOUND = config.getString(prefix, "player-only", PLAYER_NOT_FOUND);
}
}
public static class GIVE_SPAWNER {
private static final String prefix = "give-spawner.";
public static String FULL_INVENTORY = "<red>You inventory is full so the spawner was not given</red>";
public static String GIVEN = "<green>You have been given a <entity> spawner for type <mob_type></green>";
public static String SPAWNER_NAME = "<gold><entity> <mob_type> spawner</gold>";
@SuppressWarnings("unused")
private static void load() {
FULL_INVENTORY = config.getString(prefix, "full-inventory", FULL_INVENTORY);
GIVEN = config.getString(prefix, "given", GIVEN);
SPAWNER_NAME = config.getString(prefix, "spawner-name", SPAWNER_NAME);
}
}
public static class PLACE_SPAWNER {
private static final String prefix = "place-spawner.";
public static String INVALID_MOB_TYPE = "<red>This spawner has an invalid mob type (<mob-type>)</red>";
public static String INVALID_ENTITY_TYPE = "<red>This spawner has an invalid entity type (<entity-type>)</red>";
@SuppressWarnings("unused")
private static void load() {
INVALID_MOB_TYPE = config.getString(prefix, "invalid-mob-type", INVALID_MOB_TYPE);
INVALID_ENTITY_TYPE = config.getString(prefix, "invalid-entity-type", INVALID_ENTITY_TYPE);
}
}
}

View File

@ -3,14 +3,26 @@ package com.alttd.custommobs.listeners;
import com.alttd.custommobs.Main;
import com.alttd.custommobs.abilities.MobType;
import com.alttd.custommobs.abilities.MobTypeApplier;
import com.alttd.custommobs.config.Messages;
import com.alttd.custommobs.config.MobTypes;
import com.alttd.custommobs.utility.EntityTypeChecker;
import io.papermc.paper.persistence.PersistentDataContainerView;
import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.CreatureSpawner;
import org.bukkit.block.Sign;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.SpawnerSpawnEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Optional;
@ -19,9 +31,13 @@ import java.util.Optional;
public class SpawnerListener implements Listener {
private final MobTypeApplier mobTypeApplier;
private final NamespacedKey mobTypeKey;
private final NamespacedKey entityKey;
public SpawnerListener(Main main) {
public SpawnerListener(Main main, @NotNull NamespacedKey mobTypeKey, @NotNull NamespacedKey entityKey) {
mobTypeApplier = new MobTypeApplier(main);
this.mobTypeKey = mobTypeKey;
this.entityKey = entityKey;
}
@EventHandler
@ -48,4 +64,44 @@ public class SpawnerListener implements Listener {
mobTypeApplier.apply(livingEntity, mobType);
}
@EventHandler
public void onSpawnerPlace(BlockPlaceEvent event) {
ItemStack itemInHand = event.getItemInHand();
if (!itemInHand.getType().equals(org.bukkit.Material.SPAWNER)) {
return;
}
PersistentDataContainerView persistentDataContainer = itemInHand.getPersistentDataContainer();
if (!persistentDataContainer.has(mobTypeKey) || !persistentDataContainer.has(entityKey)) {
return;
}
String mobType = persistentDataContainer.get(mobTypeKey, PersistentDataType.STRING);
String entityTypeName = persistentDataContainer.get(entityKey, PersistentDataType.STRING);
if (mobType == null || entityTypeName == null) {
return;
}
Optional<MobType> optionalMobType = MobTypes.MOB_TYPES.get(mobType);
if (optionalMobType.isEmpty()) {
event.getPlayer().sendRichMessage(Messages.PLACE_SPAWNER.INVALID_MOB_TYPE, Placeholder.parsed("mob-type", mobType));
return;
}
EntityType entityType;
try {
entityType = EntityType.valueOf(entityTypeName);
if (!entityType.isSpawnable() || !EntityTypeChecker.isMob(entityType)) {
event.getPlayer().sendRichMessage(Messages.PLACE_SPAWNER.INVALID_ENTITY_TYPE, Placeholder.parsed("entity-type", entityTypeName));
return;
}
} catch (IllegalArgumentException e) {
log.info("DEBUG: Invalid mob type {}", entityTypeName);
return;
}
if (event.getBlock().getState() instanceof CreatureSpawner spawner) {
spawner.setSpawnedType(entityType);
} else {
log.info("Actual bock state {}", event.getBlock().getState().getClass().getName());
}
}
}

View File

@ -0,0 +1,24 @@
package com.alttd.custommobs.utility;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Mob;
public class EntityTypeChecker {
public static boolean isMob(EntityType entityType) {
if (entityType == null) {
return false;
}
try {
Class<? extends Entity> entityClass = entityType.getEntityClass();
if (entityClass == null) {
return false;
}
Class<?> mobEntityClass = Class.forName("org.bukkit.entity." + entityClass.getSimpleName());
return Mob.class.isAssignableFrom(mobEntityClass);
} catch (ClassNotFoundException e) {
return false;
}
}
}