@@ -59,6 +59,12 @@ private void scheduleRegionSpecificCheck() {
5959 handleSpawnerStateChange (sd , expectedStop );
6060 }
6161 });
62+ } else {
63+ // Spawner state hasn't changed, but check if it's time to spawn loot
64+ // Only process active spawners that are not stopped
65+ if (sd .getSpawnerActive () && !sd .getSpawnerStop ().get ()) {
66+ checkAndSpawnLoot (sd );
67+ }
6268 }
6369 }
6470 });
@@ -145,6 +151,75 @@ public void deactivateSpawner(SpawnerData spawner) {
145151 spawner .clearPreGeneratedLoot ();
146152 }
147153
154+ /**
155+ * Checks if a spawner should spawn loot based on its timer and spawns if needed.
156+ * This runs independently of GUI updates to ensure loot spawns even when no one is viewing.
157+ *
158+ * @param spawner The spawner to check
159+ */
160+ private void checkAndSpawnLoot (SpawnerData spawner ) {
161+ // Calculate spawn delay
162+ long cachedDelay = spawner .getCachedSpawnDelay ();
163+ if (cachedDelay == 0 ) {
164+ cachedDelay = spawner .getSpawnDelay () * 50L ; // Convert ticks to milliseconds
165+ spawner .setCachedSpawnDelay (cachedDelay );
166+ }
167+
168+ long currentTime = System .currentTimeMillis ();
169+ long lastSpawnTime = spawner .getLastSpawnTime ();
170+ long timeElapsed = currentTime - lastSpawnTime ;
171+
172+ // Check if it's time to spawn loot
173+ if (timeElapsed >= cachedDelay ) {
174+ // Try to acquire lock with short timeout to avoid blocking
175+ try {
176+ if (spawner .getDataLock ().tryLock (50 , java .util .concurrent .TimeUnit .MILLISECONDS )) {
177+ try {
178+ // Double-check time and state after acquiring lock
179+ currentTime = System .currentTimeMillis ();
180+ lastSpawnTime = spawner .getLastSpawnTime ();
181+ timeElapsed = currentTime - lastSpawnTime ;
182+
183+ if (timeElapsed >= cachedDelay && spawner .getSpawnerActive () && !spawner .getSpawnerStop ().get ()) {
184+ Location spawnerLocation = spawner .getSpawnerLocation ();
185+ if (spawnerLocation != null ) {
186+ // Schedule loot spawning on the correct region thread
187+ Scheduler .runLocationTask (spawnerLocation , () -> {
188+ // Final check before spawning
189+ if (!spawner .getSpawnerActive () || spawner .getSpawnerStop ().get ()) {
190+ spawner .clearPreGeneratedLoot ();
191+ return ;
192+ }
193+
194+ // Spawn loot (pre-generated if available, otherwise generate new)
195+ if (spawner .hasPreGeneratedLoot ()) {
196+ java .util .List <org .bukkit .inventory .ItemStack > items = spawner .getAndClearPreGeneratedItems ();
197+ int exp = spawner .getAndClearPreGeneratedExperience ();
198+ plugin .getSpawnerLootGenerator ().addPreGeneratedLoot (spawner , items , exp );
199+ } else {
200+ plugin .getSpawnerLootGenerator ().spawnLootToSpawner (spawner );
201+ }
202+
203+ // Update last spawn time to reset the timer
204+ spawner .setLastSpawnTime (System .currentTimeMillis ());
205+
206+ // Update any open GUIs to show the new loot
207+ if (plugin .getSpawnerGuiViewManager ().hasViewers (spawner )) {
208+ plugin .getSpawnerGuiViewManager ().updateSpawnerMenuViewers (spawner );
209+ }
210+ });
211+ }
212+ }
213+ } finally {
214+ spawner .getDataLock ().unlock ();
215+ }
216+ }
217+ } catch (InterruptedException e ) {
218+ Thread .currentThread ().interrupt ();
219+ }
220+ }
221+ }
222+
148223 public void cleanup () {
149224 executor .shutdown ();
150225 try {
0 commit comments