diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/configuration/Configuration.java b/plugin/src/main/java/fr/utarwyn/endercontainers/configuration/Configuration.java index b935b31b..38c0a9bd 100644 --- a/plugin/src/main/java/fr/utarwyn/endercontainers/configuration/Configuration.java +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/configuration/Configuration.java @@ -1,5 +1,6 @@ package fr.utarwyn.endercontainers.configuration; +import fr.utarwyn.endercontainers.configuration.enderchests.SaveMode; import fr.utarwyn.endercontainers.configuration.ui.EnderChestItem; import fr.utarwyn.endercontainers.configuration.ui.EnderChestItemVariant; import org.bukkit.Material; @@ -26,6 +27,7 @@ public class Configuration { private final int maxEnderchests; private final int defaultEnderchests; private final boolean useVanillaEnderchest; + private final SaveMode saveMode; private final List forbiddenMaterials; private final EnderChestItem enderchestItem; @@ -49,7 +51,6 @@ public class Configuration { private final boolean blockNametag; private final boolean updateChecker; private final boolean globalSound; - private final boolean saveOnChestClose; /** * Create a configuration object from plugin configuration. @@ -59,6 +60,7 @@ public class Configuration { */ Configuration(FileConfiguration config) throws ConfigLoadingException { boolean legacyOnlyShowAccessible; + boolean legacySaveOnChestClose; this.locale = loadValue("locale", config::isString, config::getString); this.disabledWorlds = loadValue("disabledWorlds", config::isList, config::getStringList); @@ -109,7 +111,17 @@ public class Configuration { this.blockNametag = loadValue("others.blockNametag", config::isBoolean, config::getBoolean); this.updateChecker = loadValue("others.updateChecker", config::isBoolean, config::getBoolean); this.globalSound = loadValue("others.globalSound", config::isBoolean, config::getBoolean); - this.saveOnChestClose = loadValue("others.saveOnChestClose", config::isBoolean, config::getBoolean); + + legacySaveOnChestClose = loadValue("others.saveOnChestClose", v -> true, config::getBoolean); + if (legacySaveOnChestClose) { + this.saveMode = SaveMode.ON_CLOSE; + } else { + this.saveMode = loadValue( + "enderchests.saveMode", + key -> config.isString(key) && SaveMode.fromName(config.getString(key)) != null, + key -> SaveMode.fromName(config.getString(key)) + ); + } } public String getLocale() { @@ -140,6 +152,10 @@ public boolean isNumberingEnderchests() { return this.numberingEnderchests; } + public SaveMode getSaveMode() { + return this.saveMode; + } + public List getForbiddenMaterials() { return this.forbiddenMaterials; } @@ -212,10 +228,6 @@ public boolean isGlobalSound() { return this.globalSound; } - public boolean isSaveOnChestClose() { - return this.saveOnChestClose; - } - private T loadValue(String key, Predicate checker, Function getter) throws ConfigLoadingException { Throwable cause = null; if (checker.test(key)) { diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/configuration/enderchests/SaveMode.java b/plugin/src/main/java/fr/utarwyn/endercontainers/configuration/enderchests/SaveMode.java new file mode 100644 index 00000000..f118e614 --- /dev/null +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/configuration/enderchests/SaveMode.java @@ -0,0 +1,43 @@ +package fr.utarwyn.endercontainers.configuration.enderchests; + +/** + * The mode in which the plugin will save enderchests. + * + * @author Utarwyn + * @since 2.3.0 + */ +public enum SaveMode { + /** + * Save enderchests of a player when he logs out. + * It's the default mode. + */ + LOGOUT("logout"), + /** + * Save enderchests of a player when he closes the inventory. + */ + ON_CLOSE("on-close"), + /** + * Save all enderchests during a world save. + */ + WORLD_SAVE("world-save"), + ; + + private final String name; + + SaveMode(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static SaveMode fromName(String name) { + for (SaveMode mode : SaveMode.values()) { + if (mode.getName().equalsIgnoreCase(name)) { + return mode; + } + } + return null; + } +} diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/EnderChestManager.java b/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/EnderChestManager.java index cd684f8a..22b96d2f 100644 --- a/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/EnderChestManager.java +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/EnderChestManager.java @@ -64,6 +64,7 @@ protected synchronized void unload() { // Save and unload all data this.loadingContexts.clear(); + this.contextMap.values().forEach(PlayerContext::update); this.contextMap.values().forEach(PlayerContext::save); this.contextMap.clear(); } @@ -97,6 +98,15 @@ public Optional getVanillaEnderchestUsedBy(Player player) { .findFirst(); } + /** + * Get the all context of loaded players. + * + * @return context map of all players + */ + public Map getContextMap() { + return contextMap; + } + /** * Check if the context of a specific player is unused at a given time. * All chests of this context must not have viewer in their container. @@ -140,19 +150,27 @@ public void registerPlayerContext(PlayerContext context) { /** * Save all data of a player. - * Also purge its context from memory if needed. * - * @param owner owner of the player context to save - * @param delete should the context must be deleted from the memory + * @param owner owner of the player context to save */ - public void savePlayerContext(UUID owner, boolean delete) { + public void savePlayerContext(UUID owner) { if (this.contextMap.containsKey(owner)) { - SaveTask saveTask = new SaveTask(this.contextMap.get(owner)); + PlayerContext playerContext = this.contextMap.get(owner); + SaveTask saveTask = new SaveTask(playerContext); + + playerContext.update(); this.plugin.executeTaskOnOtherThread(saveTask); + } + } - if (delete) { - this.contextMap.remove(owner); - } + /** + * Delete a player context from memory if it is unused. + * + * @param owner owner of the player context + */ + public void deletePlayerContextIfUnused(UUID owner) { + if (this.isContextUnused(owner)) { + this.contextMap.remove(owner); } } diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/context/PlayerContext.java b/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/context/PlayerContext.java index c14570bb..75d5f7aa 100644 --- a/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/context/PlayerContext.java +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/context/PlayerContext.java @@ -188,11 +188,17 @@ public boolean openEnderchestInventory(Player viewer, int num) { return accessible; } + /** + * Update container of all chests. + */ + public void update() { + this.chests.forEach(EnderChest::updateContainer); + } + /** * Save all datas stored in the context. */ public void save() { - this.chests.forEach(EnderChest::updateContainer); this.data.saveContext(this.chests); } diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestInventoryListener.java b/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestInventoryListener.java index 1d2db09b..21563429 100644 --- a/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestInventoryListener.java +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestInventoryListener.java @@ -73,31 +73,31 @@ public void onInventoryDrag(InventoryDragEvent event) { */ @EventHandler public void onInventoryClose(InventoryCloseEvent event) { - if (!(event.getPlayer() instanceof Player)) return; - Player player = (Player) event.getPlayer(); + if (!(event.getPlayer() instanceof Player) || !this.isEnderChestInventory(event.getInventory())) { + return; + } - // Play the closing sound when we use the default enderchest! - if (this.isEnderChestInventory(event.getInventory())) { - Optional vanilla = this.manager.getVanillaEnderchestUsedBy(player); + Player player = (Player) event.getPlayer(); + Optional vanilla = this.manager.getVanillaEnderchestUsedBy(player); - // When closing the default enderchest ... - if (vanilla.isPresent()) { - Player ownerObj = vanilla.get().getOwnerAsPlayer(); + // When closing the default enderchest ... + if (vanilla.isPresent()) { + Player ownerObj = vanilla.get().getOwnerAsPlayer(); - // ... save and delete the context from memory if the player is offline. - if (!ownerObj.equals(player) && !ownerObj.isOnline()) { - this.manager.savePlayerContext(vanilla.get().getOwner(), true); - ownerObj.saveData(); - } + // ... save and delete the context from memory if the player is offline. + if (!ownerObj.equals(player) && !ownerObj.isOnline()) { + this.manager.savePlayerContext(vanilla.get().getOwner()); + this.manager.deletePlayerContextIfUnused(ownerObj.getUniqueId()); + ownerObj.saveData(); } + } - // Play the closing sound - Sound sound = CompatibilityHelper.searchSound("CHEST_CLOSE", "BLOCK_CHEST_CLOSE"); - if (Files.getConfiguration().isGlobalSound()) { - player.getWorld().playSound(player.getLocation(), sound, 1f, 1f); - } else { - player.playSound(player.getLocation(), sound, 1f, 1f); - } + // Play the closing sound + Sound sound = CompatibilityHelper.searchSound("CHEST_CLOSE", "BLOCK_CHEST_CLOSE"); + if (Files.getConfiguration().isGlobalSound()) { + player.getWorld().playSound(player.getLocation(), sound, 1f, 1f); + } else { + player.playSound(player.getLocation(), sound, 1f, 1f); } } diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestListener.java b/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestListener.java index 30bd5cae..2b4786a4 100644 --- a/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestListener.java +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestListener.java @@ -2,9 +2,11 @@ import fr.utarwyn.endercontainers.Managers; import fr.utarwyn.endercontainers.configuration.Files; +import fr.utarwyn.endercontainers.configuration.enderchests.SaveMode; import fr.utarwyn.endercontainers.dependency.DependenciesManager; import fr.utarwyn.endercontainers.dependency.exceptions.BlockChestOpeningException; import fr.utarwyn.endercontainers.enderchest.EnderChestManager; +import fr.utarwyn.endercontainers.enderchest.context.PlayerContext; import fr.utarwyn.endercontainers.util.PluginMsg; import org.bukkit.Material; import org.bukkit.block.Block; @@ -15,7 +17,10 @@ import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.world.WorldSaveEvent; +import java.util.Iterator; +import java.util.Map; import java.util.UUID; /** @@ -95,11 +100,40 @@ public void onPlayerInteract(PlayerInteractEvent event) { */ @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { - UUID owner = event.getPlayer().getUniqueId(); + if (Files.getConfiguration().getSaveMode() == SaveMode.LOGOUT) { + UUID owner = event.getPlayer().getUniqueId(); + this.manager.savePlayerContext(owner); + this.manager.deletePlayerContextIfUnused(owner); + } + } + + /** + * Method called when a world is saved + * + * @param event The save event + */ + @EventHandler + public void onWorldSave(WorldSaveEvent event) { + if (Files.getConfiguration().getSaveMode() != SaveMode.WORLD_SAVE) { + return; + } + + Map contextMap = this.manager.getContextMap(); + for (Iterator> it = contextMap.entrySet().iterator(); it.hasNext(); ) { + Map.Entry entry = it.next(); + + Player player = entry.getValue().getOwnerAsObject(); + // Avoids saving players data several times in a row if multiple worlds are on the server + if (player == null || player.getWorld().equals(event.getWorld())) { + // Clear all the player data from memory + boolean unused = this.manager.isContextUnused(entry.getKey()); - // Clear all the player data from memory - boolean unused = this.manager.isContextUnused(owner); - this.manager.savePlayerContext(owner, unused); + this.manager.savePlayerContext(entry.getKey()); + if (unused) { + it.remove(); + } + } + } } } diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/inventory/EnderChestInventory.java b/plugin/src/main/java/fr/utarwyn/endercontainers/inventory/EnderChestInventory.java index 710572a5..fe227e0f 100644 --- a/plugin/src/main/java/fr/utarwyn/endercontainers/inventory/EnderChestInventory.java +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/inventory/EnderChestInventory.java @@ -5,6 +5,7 @@ import fr.utarwyn.endercontainers.compatibility.CompatibilityHelper; import fr.utarwyn.endercontainers.configuration.Files; import fr.utarwyn.endercontainers.configuration.LocaleKey; +import fr.utarwyn.endercontainers.configuration.enderchests.SaveMode; import fr.utarwyn.endercontainers.enderchest.EnderChest; import fr.utarwyn.endercontainers.enderchest.EnderChestManager; import fr.utarwyn.endercontainers.util.uuid.UUIDFetcher; @@ -121,9 +122,13 @@ public void onClose(Player player) { Player owner = Bukkit.getPlayer(this.chest.getOwner()); boolean offlineOwner = owner == null || !owner.isOnline(); - // Save chest inventory if owner is offline or forced by the configuration (experimental) - if (offlineOwner || Files.getConfiguration().isSaveOnChestClose()) { - Managers.get(EnderChestManager.class).savePlayerContext(this.chest.getOwner(), offlineOwner); + // Save chest inventory if owner is offline or forced by the configuration + if (offlineOwner || Files.getConfiguration().getSaveMode() == SaveMode.ON_CLOSE) { + EnderChestManager enderChestManager = Managers.get(EnderChestManager.class); + enderChestManager.savePlayerContext(this.chest.getOwner()); + if (offlineOwner) { + enderChestManager.deletePlayerContextIfUnused(this.chest.getOwner()); + } } // Play the closing sound diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 2bbc1738..0ad6b456 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -25,6 +25,10 @@ enderchests: # Use the vanilla enderchest as the first one. Otherwise, it will be managed by EnderContainers. useVanillaEnderchest: true + # The mode in which the plugin will save enderchests + # Can be: logout, on-close or world-save + saveMode: 'logout' + # A list of item materials which are forbidden to store in chests # Use Minecraft item IDs displayed ingame (shortcut: F3+H) forbiddenMaterials: [ ] @@ -102,7 +106,3 @@ others: # Use a global sound when a player opens/closes an enderchest globalSound: true - - # Save enderchests on close. Beware! This could be very resource intensive. - # Enable this ONLY if you have issues with the normal saving system. - saveOnChestClose: false diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/configuration/ConfigurationTest.java b/plugin/src/test/java/fr/utarwyn/endercontainers/configuration/ConfigurationTest.java index 84cb8428..195c2d92 100644 --- a/plugin/src/test/java/fr/utarwyn/endercontainers/configuration/ConfigurationTest.java +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/configuration/ConfigurationTest.java @@ -2,6 +2,7 @@ import fr.utarwyn.endercontainers.TestHelper; import fr.utarwyn.endercontainers.TestInitializationException; +import fr.utarwyn.endercontainers.configuration.enderchests.SaveMode; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.junit.jupiter.api.BeforeAll; @@ -30,6 +31,7 @@ public void loadFromTestFile() throws ConfigLoadingException, TestInitialization assertThat(config.getDisabledWorlds()).containsExactly("disabled"); assertThat(config.isOnlyShowAccessibleEnderchests()).isFalse(); assertThat(config.isUseVanillaEnderchest()).isTrue(); + assertThat(config.getSaveMode()).isEqualTo(SaveMode.LOGOUT); assertThat(config.getEnderchestItem()).isNotNull(); assertThat(config.getEnderchestItemVariants()).hasSize(5); assertThat(config.isNumberingEnderchests()).isTrue(); @@ -37,7 +39,6 @@ public void loadFromTestFile() throws ConfigLoadingException, TestInitialization assertThat(config.getMysqlSslKeystorePassword()).isNull(); assertThat(config.getMysqlSslTrustKeystoreFile()).isNull(); assertThat(config.getMysqlSslTrustKeystorePassword()).isNull(); - assertThat(config.isSaveOnChestClose()).isFalse(); } @Test diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/EnderChestManagerTest.java b/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/EnderChestManagerTest.java index d228e991..e0483428 100644 --- a/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/EnderChestManagerTest.java +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/EnderChestManagerTest.java @@ -70,6 +70,7 @@ public void unload() throws TestInitializationException { this.manager.unload(); + verify(context).update(); verify(context).save(); verify(inventoryManager).closeAll(); assertThat(this.manager.contextMap).isEmpty(); @@ -167,16 +168,32 @@ public void savePlayerContext() throws TestInitializationException { TestHelper.setupManager(this.manager); // Unregistered context? - this.manager.savePlayerContext(uuid, false); - this.registerPlayerContext(uuid); + this.manager.savePlayerContext(uuid); + verify(TestHelper.getPlugin(), never()).executeTaskOnOtherThread(any(SaveTask.class)); - // Check saving without deletion - this.manager.savePlayerContext(uuid, false); + // Check saving a registered context + PlayerContext context = this.registerPlayerContext(uuid); + this.manager.savePlayerContext(uuid); assertThat(this.manager.contextMap).containsKey(uuid); + verify(context).update(); verify(TestHelper.getPlugin()).executeTaskOnOtherThread(any(SaveTask.class)); + } - // Check deletion of a context - this.manager.savePlayerContext(uuid, true); + @Test + public void deletePlayerContextIfUnused() throws TestInitializationException { + UUID uuid = UUID.randomUUID(); + + // Setup the manager correctly + TestHelper.setupManager(this.manager); + PlayerContext context = this.registerPlayerContext(uuid); + + // Delete nothing because the context is used + this.manager.deletePlayerContextIfUnused(uuid); + assertThat(this.manager.contextMap).containsKey(uuid); + + // Delete the context + when(context.isChestsUnused()).thenReturn(true); + this.manager.deletePlayerContextIfUnused(uuid); assertThat(this.manager.contextMap).isEmpty(); } diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/context/PlayerContextTest.java b/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/context/PlayerContextTest.java index 99235772..72583df6 100644 --- a/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/context/PlayerContextTest.java +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/context/PlayerContextTest.java @@ -3,6 +3,7 @@ import com.google.common.collect.Maps; import fr.utarwyn.endercontainers.TestHelper; import fr.utarwyn.endercontainers.TestInitializationException; +import fr.utarwyn.endercontainers.enderchest.EnderChest; import fr.utarwyn.endercontainers.enderchest.VanillaEnderChest; import fr.utarwyn.endercontainers.inventory.menu.EnderChestListMenu; import fr.utarwyn.endercontainers.storage.StorageManager; @@ -22,6 +23,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import java.util.Collections; +import java.util.Optional; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; @@ -161,6 +163,14 @@ public void openEnderchestInventory() { assertThat(this.context.openEnderchestInventory(this.player, ENDERCHEST_AMOUNT)).isFalse(); } + @Test + public void update() { + this.context.update(); + Optional chest = this.context.getChest(1); + assertThat(chest).isPresent(); + assertThat(chest.get().getFillPercentage()).isZero(); + } + @Test public void save() { this.context.save(); diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestInventoryListenerTest.java b/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestInventoryListenerTest.java index 54d3364b..4cd49af2 100644 --- a/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestInventoryListenerTest.java +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestInventoryListenerTest.java @@ -190,19 +190,22 @@ public void inventoryCloseSaveOfflineVanillaChest() { // do not save if the viewer is the owner of the chest when(chest.getOwnerAsPlayer()).thenReturn(this.player); this.listener.onInventoryClose(event); - verify(this.manager, never()).savePlayerContext(player2.getUniqueId(), true); + verify(this.manager, never()).savePlayerContext(player2.getUniqueId()); + verify(this.manager, never()).deletePlayerContextIfUnused(player2.getUniqueId()); // do not save if the player is online when(chest.getOwnerAsPlayer()).thenReturn(player2); when(chest.getOwner()).thenReturn(player2Identifier); when(player2.isOnline()).thenReturn(true); this.listener.onInventoryClose(event); - verify(this.manager, never()).savePlayerContext(player2.getUniqueId(), true); + verify(this.manager, never()).savePlayerContext(player2.getUniqueId()); + verify(this.manager, never()).deletePlayerContextIfUnused(player2.getUniqueId()); // save the chest (and the player data) if the player is not the viewer and its offline when(player2.isOnline()).thenReturn(false); this.listener.onInventoryClose(event); - verify(this.manager).savePlayerContext(player2.getUniqueId(), true); + verify(this.manager).savePlayerContext(player2.getUniqueId()); + verify(this.manager).deletePlayerContextIfUnused(player2.getUniqueId()); verify(player2).saveData(); } @@ -240,7 +243,6 @@ public void inventoryCloseUnsupportedActions() { // try with an enderchest managed by the plugin -> no sound (integrated in the inventory system) when(event.getInventory().getType()).thenReturn(InventoryType.ENDER_CHEST); - when(this.manager.getVanillaEnderchestUsedBy(this.player)).thenReturn(Optional.empty()); this.listener.onInventoryClose(event); verify(this.player, never()).playSound(any(Location.class), any(Sound.class), anyFloat(), anyFloat()); } diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestListenerTest.java b/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestListenerTest.java index 24b85a17..f78f986e 100644 --- a/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestListenerTest.java +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/enderchest/listener/EnderChestListenerTest.java @@ -3,6 +3,7 @@ import fr.utarwyn.endercontainers.TestHelper; import fr.utarwyn.endercontainers.TestInitializationException; import fr.utarwyn.endercontainers.configuration.LocaleKey; +import fr.utarwyn.endercontainers.configuration.enderchests.SaveMode; import fr.utarwyn.endercontainers.dependency.DependenciesManager; import fr.utarwyn.endercontainers.dependency.exceptions.BlockChestOpeningException; import fr.utarwyn.endercontainers.enderchest.EnderChestManager; @@ -16,6 +17,7 @@ import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.world.WorldSaveEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.junit.jupiter.api.BeforeEach; @@ -25,7 +27,9 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.util.Collections; import java.util.HashMap; +import java.util.Map; import java.util.UUID; import java.util.function.Consumer; @@ -152,14 +156,35 @@ public void playerInteractDisabledWorld() { public void playerLeaveSaveContext() { PlayerQuitEvent event = new PlayerQuitEvent(this.player, ""); - // By default, we have to save the context but not delete it this.listener.onPlayerQuit(event); - verify(this.manager).savePlayerContext(this.player.getUniqueId(), false); + verify(this.manager).savePlayerContext(this.player.getUniqueId()); + verify(this.manager).deletePlayerContextIfUnused(this.player.getUniqueId()); + } + + @Test + public void worldSaveSaveContext() throws TestInitializationException { + WorldSaveEvent event = new WorldSaveEvent(this.player.getWorld()); + + // Do nothing by default + this.listener.onWorldSave(event); + verify(this.manager, never()).getContextMap(); - // With an unused context, we also have to delete the context + // Save player context if enabled in configuration + TestHelper.overrideConfigurationValue("saveMode", SaveMode.WORLD_SAVE); + + PlayerContext context = mock(PlayerContext.class); + Map contextMap = new HashMap<>(Collections.singletonMap( + this.player.getUniqueId(), context + )); + when(context.getOwnerAsObject()).thenReturn(this.player); + when(this.manager.getContextMap()).thenReturn(contextMap); when(this.manager.isContextUnused(this.player.getUniqueId())).thenReturn(true); - this.listener.onPlayerQuit(event); - verify(this.manager).savePlayerContext(this.player.getUniqueId(), true); + + this.listener.onWorldSave(event); + verify(this.manager).getContextMap(); + verify(this.manager).isContextUnused(this.player.getUniqueId()); + verify(this.manager).savePlayerContext(this.player.getUniqueId()); + assertThat(contextMap).isEmpty(); } private PlayerInteractEvent createInteractEvent(Action action) { diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/inventory/EnderChestInventoryTest.java b/plugin/src/test/java/fr/utarwyn/endercontainers/inventory/EnderChestInventoryTest.java index 50c3e8a8..2da7a9fa 100644 --- a/plugin/src/test/java/fr/utarwyn/endercontainers/inventory/EnderChestInventoryTest.java +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/inventory/EnderChestInventoryTest.java @@ -2,6 +2,7 @@ import fr.utarwyn.endercontainers.TestHelper; import fr.utarwyn.endercontainers.TestInitializationException; +import fr.utarwyn.endercontainers.configuration.enderchests.SaveMode; import fr.utarwyn.endercontainers.enderchest.EnderChest; import fr.utarwyn.endercontainers.enderchest.EnderChestManager; import org.assertj.core.api.Condition; @@ -128,19 +129,22 @@ public void saveOnClose() throws TestInitializationException { // do not save if the owner is connected this.inventory.onClose(viewer); - verify(manager, never()).savePlayerContext(any(), eq(true)); + verify(manager, never()).savePlayerContext(any()); + verify(manager, never()).deletePlayerContextIfUnused(this.chest.getOwner()); // save if forced by the configuration - TestHelper.overrideConfigurationValue("saveOnChestClose", true); + TestHelper.overrideConfigurationValue("saveMode", SaveMode.ON_CLOSE); this.inventory.onClose(viewer); - verify(manager).savePlayerContext(this.chest.getOwner(), false); - TestHelper.overrideConfigurationValue("saveOnChestClose", false); + verify(manager).savePlayerContext(this.chest.getOwner()); + verify(manager, never()).deletePlayerContextIfUnused(this.chest.getOwner()); + TestHelper.overrideConfigurationValue("saveMode", SaveMode.LOGOUT); // save if owner not connected UUID offline = UUID.fromString("62dcb385-f2ac-472f-9d88-a0cc0d957082"); when(this.chest.getOwner()).thenReturn(offline); this.inventory.onClose(viewer); - verify(manager).savePlayerContext(this.chest.getOwner(), true); + verify(manager).savePlayerContext(this.chest.getOwner()); + verify(manager).deletePlayerContextIfUnused(this.chest.getOwner()); } @Test diff --git a/plugin/src/test/resources/config.test.yml b/plugin/src/test/resources/config.test.yml index 3c79c14d..3576a2b2 100644 --- a/plugin/src/test/resources/config.test.yml +++ b/plugin/src/test/resources/config.test.yml @@ -8,6 +8,7 @@ enderchests: max: 27 default: 1 useVanillaEnderchest: true + saveMode: logout forbiddenMaterials: - andesite - bedrock