Skip to content

Commit 8cd3988

Browse files
committed
feat: update spawner loot generation logic and add world spawner unloading functionality
1 parent 4beab81 commit 8cd3988

4 files changed

Lines changed: 95 additions & 31 deletions

File tree

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ allprojects {
1010
apply(plugin = "maven-publish")
1111

1212
group = "github.nighter"
13-
version = "1.6.5"
13+
version = "1.6.6"
1414

1515
repositories {
1616
mavenCentral()

core/src/main/java/github/nighter/smartspawner/spawner/data/SpawnerManager.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,33 @@ public Set<SpawnerData> getSpawnersInWorld(String worldName) {
151151
return worldIndex.get(worldName);
152152
}
153153

154+
/**
155+
* Removes all spawners belonging to a world from in-memory indexes.
156+
* Used when a world unloads so runtime memory only contains active worlds.
157+
*
158+
* @param worldName world being unloaded
159+
* @return IDs of removed spawners for deferred reloading
160+
*/
161+
public Set<String> unloadSpawnersInWorld(String worldName) {
162+
Set<SpawnerData> worldSpawners = worldIndex.get(worldName);
163+
if (worldSpawners == null || worldSpawners.isEmpty()) {
164+
return Collections.emptySet();
165+
}
166+
167+
Set<String> removedSpawnerIds = new HashSet<>();
168+
Set<SpawnerData> snapshot = new HashSet<>(worldSpawners);
169+
170+
for (SpawnerData spawner : snapshot) {
171+
spawner.removeHologram();
172+
removedSpawnerIds.add(spawner.getSpawnerId());
173+
spawners.remove(spawner.getSpawnerId());
174+
locationIndex.entrySet().removeIf(entry -> entry.getValue() == spawner);
175+
}
176+
177+
worldIndex.remove(worldName);
178+
return removedSpawnerIds;
179+
}
180+
154181
public void initializeWithoutLoading() {
155182
// Clear existing data
156183
spawners.clear();

core/src/main/java/github/nighter/smartspawner/spawner/data/WorldEventHandler.java

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,12 @@ public void onWorldUnload(WorldUnloadEvent event) {
9797
// Remove world from processed set
9898
processedWorlds.remove(worldName);
9999

100+
// Flush pending changes before removing runtime references.
101+
// Storage handlers save modified spawners by resolving them from SpawnerManager.
102+
plugin.getSpawnerStorage().flushChanges();
103+
100104
// Unload spawners from this world
101105
unloadSpawnersFromWorld(worldName);
102-
103-
// Save any pending changes before unloading
104-
plugin.getSpawnerStorage().flushChanges();
105106
}
106107

107108
/**
@@ -126,9 +127,20 @@ public void attemptInitialSpawnerLoad() {
126127
SpawnerData spawner = entry.getValue();
127128

128129
if (spawner != null) {
129-
// Successfully loaded spawner
130-
plugin.getSpawnerManager().addSpawnerToIndexes(spawnerId, spawner);
131-
loadedCount++;
130+
String worldName = spawner.getSpawnerLocation() != null && spawner.getSpawnerLocation().getWorld() != null
131+
? spawner.getSpawnerLocation().getWorld().getName()
132+
: null;
133+
134+
if (worldName != null && Bukkit.getWorld(worldName) != null) {
135+
plugin.getSpawnerManager().addSpawnerToIndexes(spawnerId, spawner);
136+
loadedCount++;
137+
} else {
138+
PendingSpawnerData pending = loadPendingSpawnerFromFile(spawnerId);
139+
if (pending != null) {
140+
pendingSpawners.put(spawnerId, pending);
141+
pendingCount++;
142+
}
143+
}
132144
} else {
133145
// Spawner couldn't be loaded, likely due to missing world
134146
// Store as pending for later loading
@@ -186,19 +198,16 @@ private void loadPendingSpawnersForWorld(String worldName) {
186198
* Unload all spawners from a specific world
187199
*/
188200
private void unloadSpawnersFromWorld(String worldName) {
189-
Set<SpawnerData> worldSpawners = plugin.getSpawnerManager().getSpawnersInWorld(worldName);
190-
191-
if (worldSpawners != null && !worldSpawners.isEmpty()) {
192-
int unloadedCount = 0;
193-
194-
for (SpawnerData spawner : new HashSet<>(worldSpawners)) {
195-
// Remove hologram and cleanup
196-
spawner.removeHologram();
197-
unloadedCount++;
198-
}
201+
Set<String> unloadedSpawnerIds = plugin.getSpawnerManager().unloadSpawnersInWorld(worldName);
202+
if (unloadedSpawnerIds.isEmpty()) {
203+
return;
204+
}
199205

200-
logger.info("Unloaded " + unloadedCount + " spawners from world: " + worldName);
206+
for (String spawnerId : unloadedSpawnerIds) {
207+
pendingSpawners.put(spawnerId, new PendingSpawnerData(worldName));
201208
}
209+
210+
logger.info("Unloaded " + unloadedSpawnerIds.size() + " spawners from world: " + worldName);
202211
}
203212

204213
/**
@@ -210,7 +219,10 @@ private PendingSpawnerData loadPendingSpawnerFromFile(String spawnerId) {
210219
if (locationString != null) {
211220
String[] locParts = locationString.split(",");
212221
if (locParts.length >= 1) {
213-
return new PendingSpawnerData(locParts[0]);
222+
String worldName = locParts[0].trim();
223+
if (!worldName.isEmpty()) {
224+
return new PendingSpawnerData(worldName);
225+
}
214226
}
215227
}
216228
} catch (Exception e) {

core/src/main/java/github/nighter/smartspawner/spawner/lootgen/SpawnerLootGenerator.java

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -407,12 +407,13 @@ public void preGenerateLoot(SpawnerData spawner, LootGenerationCallback callback
407407

408408
final int minMobs;
409409
final int maxMobs;
410-
final boolean atCapacity;
410+
final boolean itemStorageFull;
411411

412412
try {
413413
int usedSlots = spawner.getVirtualInventory().getUsedSlots();
414414
int maxSlots = spawner.getMaxSpawnerLootSlots();
415-
atCapacity = usedSlots >= maxSlots && spawner.getSpawnerExp() >= spawner.getMaxStoredExp();
415+
itemStorageFull = usedSlots >= maxSlots;
416+
boolean atCapacity = itemStorageFull && spawner.getSpawnerExp() >= spawner.getMaxStoredExp();
416417

417418
if (atCapacity) {
418419
callback.onLootGenerated(Collections.emptyList(), 0);
@@ -426,16 +427,28 @@ public void preGenerateLoot(SpawnerData spawner, LootGenerationCallback callback
426427
}
427428

428429
Scheduler.runTaskAsync(() -> {
429-
LootResult loot = generateLoot(minMobs, maxMobs, spawner);
430+
LootResult loot;
431+
if (itemStorageFull) {
432+
loot = generateExperienceOnlyLoot(minMobs, maxMobs, spawner);
433+
} else {
434+
loot = generateLoot(minMobs, maxMobs, spawner);
435+
}
436+
430437
callback.onLootGenerated(
431-
loot.items() != null ? new ArrayList<>(loot.items()) : Collections.emptyList(),
432-
loot.experience()
438+
loot.items() != null ? new ArrayList<>(loot.items()) : Collections.emptyList(),
439+
loot.experience()
433440
);
434441
});
435442
} finally {
436443
spawner.getLootGenerationLock().unlock();
437444
}
438445
}
446+
447+
private LootResult generateExperienceOnlyLoot(int minMobs, int maxMobs, SpawnerData spawner) {
448+
int mobCount = random.nextInt(maxMobs - minMobs + 1) + minMobs;
449+
int totalExperience = spawner.getEntityExperienceValue() * mobCount;
450+
return new LootResult(Collections.emptyList(), totalExperience);
451+
}
439452

440453
/**
441454
* Adds pre-generated loot to spawner instantly when timer expires.
@@ -493,14 +506,12 @@ public void addPreGeneratedLoot(SpawnerData spawner, List<ItemStack> items, int
493506
return;
494507
}
495508

496-
final boolean capacityCheck;
497-
498509
try {
499510
int usedSlots = spawner.getVirtualInventory().getUsedSlots();
500511
int maxSlots = spawner.getMaxSpawnerLootSlots();
501-
capacityCheck = usedSlots >= maxSlots && spawner.getSpawnerExp() >= spawner.getMaxStoredExp();
502-
503-
if (capacityCheck) {
512+
boolean isCompletelyFull = usedSlots >= maxSlots && spawner.getSpawnerExp() >= spawner.getMaxStoredExp();
513+
514+
if (isCompletelyFull) {
504515
return;
505516
}
506517
} finally {
@@ -530,8 +541,22 @@ public void addPreGeneratedLoot(SpawnerData spawner, List<ItemStack> items, int
530541
}
531542

532543
if (!validItems.isEmpty()) {
533-
spawner.addItemsAndUpdateSellValue(validItems);
534-
changed = true;
544+
int usedSlots = spawner.getVirtualInventory().getUsedSlots();
545+
int maxSlots = spawner.getMaxSpawnerLootSlots();
546+
547+
if (usedSlots < maxSlots) {
548+
List<ItemStack> itemsToAdd = validItems;
549+
550+
int totalRequiredSlots = calculateRequiredSlots(itemsToAdd, spawner.getVirtualInventory());
551+
if (totalRequiredSlots > maxSlots) {
552+
itemsToAdd = limitItemsToAvailableSlots(itemsToAdd, spawner);
553+
}
554+
555+
if (!itemsToAdd.isEmpty()) {
556+
spawner.addItemsAndUpdateSellValue(itemsToAdd);
557+
changed = true;
558+
}
559+
}
535560
}
536561
}
537562

0 commit comments

Comments
 (0)