Skip to content

Commit 5d076f2

Browse files
committed
Prevent schedule error on shutdown #32
1 parent 5380a59 commit 5d076f2

5 files changed

Lines changed: 64 additions & 49 deletions

File tree

src/main/java/com/wasteofplastic/invswitcher/InvSwitcher.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package com.wasteofplastic.invswitcher;
22

33

4+
import java.util.Arrays;
45
import java.util.HashSet;
6+
import java.util.List;
57
import java.util.Objects;
68
import java.util.Set;
79
import java.util.stream.Collectors;
810

911
import org.bukkit.Bukkit;
12+
import org.bukkit.Material;
1013
import org.bukkit.World;
1114

1215
import com.wasteofplastic.invswitcher.listeners.PlayerListener;
@@ -23,6 +26,9 @@
2326
*/
2427
public class InvSwitcher extends Addon {
2528

29+
protected static final List<Material> MAT = Arrays.stream(Material.values())
30+
.filter(m -> !m.name().startsWith("LEGACY")).toList();
31+
2632
private Store store;
2733

2834
private Settings settings;
@@ -90,7 +96,7 @@ public void onEnable() {
9096
public void onDisable() {
9197
// save cache
9298
if (store != null) {
93-
getStore().saveOnlinePlayers();
99+
getStore().saveOnShutdown();
94100
}
95101

96102
}

src/main/java/com/wasteofplastic/invswitcher/Store.java

Lines changed: 52 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
*
5858
*/
5959
public class Store {
60-
private static final Material[] MAT = Material.values();
6160
private static final CharSequence THE_END = "_the_end";
6261
private static final CharSequence NETHER = "_nether";
6362
private final Database<InventoryStorage> database;
@@ -198,7 +197,7 @@ private InventoryStorage getInv(Player player) {
198197
* @param world - the world that is associated with these items/elements
199198
*/
200199
public void storeInventory(Player player, World world) {
201-
storeAndSave(player, world);
200+
storeAndSave(player, world, false);
202201
clearPlayer(player);
203202
// Done!
204203
}
@@ -207,8 +206,9 @@ public void storeInventory(Player player, World world) {
207206
* Store and save the player to the database
208207
* @param player - player
209208
* @param world - world to save
209+
* @param shutdown - true if this is a shutdown save
210210
*/
211-
public void storeAndSave(Player player, World world) {
211+
public void storeAndSave(Player player, World world, boolean shutdown) {
212212
// Get the player's store
213213
InventoryStorage store = getInv(player);
214214
// Do not differentiate between world environments
@@ -249,57 +249,66 @@ public void storeAndSave(Player player, World world) {
249249
store.setEnderChest(overworldName, contents);
250250
}
251251
if (addon.getSettings().isStatistics()) {
252-
saveStats(store, player, overworldName).thenAccept(database::saveObjectAsync);
252+
saveStats(store, player, overworldName, shutdown).thenAccept(database::saveObjectAsync);
253253
return;
254254
}
255255
database.saveObjectAsync(store);
256256
}
257257

258-
private CompletableFuture<InventoryStorage> saveStats(InventoryStorage store, Player player, String worldName) {
258+
private CompletableFuture<InventoryStorage> saveStats(InventoryStorage store, Player player, String worldName,
259+
boolean shutdown) {
259260
CompletableFuture<InventoryStorage> result = new CompletableFuture<>();
260261
store.clearStats(worldName);
262+
261263
// Statistics
262-
Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> {
263-
Arrays.stream(Statistic.values()).forEach(s -> {
264-
Map<Material, Integer> map;
265-
Map<EntityType, Integer> entMap;
266-
switch (s.getType()) {
267-
case BLOCK -> {
268-
map = Arrays.stream(MAT).filter(Material::isBlock).filter(m -> !m.isLegacy())
269-
.filter(m -> player.getStatistic(s, m) > 0)
270-
.collect(Collectors.toMap(k -> k, v -> player.getStatistic(s, v)));
271-
if (!map.isEmpty()) {
272-
store.getBlockStats(worldName).put(s, map);
273-
}
274-
}
275-
case ITEM -> {
276-
map = Arrays.stream(MAT).filter(Material::isItem).filter(m -> !m.isLegacy())
277-
.filter(m -> player.getStatistic(s, m) > 0)
278-
.collect(Collectors.toMap(k -> k, v -> player.getStatistic(s, v)));
279-
if (!map.isEmpty()) {
280-
store.getItemStats(worldName).put(s, map);
281-
}
264+
if (shutdown) {
265+
saveStatistics(result, store, player, worldName);
266+
} else {
267+
// Cannot schedule tasks on shutdown
268+
Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(),
269+
() -> saveStatistics(result, store, player, worldName));
270+
}
271+
return result;
272+
273+
}
274+
275+
private void saveStatistics(CompletableFuture<InventoryStorage> result, InventoryStorage store, Player player,
276+
String worldName) {
277+
Arrays.stream(Statistic.values()).forEach(s -> {
278+
Map<Material, Integer> map;
279+
Map<EntityType, Integer> entMap;
280+
switch (s.getType()) {
281+
case BLOCK -> {
282+
map = InvSwitcher.MAT.stream().filter(Material::isBlock).filter(m -> player.getStatistic(s, m) > 0)
283+
.collect(Collectors.toMap(k -> k, v -> player.getStatistic(s, v)));
284+
if (!map.isEmpty()) {
285+
store.getBlockStats(worldName).put(s, map);
282286
}
283-
case ENTITY -> {
284-
entMap = Arrays.stream(EntityType.values()).filter(EntityType::isAlive)
285-
.filter(m -> player.getStatistic(s, m) > 0)
286-
.collect(Collectors.toMap(k -> k, v -> player.getStatistic(s, v)));
287-
if (!entMap.isEmpty()) {
288-
store.getEntityStats(worldName).put(s, entMap);
289-
}
287+
}
288+
case ITEM -> {
289+
map = InvSwitcher.MAT.stream().filter(Material::isItem).filter(m -> player.getStatistic(s, m) > 0)
290+
.collect(Collectors.toMap(k -> k, v -> player.getStatistic(s, v)));
291+
if (!map.isEmpty()) {
292+
store.getItemStats(worldName).put(s, map);
290293
}
291-
case UNTYPED -> {
292-
int sc = player.getStatistic(s);
293-
if (sc > 0) {
294-
store.getUntypedStats(worldName).put(s, sc);
295-
}
294+
}
295+
case ENTITY -> {
296+
entMap = Arrays.stream(EntityType.values()).filter(EntityType::isAlive)
297+
.filter(m -> player.getStatistic(s, m) > 0)
298+
.collect(Collectors.toMap(k -> k, v -> player.getStatistic(s, v)));
299+
if (!entMap.isEmpty()) {
300+
store.getEntityStats(worldName).put(s, entMap);
296301
}
302+
}
303+
case UNTYPED -> {
304+
int sc = player.getStatistic(s);
305+
if (sc > 0) {
306+
store.getUntypedStats(worldName).put(s, sc);
297307
}
298-
});
299-
result.complete(store);
308+
}
309+
}
300310
});
301-
return result;
302-
311+
result.complete(store);
303312
}
304313

305314
/**
@@ -461,7 +470,7 @@ private static void setTotalExperience(final Player player, final int exp)
461470
/**
462471
* Save all online players
463472
*/
464-
public void saveOnlinePlayers() {
465-
Bukkit.getOnlinePlayers().forEach(p -> this.storeAndSave(p, p.getWorld()));
473+
public void saveOnShutdown() {
474+
Bukkit.getOnlinePlayers().forEach(p -> this.storeAndSave(p, p.getWorld(), true));
466475
}
467476
}

src/main/java/com/wasteofplastic/invswitcher/listeners/PlayerListener.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public void onPlayerJoin(final PlayerJoinEvent event) {
7171
@EventHandler(priority = EventPriority.LOW, ignoreCancelled=true)
7272
public void onPlayerQuit(final PlayerQuitEvent event) {
7373
if (addon.getWorlds().contains(event.getPlayer().getWorld())) {
74-
addon.getStore().storeAndSave(event.getPlayer(), event.getPlayer().getWorld());
74+
addon.getStore().storeAndSave(event.getPlayer(), event.getPlayer().getWorld(), false);
7575
}
7676
addon.getStore().removeFromCache(event.getPlayer());
7777
}

src/test/java/com/wasteofplastic/invswitcher/StoreTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,14 +262,14 @@ public void testStoreInventoryAll() {
262262
}
263263

264264
/**
265-
* Test method for {@link com.wasteofplastic.invswitcher.Store#saveOnlinePlayers()}.
265+
* Test method for {@link com.wasteofplastic.invswitcher.Store#saveOnShutdown()}.
266266
*/
267267
@Test
268268
public void testSaveOnlinePlayers() {
269269
// Mock the static method
270270
try (MockedStatic<Bukkit> mockedBukkit = mockStatic(Bukkit.class)) {
271271
// Run the code under test
272-
s.saveOnlinePlayers();
272+
s.saveOnShutdown();
273273

274274
// Verify that the static method was called
275275
mockedBukkit.verify(() -> Bukkit.getOnlinePlayers());

src/test/java/com/wasteofplastic/invswitcher/listeners/PlayerListenerTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public void testOnPlayerJoinWithStorage() {
141141
public void testOnPlayerQuit() {
142142
PlayerQuitEvent event = new PlayerQuitEvent(player, "");
143143
pl.onPlayerQuit(event);
144-
verify(store).storeAndSave(player, world);
144+
verify(store).storeAndSave(player, world, false);
145145
verify(store).removeFromCache(player);
146146
}
147147

@@ -153,7 +153,7 @@ public void testOnPlayerQuitNotCoveredWorld() {
153153
when(player.getWorld()).thenReturn(notWorld);
154154
PlayerQuitEvent event = new PlayerQuitEvent(player, "");
155155
pl.onPlayerQuit(event);
156-
verify(store, never()).storeAndSave(player, world);
156+
verify(store, never()).storeAndSave(player, world, false);
157157
verify(store).removeFromCache(player);
158158
}
159159

0 commit comments

Comments
 (0)