Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,10 @@ private void setupBtatsMetrics() {
public void reload() {
// reload gui components
guiLayoutConfig.reloadLayouts();

// Clear spawner info slot cache since layout may have changed
spawnerGuiViewManager.clearSlotCache();

spawnerStorageAction.reload();
spawnerStorageUI.reload();
filterConfigUI.reload();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import github.nighter.smartspawner.spawner.gui.storage.StoragePageHolder;
import github.nighter.smartspawner.spawner.gui.main.SpawnerMenuUI;
import github.nighter.smartspawner.spawner.gui.storage.SpawnerStorageUI;
import github.nighter.smartspawner.spawner.gui.layout.GuiLayout;
import github.nighter.smartspawner.spawner.gui.layout.GuiButton;
import github.nighter.smartspawner.spawner.gui.synchronization.ItemUpdater;
import github.nighter.smartspawner.spawner.properties.SpawnerData;
import github.nighter.smartspawner.language.LanguageManager;
import github.nighter.smartspawner.Scheduler;
Expand Down Expand Up @@ -40,10 +43,10 @@ public class SpawnerGuiViewManager implements Listener {
private static final long BATCH_PROCESS_INTERVAL = 5L; // Process batches every 250ms
private static final int MAX_PLAYERS_PER_BATCH = 10; // Limit players processed per batch

// GUI slot constants
private static final int CHEST_SLOT = 11;
private static final int SPAWNER_INFO_SLOT = 13;
private static final int EXP_SLOT = 15;
// Cached slot positions - initialized once when config loads, re-initialized on reload
private volatile int cachedStorageSlot = -1;
private volatile int cachedExpSlot = -1;
private volatile int cachedSpawnerInfoSlot = -1;

// Update flags - using bit flags for efficient state tracking
private static final int UPDATE_CHEST = 1;
Expand Down Expand Up @@ -117,6 +120,9 @@ public SpawnerGuiViewManager(SmartSpawner plugin) {

// Preload commonly used strings to avoid repeated lookups
initCachedStrings();

// Initialize all slot positions from layout configuration
initializeSlotPositions();
}

private void initCachedStrings() {
Expand Down Expand Up @@ -335,9 +341,84 @@ public void clearAllTrackedGuis() {
lastTimerValue.clear();
}

// ===============================================================
// Event Handlers
// ===============================================================
/**
* Initialize all GUI slot positions from the current layout configuration.
* This is called once during construction and again when layout is reloaded
* for optimal performance.
*/
private void initializeSlotPositions() {
// Get the current layout
GuiLayout layout = plugin.getGuiLayoutConfig().getCurrentMainLayout();
if (layout == null) {
// Set all slots to -1 if no layout is available
cachedStorageSlot = -1;
cachedExpSlot = -1;
cachedSpawnerInfoSlot = -1;
return;
}

// Initialize storage slot
GuiButton storageButton = layout.getButton("storage");
cachedStorageSlot = storageButton != null ? storageButton.getSlot() : -1;

// Initialize exp slot
GuiButton expButton = layout.getButton("exp");
cachedExpSlot = expButton != null ? expButton.getSlot() : -1;

// Initialize spawner info slot using the same logic as SpawnerMenuUI
GuiButton spawnerInfoButton = null;

// Check for shop integration to determine which button to use
if (plugin.hasSellIntegration()) {
spawnerInfoButton = layout.getButton("spawner_info_with_shop");
}

if (spawnerInfoButton == null) {
spawnerInfoButton = layout.getButton("spawner_info_no_shop");
}

if (spawnerInfoButton == null) {
spawnerInfoButton = layout.getButton("spawner_info");
}

cachedSpawnerInfoSlot = spawnerInfoButton != null ? spawnerInfoButton.getSlot() : -1;
}

/**
* Get the storage slot from the cached layout configuration.
*
* @return the slot number for the storage button, or -1 if not found
*/
private int getStorageSlot() {
return cachedStorageSlot;
}

/**
* Get the exp slot from the cached layout configuration.
*
* @return the slot number for the exp button, or -1 if not found
*/
private int getExpSlot() {
return cachedExpSlot;
}

/**
* Get the spawner info slot from the cached layout configuration.
*
* @return the slot number for the spawner info button, or -1 if not found
*/
private int getSpawnerInfoSlot() {
return cachedSpawnerInfoSlot;
}

/**
* Clear all cached slot positions and re-initialize them when GUI layout changes.
* This method is called when layout configuration is reloaded.
*/
public void clearSlotCache() {
// Re-initialize all slot positions from the updated layout
initializeSlotPositions();
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onInventoryOpen(InventoryOpenEvent event) {
Expand Down Expand Up @@ -518,9 +599,12 @@ private void updateGuiForSpawnerInfo() {
InventoryHolder holder = openInventory.getHolder(false);

if (holder instanceof SpawnerMenuHolder) {
updateSpawnerInfoItemTimerOptimized(openInventory, spawner, finalTimerValue);
// Force inventory update to ensure changes are visible to the player
player.updateInventory();
int spawnerInfoSlot = getSpawnerInfoSlot();
if (spawnerInfoSlot >= 0) {
updateSpawnerInfoItemTimerOptimized(openInventory, spawner, finalTimerValue, spawnerInfoSlot);
// Force inventory update to ensure changes are visible to the player
player.updateInventory();
}
} else {
// Player no longer has main menu open, remove from main menu tracking
untrackViewer(finalPlayerId);
Expand Down Expand Up @@ -581,18 +665,27 @@ private void processInventoryUpdate(Player player, Inventory inventory, SpawnerD
boolean needsUpdate = false;

if ((flags & UPDATE_CHEST) != 0) {
updateChestItem(inventory, spawner);
needsUpdate = true;
int storageSlot = getStorageSlot();
if (storageSlot >= 0) {
updateChestItem(inventory, spawner, storageSlot);
needsUpdate = true;
}
}

if ((flags & UPDATE_INFO) != 0) {
updateSpawnerInfoItem(inventory, spawner, player);
needsUpdate = true;
int spawnerInfoSlot = getSpawnerInfoSlot();
if (spawnerInfoSlot >= 0) {
updateSpawnerInfoItem(inventory, spawner, player, spawnerInfoSlot);
needsUpdate = true;
}
}

if ((flags & UPDATE_EXP) != 0) {
updateExpItem(inventory, spawner);
needsUpdate = true;
int expSlot = getExpSlot();
if (expSlot >= 0) {
updateExpItem(inventory, spawner, expSlot);
needsUpdate = true;
}
}

if (needsUpdate) {
Expand Down Expand Up @@ -677,7 +770,7 @@ private void updateMainMenuViewers(SpawnerData spawner) {
return;
}

updateSpawnerInfoItemTimerOptimized(openInv, spawner, finalTimerValue);
updateSpawnerInfoItemTimerOptimized(openInv, spawner, finalTimerValue, getSpawnerInfoSlot());
viewer.updateInventory();
});
}
Expand Down Expand Up @@ -711,9 +804,12 @@ public void forceTimerUpdate(Player player, SpawnerData spawner) {

InventoryHolder holder = openInventory.getHolder(false);
if (holder instanceof SpawnerMenuHolder) {
updateSpawnerInfoItemTimer(openInventory, spawner);
// Force inventory update to ensure changes are visible immediately
player.updateInventory();
int spawnerInfoSlot = getSpawnerInfoSlot();
if (spawnerInfoSlot >= 0) {
updateSpawnerInfoItemTimer(openInventory, spawner, spawnerInfoSlot);
// Force inventory update to ensure changes are visible immediately
player.updateInventory();
}
}
});
}
Expand Down Expand Up @@ -867,9 +963,11 @@ public void updateSpawnerMenuGui(Player player, SpawnerData spawner, boolean for
updateFlags.put(player.getUniqueId(), UPDATE_ALL);
}

private void updateSpawnerInfoItem(Inventory inventory, SpawnerData spawner, Player player) {
private void updateSpawnerInfoItem(Inventory inventory, SpawnerData spawner, Player player, int spawnerInfoSlot) {
if (spawnerInfoSlot < 0) return;

// Get the current spawner info item from the inventory
ItemStack currentSpawnerItem = inventory.getItem(SPAWNER_INFO_SLOT);
ItemStack currentSpawnerItem = inventory.getItem(spawnerInfoSlot);
if (currentSpawnerItem == null || !currentSpawnerItem.hasItemMeta()) return;

// Create a freshly generated spawner info item using the method from SpawnerMenuUI
Expand All @@ -881,7 +979,7 @@ private void updateSpawnerInfoItem(Inventory inventory, SpawnerData spawner, Pla
preserveTimerInfo(currentSpawnerItem, newSpawnerItem);

// Update the item in the inventory
inventory.setItem(SPAWNER_INFO_SLOT, newSpawnerItem);
inventory.setItem(spawnerInfoSlot, newSpawnerItem);
}
}

Expand Down Expand Up @@ -962,13 +1060,15 @@ private void preserveTimerInfo(ItemStack currentItem, ItemStack newItem) {
* Optimized version of updateSpawnerInfoItemTimer that accepts pre-calculated timer value
* to avoid redundant calculations and improve performance.
*/
private void updateSpawnerInfoItemTimerOptimized(Inventory inventory, SpawnerData spawner, String timeDisplay) {
private void updateSpawnerInfoItemTimerOptimized(Inventory inventory, SpawnerData spawner, String timeDisplay, int spawnerInfoSlot) {
// Skip timer updates if GUI doesn't use timer placeholders
if (!isTimerPlaceholdersEnabled()) {
return;
}

ItemStack spawnerItem = inventory.getItem(SPAWNER_INFO_SLOT);
if (spawnerInfoSlot < 0) return;

ItemStack spawnerItem = inventory.getItem(spawnerInfoSlot);
if (spawnerItem == null || !spawnerItem.hasItemMeta()) return;

ItemMeta meta = spawnerItem.getItemMeta();
Expand Down Expand Up @@ -1004,17 +1104,19 @@ private void updateSpawnerInfoItemTimerOptimized(Inventory inventory, SpawnerDat
meta.setLore(updatedLore);
spawnerItem.setItemMeta(meta);
// Update the inventory directly to ensure changes are applied
inventory.setItem(SPAWNER_INFO_SLOT, spawnerItem);
inventory.setItem(spawnerInfoSlot, spawnerItem);
}
}

private void updateSpawnerInfoItemTimer(Inventory inventory, SpawnerData spawner) {
private void updateSpawnerInfoItemTimer(Inventory inventory, SpawnerData spawner, int spawnerInfoSlot) {
// Skip timer updates if GUI doesn't use timer placeholders
if (!isTimerPlaceholdersEnabled()) {
return;
}

ItemStack spawnerItem = inventory.getItem(SPAWNER_INFO_SLOT);
if (spawnerInfoSlot < 0) return;

ItemStack spawnerItem = inventory.getItem(spawnerInfoSlot);
if (spawnerItem == null || !spawnerItem.hasItemMeta()) return;

ItemMeta meta = spawnerItem.getItemMeta();
Expand Down Expand Up @@ -1064,7 +1166,7 @@ private void updateSpawnerInfoItemTimer(Inventory inventory, SpawnerData spawner
meta.setLore(updatedLore);
spawnerItem.setItemMeta(meta);
// Update the inventory directly to ensure changes are applied
inventory.setItem(SPAWNER_INFO_SLOT, spawnerItem);
inventory.setItem(spawnerInfoSlot, spawnerItem);
}
}

Expand Down Expand Up @@ -1199,31 +1301,35 @@ private String formatTime(long milliseconds) {
return String.format("%02d:%02d", minutes, seconds);
}

private void updateChestItem(Inventory inventory, SpawnerData spawner) {
private void updateChestItem(Inventory inventory, SpawnerData spawner, int storageSlot) {
if (storageSlot < 0) return;

// Get the chest item from the inventory
ItemStack currentChestItem = inventory.getItem(CHEST_SLOT);
ItemStack currentChestItem = inventory.getItem(storageSlot);
if (currentChestItem == null || !currentChestItem.hasItemMeta()) return;

// Create a freshly generated chest item using the optimized method from SpawnerMenuUI
ItemStack newChestItem = spawnerMenuUI.createLootStorageItem(spawner);

// If the new item is different from current item, update it
if (!ItemUpdater.areItemsEqual(currentChestItem, newChestItem)) {
inventory.setItem(CHEST_SLOT, newChestItem);
inventory.setItem(storageSlot, newChestItem);
}
}

private void updateExpItem(Inventory inventory, SpawnerData spawner) {
private void updateExpItem(Inventory inventory, SpawnerData spawner, int expSlot) {
if (expSlot < 0) return;

// Get the exp item from the inventory
ItemStack currentExpItem = inventory.getItem(EXP_SLOT);
ItemStack currentExpItem = inventory.getItem(expSlot);
if (currentExpItem == null || !currentExpItem.hasItemMeta()) return;

// Create a freshly generated exp item using the method from SpawnerMenuUI
ItemStack newExpItem = spawnerMenuUI.createExpItem(spawner);

// If the new item is different from current item, update it
if (!ItemUpdater.areItemsEqual(currentExpItem, newExpItem)) {
inventory.setItem(EXP_SLOT, newExpItem);
inventory.setItem(expSlot, newExpItem);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,19 @@ private void createDefaultLayoutFileWithHeader(String layoutName, File destinati
plugin.saveResource(resourcePath, true);

FileConfiguration config = YamlConfiguration.loadConfiguration(destinationFile);
config.set(GUI_LAYOUT_VERSION_KEY, currentVersion);
config.save(destinationFile);

// Create a new configuration with version at the top
FileConfiguration newConfig = new YamlConfiguration();
newConfig.set(GUI_LAYOUT_VERSION_KEY, currentVersion);

// Copy all existing keys to preserve order, with version first
for (String key : config.getKeys(false)) {
if (!key.equals(GUI_LAYOUT_VERSION_KEY)) {
newConfig.set(key, config.get(key));
}
}

newConfig.save(destinationFile);

} catch (Exception e) {
plugin.getLogger().log(Level.WARNING, "Failed to create default layout file with header for " + layoutName + "/" + fileName, e);
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/resources/gui_layouts/DonutSMP/main_gui.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
gui_layout_version: 1.5.1

# DonutSMP Main GUI Layout Configuration
# This configures the main spawner GUI layout (27 slots, 3 rows of 9)
# Valid materials can be found at: https://jd.papermc.io/paper/1.21.8/org/bukkit/Material.html
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/resources/gui_layouts/DonutSMP/storage_gui.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
gui_layout_version: 1.5.1

# DonutSMP Storage GUI Layout Configuration
# Slot positions: 1-9 corresponds to inventory slots 46-54 (bottom row)
# Valid materials can be found at: https://jd.papermc.io/paper/1.21.6/org/bukkit/Material.html
Expand Down Expand Up @@ -39,4 +41,3 @@ buttons:
slot: 8
material: DROPPER
enabled: true

2 changes: 2 additions & 0 deletions core/src/main/resources/gui_layouts/default/main_gui.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
gui_layout_version: 1.5.1

# Default Main GUI Layout Configuration
# This configures the main spawner GUI layout (27 slots, 3 rows of 9)
# Valid materials can be found at: https://jd.papermc.io/paper/1.21.8/org/bukkit/Material.html
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/resources/gui_layouts/default/storage_gui.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
gui_layout_version: 1.5.1

# Default Storage GUI Layout Configuration
# Slot positions: 1-9 corresponds to inventory slots 46-54 (bottom row)
# Valid materials can be found at: https://jd.papermc.io/paper/1.21.8/org/bukkit/Material.html
Expand Down