55import github .nighter .smartspawner .spawner .gui .storage .StoragePageHolder ;
66import github .nighter .smartspawner .spawner .gui .main .SpawnerMenuUI ;
77import github .nighter .smartspawner .spawner .gui .storage .SpawnerStorageUI ;
8+ import github .nighter .smartspawner .spawner .gui .layout .GuiLayout ;
9+ import github .nighter .smartspawner .spawner .gui .layout .GuiButton ;
10+ import github .nighter .smartspawner .spawner .gui .synchronization .ItemUpdater ;
811import github .nighter .smartspawner .spawner .properties .SpawnerData ;
912import github .nighter .smartspawner .language .LanguageManager ;
1013import github .nighter .smartspawner .Scheduler ;
@@ -40,10 +43,10 @@ public class SpawnerGuiViewManager implements Listener {
4043 private static final long BATCH_PROCESS_INTERVAL = 5L ; // Process batches every 250ms
4144 private static final int MAX_PLAYERS_PER_BATCH = 10 ; // Limit players processed per batch
4245
43- // GUI slot constants
44- private static final int CHEST_SLOT = 11 ;
45- private static final int SPAWNER_INFO_SLOT = 13 ;
46- private static final int EXP_SLOT = 15 ;
46+ // Cached slot positions - initialized once when config loads, re-initialized on reload
47+ private volatile int cachedStorageSlot = - 1 ;
48+ private volatile int cachedExpSlot = - 1 ;
49+ private volatile int cachedSpawnerInfoSlot = - 1 ;
4750
4851 // Update flags - using bit flags for efficient state tracking
4952 private static final int UPDATE_CHEST = 1 ;
@@ -117,6 +120,9 @@ public SpawnerGuiViewManager(SmartSpawner plugin) {
117120
118121 // Preload commonly used strings to avoid repeated lookups
119122 initCachedStrings ();
123+
124+ // Initialize all slot positions from layout configuration
125+ initializeSlotPositions ();
120126 }
121127
122128 private void initCachedStrings () {
@@ -335,9 +341,84 @@ public void clearAllTrackedGuis() {
335341 lastTimerValue .clear ();
336342 }
337343
338- // ===============================================================
339- // Event Handlers
340- // ===============================================================
344+ /**
345+ * Initialize all GUI slot positions from the current layout configuration.
346+ * This is called once during construction and again when layout is reloaded
347+ * for optimal performance.
348+ */
349+ private void initializeSlotPositions () {
350+ // Get the current layout
351+ GuiLayout layout = plugin .getGuiLayoutConfig ().getCurrentMainLayout ();
352+ if (layout == null ) {
353+ // Set all slots to -1 if no layout is available
354+ cachedStorageSlot = -1 ;
355+ cachedExpSlot = -1 ;
356+ cachedSpawnerInfoSlot = -1 ;
357+ return ;
358+ }
359+
360+ // Initialize storage slot
361+ GuiButton storageButton = layout .getButton ("storage" );
362+ cachedStorageSlot = storageButton != null ? storageButton .getSlot () : -1 ;
363+
364+ // Initialize exp slot
365+ GuiButton expButton = layout .getButton ("exp" );
366+ cachedExpSlot = expButton != null ? expButton .getSlot () : -1 ;
367+
368+ // Initialize spawner info slot using the same logic as SpawnerMenuUI
369+ GuiButton spawnerInfoButton = null ;
370+
371+ // Check for shop integration to determine which button to use
372+ if (plugin .hasSellIntegration ()) {
373+ spawnerInfoButton = layout .getButton ("spawner_info_with_shop" );
374+ }
375+
376+ if (spawnerInfoButton == null ) {
377+ spawnerInfoButton = layout .getButton ("spawner_info_no_shop" );
378+ }
379+
380+ if (spawnerInfoButton == null ) {
381+ spawnerInfoButton = layout .getButton ("spawner_info" );
382+ }
383+
384+ cachedSpawnerInfoSlot = spawnerInfoButton != null ? spawnerInfoButton .getSlot () : -1 ;
385+ }
386+
387+ /**
388+ * Get the storage slot from the cached layout configuration.
389+ *
390+ * @return the slot number for the storage button, or -1 if not found
391+ */
392+ private int getStorageSlot () {
393+ return cachedStorageSlot ;
394+ }
395+
396+ /**
397+ * Get the exp slot from the cached layout configuration.
398+ *
399+ * @return the slot number for the exp button, or -1 if not found
400+ */
401+ private int getExpSlot () {
402+ return cachedExpSlot ;
403+ }
404+
405+ /**
406+ * Get the spawner info slot from the cached layout configuration.
407+ *
408+ * @return the slot number for the spawner info button, or -1 if not found
409+ */
410+ private int getSpawnerInfoSlot () {
411+ return cachedSpawnerInfoSlot ;
412+ }
413+
414+ /**
415+ * Clear all cached slot positions and re-initialize them when GUI layout changes.
416+ * This method is called when layout configuration is reloaded.
417+ */
418+ public void clearSlotCache () {
419+ // Re-initialize all slot positions from the updated layout
420+ initializeSlotPositions ();
421+ }
341422
342423 @ EventHandler (priority = EventPriority .MONITOR , ignoreCancelled = true )
343424 public void onInventoryOpen (InventoryOpenEvent event ) {
@@ -518,9 +599,12 @@ private void updateGuiForSpawnerInfo() {
518599 InventoryHolder holder = openInventory .getHolder (false );
519600
520601 if (holder instanceof SpawnerMenuHolder ) {
521- updateSpawnerInfoItemTimerOptimized (openInventory , spawner , finalTimerValue );
522- // Force inventory update to ensure changes are visible to the player
523- player .updateInventory ();
602+ int spawnerInfoSlot = getSpawnerInfoSlot ();
603+ if (spawnerInfoSlot >= 0 ) {
604+ updateSpawnerInfoItemTimerOptimized (openInventory , spawner , finalTimerValue , spawnerInfoSlot );
605+ // Force inventory update to ensure changes are visible to the player
606+ player .updateInventory ();
607+ }
524608 } else {
525609 // Player no longer has main menu open, remove from main menu tracking
526610 untrackViewer (finalPlayerId );
@@ -581,18 +665,27 @@ private void processInventoryUpdate(Player player, Inventory inventory, SpawnerD
581665 boolean needsUpdate = false ;
582666
583667 if ((flags & UPDATE_CHEST ) != 0 ) {
584- updateChestItem (inventory , spawner );
585- needsUpdate = true ;
668+ int storageSlot = getStorageSlot ();
669+ if (storageSlot >= 0 ) {
670+ updateChestItem (inventory , spawner , storageSlot );
671+ needsUpdate = true ;
672+ }
586673 }
587674
588675 if ((flags & UPDATE_INFO ) != 0 ) {
589- updateSpawnerInfoItem (inventory , spawner , player );
590- needsUpdate = true ;
676+ int spawnerInfoSlot = getSpawnerInfoSlot ();
677+ if (spawnerInfoSlot >= 0 ) {
678+ updateSpawnerInfoItem (inventory , spawner , player , spawnerInfoSlot );
679+ needsUpdate = true ;
680+ }
591681 }
592682
593683 if ((flags & UPDATE_EXP ) != 0 ) {
594- updateExpItem (inventory , spawner );
595- needsUpdate = true ;
684+ int expSlot = getExpSlot ();
685+ if (expSlot >= 0 ) {
686+ updateExpItem (inventory , spawner , expSlot );
687+ needsUpdate = true ;
688+ }
596689 }
597690
598691 if (needsUpdate ) {
@@ -677,7 +770,7 @@ private void updateMainMenuViewers(SpawnerData spawner) {
677770 return ;
678771 }
679772
680- updateSpawnerInfoItemTimerOptimized (openInv , spawner , finalTimerValue );
773+ updateSpawnerInfoItemTimerOptimized (openInv , spawner , finalTimerValue , getSpawnerInfoSlot () );
681774 viewer .updateInventory ();
682775 });
683776 }
@@ -711,9 +804,12 @@ public void forceTimerUpdate(Player player, SpawnerData spawner) {
711804
712805 InventoryHolder holder = openInventory .getHolder (false );
713806 if (holder instanceof SpawnerMenuHolder ) {
714- updateSpawnerInfoItemTimer (openInventory , spawner );
715- // Force inventory update to ensure changes are visible immediately
716- player .updateInventory ();
807+ int spawnerInfoSlot = getSpawnerInfoSlot ();
808+ if (spawnerInfoSlot >= 0 ) {
809+ updateSpawnerInfoItemTimer (openInventory , spawner , spawnerInfoSlot );
810+ // Force inventory update to ensure changes are visible immediately
811+ player .updateInventory ();
812+ }
717813 }
718814 });
719815 }
@@ -867,9 +963,11 @@ public void updateSpawnerMenuGui(Player player, SpawnerData spawner, boolean for
867963 updateFlags .put (player .getUniqueId (), UPDATE_ALL );
868964 }
869965
870- private void updateSpawnerInfoItem (Inventory inventory , SpawnerData spawner , Player player ) {
966+ private void updateSpawnerInfoItem (Inventory inventory , SpawnerData spawner , Player player , int spawnerInfoSlot ) {
967+ if (spawnerInfoSlot < 0 ) return ;
968+
871969 // Get the current spawner info item from the inventory
872- ItemStack currentSpawnerItem = inventory .getItem (SPAWNER_INFO_SLOT );
970+ ItemStack currentSpawnerItem = inventory .getItem (spawnerInfoSlot );
873971 if (currentSpawnerItem == null || !currentSpawnerItem .hasItemMeta ()) return ;
874972
875973 // Create a freshly generated spawner info item using the method from SpawnerMenuUI
@@ -881,7 +979,7 @@ private void updateSpawnerInfoItem(Inventory inventory, SpawnerData spawner, Pla
881979 preserveTimerInfo (currentSpawnerItem , newSpawnerItem );
882980
883981 // Update the item in the inventory
884- inventory .setItem (SPAWNER_INFO_SLOT , newSpawnerItem );
982+ inventory .setItem (spawnerInfoSlot , newSpawnerItem );
885983 }
886984 }
887985
@@ -962,13 +1060,15 @@ private void preserveTimerInfo(ItemStack currentItem, ItemStack newItem) {
9621060 * Optimized version of updateSpawnerInfoItemTimer that accepts pre-calculated timer value
9631061 * to avoid redundant calculations and improve performance.
9641062 */
965- private void updateSpawnerInfoItemTimerOptimized (Inventory inventory , SpawnerData spawner , String timeDisplay ) {
1063+ private void updateSpawnerInfoItemTimerOptimized (Inventory inventory , SpawnerData spawner , String timeDisplay , int spawnerInfoSlot ) {
9661064 // Skip timer updates if GUI doesn't use timer placeholders
9671065 if (!isTimerPlaceholdersEnabled ()) {
9681066 return ;
9691067 }
9701068
971- ItemStack spawnerItem = inventory .getItem (SPAWNER_INFO_SLOT );
1069+ if (spawnerInfoSlot < 0 ) return ;
1070+
1071+ ItemStack spawnerItem = inventory .getItem (spawnerInfoSlot );
9721072 if (spawnerItem == null || !spawnerItem .hasItemMeta ()) return ;
9731073
9741074 ItemMeta meta = spawnerItem .getItemMeta ();
@@ -1004,17 +1104,19 @@ private void updateSpawnerInfoItemTimerOptimized(Inventory inventory, SpawnerDat
10041104 meta .setLore (updatedLore );
10051105 spawnerItem .setItemMeta (meta );
10061106 // Update the inventory directly to ensure changes are applied
1007- inventory .setItem (SPAWNER_INFO_SLOT , spawnerItem );
1107+ inventory .setItem (spawnerInfoSlot , spawnerItem );
10081108 }
10091109 }
10101110
1011- private void updateSpawnerInfoItemTimer (Inventory inventory , SpawnerData spawner ) {
1111+ private void updateSpawnerInfoItemTimer (Inventory inventory , SpawnerData spawner , int spawnerInfoSlot ) {
10121112 // Skip timer updates if GUI doesn't use timer placeholders
10131113 if (!isTimerPlaceholdersEnabled ()) {
10141114 return ;
10151115 }
10161116
1017- ItemStack spawnerItem = inventory .getItem (SPAWNER_INFO_SLOT );
1117+ if (spawnerInfoSlot < 0 ) return ;
1118+
1119+ ItemStack spawnerItem = inventory .getItem (spawnerInfoSlot );
10181120 if (spawnerItem == null || !spawnerItem .hasItemMeta ()) return ;
10191121
10201122 ItemMeta meta = spawnerItem .getItemMeta ();
@@ -1064,7 +1166,7 @@ private void updateSpawnerInfoItemTimer(Inventory inventory, SpawnerData spawner
10641166 meta .setLore (updatedLore );
10651167 spawnerItem .setItemMeta (meta );
10661168 // Update the inventory directly to ensure changes are applied
1067- inventory .setItem (SPAWNER_INFO_SLOT , spawnerItem );
1169+ inventory .setItem (spawnerInfoSlot , spawnerItem );
10681170 }
10691171 }
10701172
@@ -1199,31 +1301,35 @@ private String formatTime(long milliseconds) {
11991301 return String .format ("%02d:%02d" , minutes , seconds );
12001302 }
12011303
1202- private void updateChestItem (Inventory inventory , SpawnerData spawner ) {
1304+ private void updateChestItem (Inventory inventory , SpawnerData spawner , int storageSlot ) {
1305+ if (storageSlot < 0 ) return ;
1306+
12031307 // Get the chest item from the inventory
1204- ItemStack currentChestItem = inventory .getItem (CHEST_SLOT );
1308+ ItemStack currentChestItem = inventory .getItem (storageSlot );
12051309 if (currentChestItem == null || !currentChestItem .hasItemMeta ()) return ;
12061310
12071311 // Create a freshly generated chest item using the optimized method from SpawnerMenuUI
12081312 ItemStack newChestItem = spawnerMenuUI .createLootStorageItem (spawner );
12091313
12101314 // If the new item is different from current item, update it
12111315 if (!ItemUpdater .areItemsEqual (currentChestItem , newChestItem )) {
1212- inventory .setItem (CHEST_SLOT , newChestItem );
1316+ inventory .setItem (storageSlot , newChestItem );
12131317 }
12141318 }
12151319
1216- private void updateExpItem (Inventory inventory , SpawnerData spawner ) {
1320+ private void updateExpItem (Inventory inventory , SpawnerData spawner , int expSlot ) {
1321+ if (expSlot < 0 ) return ;
1322+
12171323 // Get the exp item from the inventory
1218- ItemStack currentExpItem = inventory .getItem (EXP_SLOT );
1324+ ItemStack currentExpItem = inventory .getItem (expSlot );
12191325 if (currentExpItem == null || !currentExpItem .hasItemMeta ()) return ;
12201326
12211327 // Create a freshly generated exp item using the method from SpawnerMenuUI
12221328 ItemStack newExpItem = spawnerMenuUI .createExpItem (spawner );
12231329
12241330 // If the new item is different from current item, update it
12251331 if (!ItemUpdater .areItemsEqual (currentExpItem , newExpItem )) {
1226- inventory .setItem (EXP_SLOT , newExpItem );
1332+ inventory .setItem (expSlot , newExpItem );
12271333 }
12281334 }
12291335
0 commit comments