From 5e52e83ac5661b36ce70b0fbae607a75dfabf38b Mon Sep 17 00:00:00 2001 From: Philip Urban Date: Tue, 22 Dec 2020 12:30:51 +0100 Subject: [PATCH] feat: Implement cyclic inventory saves. Closes #7 --- .../raidcraft/rcinventory/PluginConfig.java | 6 ++ .../de/raidcraft/rcinventory/RCInventory.java | 5 +- .../listener/PlayerJoinListener.java | 3 +- .../rcinventory/manager/InventoryManager.java | 63 +++++++++++++++++-- .../rcinventory/util/SchedulerUtil.java | 16 +++++ .../rcinventory/IntegrationTest.java | 11 +++- 6 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 src/main/java/de/raidcraft/rcinventory/util/SchedulerUtil.java diff --git a/src/main/java/de/raidcraft/rcinventory/PluginConfig.java b/src/main/java/de/raidcraft/rcinventory/PluginConfig.java index ab588c2..f9b75b9 100644 --- a/src/main/java/de/raidcraft/rcinventory/PluginConfig.java +++ b/src/main/java/de/raidcraft/rcinventory/PluginConfig.java @@ -19,6 +19,12 @@ public class PluginConfig extends BukkitYamlConfiguration { private int restoreDelayMs = 500; @Comment("Writes a chat message to player after inventory was restored") private boolean restoreMessage = true; + @Comment("Interval in minutes to save player inventories periodically. 0 = disabled") + private int saveIntervalMin = 5; + @Comment("Enable console messages about skipped inventory save events") + private boolean logSkippedSaves = true; + @Comment("Enable console messages about successfull inventory load events") + private boolean logSuccessfulLoads = true; public PluginConfig(Path path) { diff --git a/src/main/java/de/raidcraft/rcinventory/RCInventory.java b/src/main/java/de/raidcraft/rcinventory/RCInventory.java index f7100ec..d96f0a2 100644 --- a/src/main/java/de/raidcraft/rcinventory/RCInventory.java +++ b/src/main/java/de/raidcraft/rcinventory/RCInventory.java @@ -75,10 +75,7 @@ public void reload() { @Override public void onDisable() { - // Save all player inventories here - for(Player player : Bukkit.getOnlinePlayers()) { - inventoryManager.savePlayerInventory(player); - } + inventoryManager.saveAllPlayersInventories(); } private void loadConfig() { diff --git a/src/main/java/de/raidcraft/rcinventory/listener/PlayerJoinListener.java b/src/main/java/de/raidcraft/rcinventory/listener/PlayerJoinListener.java index f8a5a49..562b203 100644 --- a/src/main/java/de/raidcraft/rcinventory/listener/PlayerJoinListener.java +++ b/src/main/java/de/raidcraft/rcinventory/listener/PlayerJoinListener.java @@ -1,6 +1,7 @@ package de.raidcraft.rcinventory.listener; import de.raidcraft.rcinventory.RCInventory; +import de.raidcraft.rcinventory.util.SchedulerUtil; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -41,7 +42,7 @@ public void onPlayerJoin(PlayerJoinEvent event) { restoreCache.put(event.getPlayer().getUniqueId(), System.currentTimeMillis()); DelayedPlayerRestoreTask task = new DelayedPlayerRestoreTask(event.getPlayer()); Bukkit.getScheduler().runTaskLater(plugin, task, - plugin.getPluginConfig().getRestoreDelayMs() / 50 /* Ms per tick */); + SchedulerUtil.msInTicks(plugin.getPluginConfig().getRestoreDelayMs())); } @EventHandler(priority = EventPriority.HIGHEST) diff --git a/src/main/java/de/raidcraft/rcinventory/manager/InventoryManager.java b/src/main/java/de/raidcraft/rcinventory/manager/InventoryManager.java index 17f466f..6669baf 100644 --- a/src/main/java/de/raidcraft/rcinventory/manager/InventoryManager.java +++ b/src/main/java/de/raidcraft/rcinventory/manager/InventoryManager.java @@ -7,17 +7,45 @@ import de.raidcraft.rcinventory.inventory.Base64Inventory; import de.raidcraft.rcinventory.inventory.Inventory; import de.raidcraft.rcinventory.database.TDatabaseInventory; +import de.raidcraft.rcinventory.util.SchedulerUtil; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitTask; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; public class InventoryManager { - RCInventory plugin; + private final static int SAVE_TASK_PROCESS_INTERVAL_MS = 60*1000; + private RCInventory plugin; + private BukkitTask saveTask = null; public InventoryManager(RCInventory plugin) { + this.plugin = plugin; + setupSaveTask(); + } + + public void setupSaveTask() { + + if(saveTask != null) { + Bukkit.getScheduler().cancelTask(saveTask.getTaskId()); + saveTask = null; + } + + PeriodicSaveTask task = new PeriodicSaveTask(); + saveTask = Bukkit.getScheduler().runTaskTimer(plugin, task, + SchedulerUtil.msInTicks(SAVE_TASK_PROCESS_INTERVAL_MS), + SchedulerUtil.msInTicks(SAVE_TASK_PROCESS_INTERVAL_MS)); + } + + public void saveAllPlayersInventories() { + + Bukkit.getOnlinePlayers().forEach(player -> savePlayerInventory(player)); } public void savePlayerInventory(Player player) { @@ -39,11 +67,13 @@ public void savePlayerInventory(Player player) { // Save inventory to database TDatabaseInventory databaseInventory = new TDatabaseInventory(inventory); if(latestInventory != null && latestInventory.equals(databaseInventory)) { - plugin.getLogger().info("Inventory of '" + player.getDisplayName() + "' already synced"); + if(plugin.getPluginConfig().isLogSkippedSaves()) { + plugin.getLogger().info("Inventory of '" + player.getDisplayName() + "' already up-to-date"); + } return; } databaseInventory.save(); - plugin.getLogger().info("Synced inventory of '" + player.getDisplayName() + "' into database"); + plugin.getLogger().info("Saved inventory of '" + player.getDisplayName() + "' into database"); } public void restorePlayerInventory(Player player) { @@ -74,6 +104,31 @@ public void restorePlayerInventory(Player player) { if(plugin.getPluginConfig().isRestoreMessage()) { Messages.send(player, Messages.inventoryRestored(player, inventory)); } - plugin.getLogger().info("Restored inventory of '" + player.getDisplayName() + "' from database"); + if(plugin.getPluginConfig().isLogSuccessfulLoads()) { + plugin.getLogger().info("Restored inventory of '" + player.getDisplayName() + "' from database"); + } + } + + private class PeriodicSaveTask implements Runnable { + + private Map lastSaves = new HashMap<>(); + + @Override + public void run() { + + Bukkit.getOnlinePlayers().forEach(player -> { + + // Check if last save was recently + if(lastSaves.containsKey(player.getUniqueId())) { + long timeGone = System.currentTimeMillis() - lastSaves.get(player.getUniqueId()); + if(timeGone < SchedulerUtil.minInMs(plugin.getPluginConfig().getSaveIntervalMin())) { + return; + } + } + + savePlayerInventory(player); + lastSaves.put(player.getUniqueId(), System.currentTimeMillis()); + }); + } } } diff --git a/src/main/java/de/raidcraft/rcinventory/util/SchedulerUtil.java b/src/main/java/de/raidcraft/rcinventory/util/SchedulerUtil.java new file mode 100644 index 0000000..4a34512 --- /dev/null +++ b/src/main/java/de/raidcraft/rcinventory/util/SchedulerUtil.java @@ -0,0 +1,16 @@ +package de.raidcraft.rcinventory.util; + +public class SchedulerUtil { + + public final static int MS_PER_TICK = 50; + + public static int msInTicks(int ms) { + + return ms / MS_PER_TICK; + } + + public static int minInMs(int min) { + + return min * 60 * 1000; + } +} diff --git a/src/test/java/de/raidcraft/rcinventory/IntegrationTest.java b/src/test/java/de/raidcraft/rcinventory/IntegrationTest.java index 88fad9a..8676822 100644 --- a/src/test/java/de/raidcraft/rcinventory/IntegrationTest.java +++ b/src/test/java/de/raidcraft/rcinventory/IntegrationTest.java @@ -3,8 +3,12 @@ import be.seeseemelk.mockbukkit.MockBukkit; import be.seeseemelk.mockbukkit.ServerMock; import de.raidcraft.rcinventory.database.TDatabaseInventory; +import io.ebean.Model; import org.bukkit.entity.Player; import org.junit.jupiter.api.*; + +import java.util.List; + import static org.assertj.core.api.Assertions.assertThat; public class IntegrationTest { @@ -31,15 +35,20 @@ class InventoryManagerTests { @BeforeEach void setUp() { - TDatabaseInventory.find.all().forEach(entry -> entry.delete()); + } @Test @DisplayName("save-inventory") void storeInventory() { + + Player player = server.addPlayer(); + // Delete all entries + TDatabaseInventory.find.all().forEach(Model::delete); + // Database must be empty assertThat(TDatabaseInventory.find.query().findCount() == 0).isTrue();