Skip to content

Commit 6ad045c

Browse files
authored
Merge pull request #15 from NighterDevelopment/copilot/fix-d9d382cb-d31c-49a0-8329-c4975c12be77
Fix duplication exploit by force-closing filter GUI viewers when spawner is destroyed
2 parents ed310ab + dc3cfbb commit 6ad045c

2 files changed

Lines changed: 54 additions & 2 deletions

File tree

core/src/main/java/github/nighter/smartspawner/SmartSpawner.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,10 @@ public SpawnerStorageUI getSpawnerStorageUI() {
371371
return spawnerStorageUI;
372372
}
373373

374+
public FilterConfigUI getFilterConfigUI() {
375+
return filterConfigUI;
376+
}
377+
374378
public GuiLayoutConfig getGuiLayoutConfig() {
375379
return guiLayoutConfig;
376380
}

core/src/main/java/github/nighter/smartspawner/spawner/gui/synchronization/SpawnerGuiViewManager.java

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import github.nighter.smartspawner.SmartSpawner;
44
import github.nighter.smartspawner.spawner.gui.main.SpawnerMenuHolder;
55
import github.nighter.smartspawner.spawner.gui.storage.StoragePageHolder;
6+
import github.nighter.smartspawner.spawner.gui.storage.filter.FilterConfigHolder;
67
import github.nighter.smartspawner.spawner.gui.main.SpawnerMenuUI;
78
import github.nighter.smartspawner.spawner.gui.storage.SpawnerStorageUI;
89
import github.nighter.smartspawner.spawner.gui.layout.GuiLayout;
@@ -63,6 +64,9 @@ public class SpawnerGuiViewManager implements Listener {
6364
private final Map<UUID, SpawnerViewerInfo> playerToSpawnerMap;
6465
private final Map<String, Set<UUID>> spawnerToPlayersMap;
6566
private final Set<Class<? extends InventoryHolder>> validHolderTypes;
67+
68+
// Additional tracking for filter GUI viewers to prevent duplication exploits
69+
private final Map<String, Set<UUID>> spawnerToFilterViewersMap;
6670

6771
// NEW: Separate tracking for main menu viewers only (for timer updates)
6872
private final Map<UUID, SpawnerViewerInfo> mainMenuViewers = new ConcurrentHashMap<>();
@@ -102,7 +106,8 @@ private static class SpawnerViewerInfo {
102106
// Enum to track different viewer types
103107
private enum ViewerType {
104108
MAIN_MENU, // SpawnerMenuHolder - needs timer updates
105-
STORAGE // StoragePageHolder - no timer updates needed
109+
STORAGE, // StoragePageHolder - no timer updates needed
110+
FILTER // FilterConfigHolder - no timer updates needed
106111
}
107112

108113
public SpawnerGuiViewManager(SmartSpawner plugin) {
@@ -112,10 +117,12 @@ public SpawnerGuiViewManager(SmartSpawner plugin) {
112117
this.spawnerMenuUI = plugin.getSpawnerMenuUI();
113118
this.playerToSpawnerMap = new ConcurrentHashMap<>();
114119
this.spawnerToPlayersMap = new ConcurrentHashMap<>();
120+
this.spawnerToFilterViewersMap = new ConcurrentHashMap<>();
115121
this.isTaskRunning = false;
116122
this.validHolderTypes = Set.of(
117123
SpawnerMenuHolder.class,
118-
StoragePageHolder.class
124+
StoragePageHolder.class,
125+
FilterConfigHolder.class
119126
);
120127

121128
// Preload commonly used strings to avoid repeated lookups
@@ -257,6 +264,12 @@ public void trackViewer(UUID playerId, SpawnerData spawner, ViewerType viewerTyp
257264
spawnerToMainMenuViewers.computeIfAbsent(spawner.getSpawnerId(), k -> ConcurrentHashMap.newKeySet())
258265
.add(playerId);
259266
}
267+
268+
// Separately track filter GUI viewers to prevent duplication exploits
269+
if (viewerType == ViewerType.FILTER) {
270+
spawnerToFilterViewersMap.computeIfAbsent(spawner.getSpawnerId(), k -> ConcurrentHashMap.newKeySet())
271+
.add(playerId);
272+
}
260273

261274
// Start update task if we have any viewers (for pending updates processing)
262275
if (!isTaskRunning && !playerToSpawnerMap.isEmpty()) {
@@ -295,6 +308,18 @@ public void untrackViewer(UUID playerId) {
295308
}
296309
}
297310
}
311+
312+
// Also remove from filter viewer tracking if present
313+
if (info != null) {
314+
String spawnerId = info.spawnerData.getSpawnerId();
315+
Set<UUID> filterViewers = spawnerToFilterViewersMap.get(spawnerId);
316+
if (filterViewers != null) {
317+
filterViewers.remove(playerId);
318+
if (filterViewers.isEmpty()) {
319+
spawnerToFilterViewersMap.remove(spawnerId);
320+
}
321+
}
322+
}
298323

299324
// Also remove from pending updates and performance tracking
300325
pendingUpdates.remove(playerId);
@@ -335,6 +360,7 @@ public void clearAllTrackedGuis() {
335360
spawnerToPlayersMap.clear();
336361
mainMenuViewers.clear();
337362
spawnerToMainMenuViewers.clear();
363+
spawnerToFilterViewersMap.clear();
338364
pendingUpdates.clear();
339365
updateFlags.clear();
340366
lastTimerUpdate.clear();
@@ -437,6 +463,9 @@ public void onInventoryOpen(InventoryOpenEvent event) {
437463
} else if (holder instanceof StoragePageHolder storageHolder) {
438464
spawnerData = storageHolder.getSpawnerData();
439465
viewerType = ViewerType.STORAGE;
466+
} else if (holder instanceof FilterConfigHolder filterHolder) {
467+
spawnerData = filterHolder.getSpawnerData();
468+
viewerType = ViewerType.FILTER;
440469
}
441470

442471
if (spawnerData != null && viewerType != null) {
@@ -1347,6 +1376,25 @@ public void closeAllViewersInventory(SpawnerData spawner) {
13471376
}
13481377
}
13491378
}
1379+
1380+
// Force close filter GUI viewers to prevent duplication exploits
1381+
Set<UUID> filterViewers = spawnerToFilterViewersMap.get(spawnerId);
1382+
if (filterViewers != null && !filterViewers.isEmpty()) {
1383+
// Create a copy to avoid concurrent modification
1384+
Set<UUID> filterViewersCopy = new HashSet<>(filterViewers);
1385+
for (UUID viewerId : filterViewersCopy) {
1386+
Player viewer = Bukkit.getPlayer(viewerId);
1387+
if (viewer != null && viewer.isOnline()) {
1388+
// Check if they actually have a filter GUI open for this spawner
1389+
Inventory openInventory = viewer.getOpenInventory().getTopInventory();
1390+
if (openInventory != null && openInventory.getHolder(false) instanceof FilterConfigHolder filterHolder) {
1391+
if (filterHolder.getSpawnerData().getSpawnerId().equals(spawnerId)) {
1392+
viewer.closeInventory();
1393+
}
1394+
}
1395+
}
1396+
}
1397+
}
13501398

13511399
// Check for stacker viewers if that functionality exists
13521400
if (plugin.getSpawnerStackerHandler() != null) {

0 commit comments

Comments
 (0)