From 3a0dad8586a92efd3ce2d93a35420c1802da1156 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Oct 2025 14:00:57 +0000 Subject: [PATCH 1/2] Initial plan From 9ef25e6bb0d4ec284f463796fb1281a14145468d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Oct 2025 14:07:05 +0000 Subject: [PATCH 2/2] Optimize SpawnerHologram performance and add /ss hologram clear command Co-authored-by: ptthanh02 <73684260+ptthanh02@users.noreply.github.com> --- .../hologram/HologramClearSubCommand.java | 51 ++++++++++++++++++ .../commands/hologram/HologramSubCommand.java | 18 +++++++ .../commands/hologram/SpawnerHologram.java | 54 +++++++++++++------ .../resources/language/DonutSMP/messages.yml | 8 +++ .../resources/language/de_DE/messages.yml | 8 +++ .../resources/language/en_US/messages.yml | 8 +++ 6 files changed, 130 insertions(+), 17 deletions(-) create mode 100644 core/src/main/java/github/nighter/smartspawner/commands/hologram/HologramClearSubCommand.java diff --git a/core/src/main/java/github/nighter/smartspawner/commands/hologram/HologramClearSubCommand.java b/core/src/main/java/github/nighter/smartspawner/commands/hologram/HologramClearSubCommand.java new file mode 100644 index 00000000..1aa3843e --- /dev/null +++ b/core/src/main/java/github/nighter/smartspawner/commands/hologram/HologramClearSubCommand.java @@ -0,0 +1,51 @@ +package github.nighter.smartspawner.commands.hologram; + +import com.mojang.brigadier.context.CommandContext; +import github.nighter.smartspawner.SmartSpawner; +import github.nighter.smartspawner.commands.BaseSubCommand; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class HologramClearSubCommand extends BaseSubCommand { + + public HologramClearSubCommand(SmartSpawner plugin) { + super(plugin); + } + + @Override + public String getName() { + return "clear"; + } + + @Override + public String getPermission() { + return "smartspawner.hologram.clear"; + } + + @Override + public String getDescription() { + return "Clear all text display holograms"; + } + + @Override + public int execute(CommandContext context) { + CommandSender sender = context.getSource().getSender(); + + try { + // Execute the Minecraft command to kill all text_display entities + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "minecraft:kill @e[type=text_display]"); + + // Send success message to player + plugin.getMessageService().sendMessage(sender, "command_hologram_cleared"); + + return 1; + } catch (Exception e) { + plugin.getLogger().severe("Error clearing holograms: " + e.getMessage()); + plugin.getMessageService().sendMessage(sender, "command_hologram_clear_error"); + return 0; + } + } +} diff --git a/core/src/main/java/github/nighter/smartspawner/commands/hologram/HologramSubCommand.java b/core/src/main/java/github/nighter/smartspawner/commands/hologram/HologramSubCommand.java index 51cb9e50..45dee000 100644 --- a/core/src/main/java/github/nighter/smartspawner/commands/hologram/HologramSubCommand.java +++ b/core/src/main/java/github/nighter/smartspawner/commands/hologram/HologramSubCommand.java @@ -1,10 +1,12 @@ package github.nighter.smartspawner.commands.hologram; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import github.nighter.smartspawner.SmartSpawner; import github.nighter.smartspawner.commands.BaseSubCommand; import github.nighter.smartspawner.spawner.properties.SpawnerManager; import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.Commands; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.FileConfiguration; import org.jspecify.annotations.NullMarked; @@ -12,10 +14,12 @@ @NullMarked public class HologramSubCommand extends BaseSubCommand { private final SpawnerManager spawnerManager; + private final HologramClearSubCommand clearSubCommand; public HologramSubCommand(SmartSpawner plugin) { super(plugin); this.spawnerManager = plugin.getSpawnerManager(); + this.clearSubCommand = new HologramClearSubCommand(plugin); } @Override @@ -33,6 +37,20 @@ public String getDescription() { return "Toggle hologram display for spawners"; } + @Override + public LiteralArgumentBuilder build() { + LiteralArgumentBuilder builder = Commands.literal(getName()); + builder.requires(source -> hasPermission(source.getSender())); + + // Add the toggle functionality as the default execution + builder.executes(this::execute); + + // Add the clear subcommand + builder.then(clearSubCommand.build()); + + return builder; + } + @Override public int execute(CommandContext context) { CommandSender sender = context.getSource().getSender(); diff --git a/core/src/main/java/github/nighter/smartspawner/commands/hologram/SpawnerHologram.java b/core/src/main/java/github/nighter/smartspawner/commands/hologram/SpawnerHologram.java index 812a6557..8f500893 100644 --- a/core/src/main/java/github/nighter/smartspawner/commands/hologram/SpawnerHologram.java +++ b/core/src/main/java/github/nighter/smartspawner/commands/hologram/SpawnerHologram.java @@ -135,13 +135,6 @@ public void updateText() { } public void updateData(int stackSize, EntityType entityType, int currentExp, int maxExp, int currentItems, int maxSlots) { - // First, ensure we have a valid hologram - TextDisplay display = textDisplay.get(); - if (display == null || !display.isValid()) { - // If hologram doesn't exist or is invalid, recreate it - createHologram(); - } - // Update data values this.stackSize = stackSize; this.entityType = entityType; @@ -150,17 +143,39 @@ public void updateData(int stackSize, EntityType entityType, int currentExp, int this.currentItems = currentItems; this.maxSlots = maxSlots; - // Update the text display - updateText(); + // First, ensure we have a valid hologram + TextDisplay display = textDisplay.get(); + if (display == null) { + // If hologram doesn't exist, recreate it + createHologram(); + } else { + // Check validity on the entity thread to avoid race conditions + Scheduler.runEntityTask(display, () -> { + if (!display.isValid()) { + // If invalid, recreate the hologram + textDisplay.set(null); + createHologram(); + } else { + // Update the text display + updateText(); + } + }); + } } public void remove() { TextDisplay display = textDisplay.get(); - if (display != null && display.isValid()) { - // Run on the entity's thread - Scheduler.runEntityTask(display, display::remove); + if (display != null) { + // Run on the entity's thread to ensure safe removal + Scheduler.runEntityTask(display, () -> { + if (display.isValid()) { + display.remove(); + } + }); textDisplay.set(null); } + // Also clean up any stuck holograms + cleanupExistingHologram(); } public void cleanupExistingHologram() { @@ -169,13 +184,16 @@ public void cleanupExistingHologram() { // First, check if our tracked hologram is still valid TextDisplay display = textDisplay.get(); if (display != null) { - if (display.isValid()) { - // If it's valid but we're cleaning up, remove it - display.remove(); - } + // Always remove the tracked display, even if it appears invalid + Scheduler.runEntityTask(display, () -> { + if (display.isValid()) { + display.remove(); + } + }); textDisplay.set(null); } + // Use async task to avoid blocking Scheduler.runLocationTask(spawnerLocation, () -> { // Define a tighter search radius just to catch any potentially duplicated holograms // with the same identifier (which shouldn't happen but being safe) @@ -186,7 +204,9 @@ public void cleanupExistingHologram() { .stream() .filter(entity -> entity instanceof TextDisplay && entity.getCustomName() != null) .filter(entity -> entity.getCustomName().equals(uniqueIdentifier)) - .forEach(Entity::remove); + .forEach(entity -> { + Scheduler.runEntityTask(entity, entity::remove); + }); }); } } \ No newline at end of file diff --git a/core/src/main/resources/language/DonutSMP/messages.yml b/core/src/main/resources/language/DonutSMP/messages.yml index c550ec2a..fddd0c4f 100644 --- a/core/src/main/resources/language/DonutSMP/messages.yml +++ b/core/src/main/resources/language/DonutSMP/messages.yml @@ -252,6 +252,14 @@ command_hologram_disabled: message: "&#f8f8ffʜᴏʟᴏɢʀᴀᴍꜱ &#e05252ᴅɪꜱᴀʙʟᴇᴅ &#f8f8ffꜰᴏʀ ᴀʟʟ ꜱᴘᴀᴡɴᴇʀꜱ" sound: block.note_block.bass +command_hologram_cleared: + message: "%eb9aꜱᴜᴄᴄᴇꜱꜱꜰᴜʟʟʏ &#f8f8ffᴄʟᴇᴀʀᴇᴅ ᴀʟʟ ʜᴏʟᴏɢʀᴀᴍꜱ" + sound: entity.experience_orb.pickup + +command_hologram_clear_error: + message: "&#ff5252ꜰᴀɪʟᴇᴅ ᴛᴏ ᴄʟᴇᴀʀ ʜᴏʟᴏɢʀᴀᴍꜱ" + sound: block.note_block.pling + # Prices command messages prices_not_available: message: "&#ff5252ꜱᴇʟʟ ɪɴᴛᴇɢʀᴀᴛɪᴏɴ ɪꜱ ɴᴏᴛ ᴀᴠᴀɪʟᴀʙʟᴇ. ᴘʟᴇᴀꜱᴇ ᴄᴏɴꜰɪɢᴜʀᴇ ᴇᴄᴏɴᴏᴍʏ ꜱᴇᴛᴛɪɴɢꜱ." diff --git a/core/src/main/resources/language/de_DE/messages.yml b/core/src/main/resources/language/de_DE/messages.yml index a7cb727c..2e93d374 100644 --- a/core/src/main/resources/language/de_DE/messages.yml +++ b/core/src/main/resources/language/de_DE/messages.yml @@ -252,6 +252,14 @@ command_hologram_disabled: message: "&#f8f8ffʜᴏʟᴏɢʀᴀᴍᴍᴇ &#e05252ᴅᴇᴀᴋᴛɪᴠɪᴇʀᴛ &#f8f8ffꜰᴜ̈ʀ ᴀʟʟᴇ ꜱᴘᴀᴡɴᴇʀ" sound: block.note_block.bass +command_hologram_cleared: + message: "%eb9aᴇʀꜰᴏʟɢʀᴇɪᴄʜ &#f8f8ffᴀʟʟᴇ ʜᴏʟᴏɢʀᴀᴍᴍᴇ ɢᴇʟᴏ̈ꜱᴄʜᴛ" + sound: entity.experience_orb.pickup + +command_hologram_clear_error: + message: "&#ff5252ꜰᴇʜʟᴇʀ ʙᴇɪᴍ ʟᴏ̈ꜱᴄʜᴇɴ ᴅᴇʀ ʜᴏʟᴏɢʀᴀᴍᴍᴇ" + sound: block.note_block.pling + # Prices command messagess prices_not_available: message: "&#ff5252ᴠᴇʀᴋᴀᴜꜰ ɪɴᴛᴇɢʀᴀᴛɪᴏɴ ɪꜱᴛ ɴɪᴄʜᴛ ᴠᴇʀꜰᴜ̈ɢʙᴀʀ. ʙɪᴛᴛᴇ ᴇᴄᴏɴᴏᴍʏ ᴇɪɴꜱᴛᴇʟʟᴜɴɢᴇɴ ᴋᴏɴꜰɪɢᴜʀɪᴇʀᴇɴ." diff --git a/core/src/main/resources/language/en_US/messages.yml b/core/src/main/resources/language/en_US/messages.yml index 3fab7efa..b2339144 100644 --- a/core/src/main/resources/language/en_US/messages.yml +++ b/core/src/main/resources/language/en_US/messages.yml @@ -252,6 +252,14 @@ command_hologram_disabled: message: "&#f8f8ffʜᴏʟᴏɢʀᴀᴍꜱ &#e05252ᴅɪꜱᴀʙʟᴇᴅ &#f8f8ffꜰᴏʀ ᴀʟʟ ꜱᴘᴀᴡɴᴇʀꜱ" sound: block.note_block.bass +command_hologram_cleared: + message: "%eb9aꜱᴜᴄᴄᴇꜱꜱꜰᴜʟʟʏ &#f8f8ffᴄʟᴇᴀʀᴇᴅ ᴀʟʟ ʜᴏʟᴏɢʀᴀᴍꜱ" + sound: entity.experience_orb.pickup + +command_hologram_clear_error: + message: "&#ff5252ꜰᴀɪʟᴇᴅ ᴛᴏ ᴄʟᴇᴀʀ ʜᴏʟᴏɢʀᴀᴍꜱ" + sound: block.note_block.pling + # Prices command messagess prices_not_available: message: "&#ff5252ꜱᴇʟʟ ɪɴᴛᴇɢʀᴀᴛɪᴏɴ ɪꜱ ɴᴏᴛ ᴀᴠᴀɪʟᴀʙʟᴇ. ᴘʟᴇᴀꜱᴇ ᴄᴏɴꜰɪɢᴜʀᴇ ᴇᴄᴏɴᴏᴍʏ ꜱᴇᴛᴛɪɴɢꜱ."