diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ADataController.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ADataController.java index a18b5e55e2..ca9b67a09d 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ADataController.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ADataController.java @@ -164,6 +164,11 @@ protected void scheduleReadTask(Runnable run) { readExecutor.submit(run); } + protected void scheduleWriteTask(Runnable run) { + checkDestroy(); + writeExecutor.submit(run); + } + protected List getData(RecordKey key) { return getData(key, false); } @@ -180,6 +185,13 @@ protected void deleteData(RecordKey key) { dataAdapter.deleteData(key); } + protected void abortScopeTask(ScopeKey key) { + var task = scheduledWriteTasks.remove(key); + if (task != null) { + task.abort(); + } + } + public final DataType getDataType() { return dataType; } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/BlockDataController.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/BlockDataController.java index 55b09dcf09..c71b6a4103 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/BlockDataController.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/BlockDataController.java @@ -178,13 +178,16 @@ void saveNewBlock(Location l, String sfId) { public void removeBlock(Location l) { checkDestroy(); - Slimefun.getNetworkManager().updateAllNetworks(l); - var removed = getChunkDataCache(l.getChunk(), true).removeBlockData(l); + Slimefun.getNetworkManager().updateAllNetworks(l); if (removed == null) { return; } + if (!removed.isDataLoaded()) { + return; + } + var menu = removed.getBlockMenu(); if (menu != null) { InventoryUtil.closeInventory(menu.toInventory()); @@ -278,7 +281,7 @@ public void setBlockDataLocation(SlimefunBlockData blockData, Location target) { var hasTicker = false; - if (Slimefun.getRegistry().getTickerBlocks().contains(blockData.getSfId())) { + if (blockData.isDataLoaded() && Slimefun.getRegistry().getTickerBlocks().contains(blockData.getSfId())) { Slimefun.getTickerTask().disableTicker(blockData.getLocation()); hasTicker = true; } @@ -373,12 +376,7 @@ public void loadChunk(Chunk chunk, boolean isNewChunk) { chunkData.addBlockCacheInternal(blockData, false); if (sfItem.loadDataByDefault()) { - scheduleReadTask(() -> { - loadBlockData(blockData); - if (sfItem.isTicking()) { - Slimefun.getTickerTask().enableTicker(blockData.getLocation()); - } - }); + scheduleReadTask(() -> loadBlockData(blockData)); } }); Bukkit.getPluginManager().callEvent(new SlimefunChunkDataLoadEvent(chunkData)); @@ -470,6 +468,11 @@ public void loadBlockData(SlimefunBlockData blockData) { invSnapshots.put(blockData.getKey(), InvStorageUtils.getInvSnapshot(content)); } } + + var sfItem = SlimefunItem.getById(blockData.getSfId()); + if (sfItem != null && sfItem.isTicking()) { + Slimefun.getTickerTask().enableTicker(blockData.getLocation()); + } } finally { lock.unlock(key); } @@ -537,6 +540,60 @@ public Set getAllLoadedChunkData() { return new HashSet<>(loadedChunk.values()); } + public void removeAllDataInChunk(Chunk chunk) { + var cKey = LocationUtils.getChunkKey(chunk); + var cache = loadedChunk.remove(cKey); + + if (cache != null && cache.isDataLoaded()) { + cache.getAllBlockData().forEach(this::clearBlockCacheAndTasks); + } + deleteChunkAndBlockDataDirectly(cKey); + } + + public void removeAllDataInChunkAsync(Chunk chunk, Runnable onFinishedCallback) { + scheduleWriteTask(() -> { + removeAllDataInChunk(chunk); + onFinishedCallback.run(); + }); + } + + public void removeAllDataInWorld(World world) { + // 1. remove block cache + var loadedBlockData = new HashSet(); + for (var chunkData : getAllLoadedChunkData(world)) { + loadedBlockData.addAll(chunkData.getAllBlockData()); + chunkData.removeAllCacheInternal(); + } + + // 2. remove ticker and delayed tasks + loadedBlockData.forEach(this::clearBlockCacheAndTasks); + + // 3. remove from database + var prefix = world.getName() + ";"; + deleteChunkAndBlockDataDirectly(prefix + "%"); + + // 4. remove chunk cache + loadedChunk.entrySet().removeIf(entry -> entry.getKey().startsWith(prefix)); + } + + public void removeAllDataInWorldAsync(World world, Runnable onFinishedCallback) { + scheduleWriteTask(() -> { + removeAllDataInWorld(world); + onFinishedCallback.run(); + }); + } + + public Set getAllLoadedChunkData(World world) { + var prefix = world.getName() + ";"; + var re = new HashSet(); + loadedChunk.forEach((k, v) -> { + if (k.startsWith(prefix)) { + re.add(v); + } + }); + return re; + } + private void scheduleDelayedBlockInvUpdate(SlimefunBlockData blockData, int slot) { var scopeKey = new LocationKey(DataScope.NONE, blockData.getLocation()); var reqKey = new RecordKey(DataScope.BLOCK_INVENTORY); @@ -671,4 +728,26 @@ private SlimefunChunkData getChunkDataCache(Chunk chunk, boolean createOnNotExis }) : loadedChunk.get(LocationUtils.getChunkKey(chunk)); } + + private void deleteChunkAndBlockDataDirectly(String cKey) { + var req = new RecordKey(DataScope.BLOCK_DATA); + req.addCondition(FieldKey.CHUNK, cKey); + deleteData(req); + + req = new RecordKey(DataScope.CHUNK_DATA); + req.addCondition(FieldKey.CHUNK, cKey); + deleteData(req); + } + + private void clearBlockCacheAndTasks(SlimefunBlockData blockData) { + var l = blockData.getLocation(); + if (blockData.isDataLoaded() && Slimefun.getRegistry().getTickerBlocks().contains(blockData.getSfId())) { + Slimefun.getTickerTask().disableTicker(l); + } + Slimefun.getNetworkManager().updateAllNetworks(l); + + var scopeKey = new LocationKey(DataScope.NONE, l); + removeDelayedBlockDataUpdates(scopeKey); + abortScopeTask(scopeKey); + } } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunChunkData.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunChunkData.java index c16b23aa11..9dadc5afd0 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunChunkData.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunChunkData.java @@ -94,12 +94,16 @@ Set getAllCacheInternal() { return re; } + void removeAllCacheInternal() { + sfBlocks.clear(); + } + boolean hasBlockCache(String lKey) { return sfBlocks.containsKey(lKey); } SlimefunBlockData removeBlockDataCacheInternal(String lKey) { - var re = sfBlocks.put(lKey, INVALID_BLOCK_DATA); + var re = isDataLoaded() ? sfBlocks.remove(lKey) : sfBlocks.put(lKey, INVALID_BLOCK_DATA); return re == INVALID_BLOCK_DATA ? null : re; } @@ -120,4 +124,12 @@ public void removeData(String key) { public Set getAllBlockData() { return getAllCacheInternal(); } + + @Override + protected void setIsDataLoaded(boolean isDataLoaded) { + super.setIsDataLoaded(isDataLoaded); + if (isDataLoaded) { + sfBlocks.entrySet().removeIf(entry -> entry.getValue() == INVALID_BLOCK_DATA); + } + } } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/task/QueuedWriteTask.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/task/QueuedWriteTask.java index bd87ff9ca7..6074b36877 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/task/QueuedWriteTask.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/task/QueuedWriteTask.java @@ -10,11 +10,16 @@ public class QueuedWriteTask implements Runnable { private final Queue queue = new LinkedList<>(); private final Map tasks = new HashMap<>(); private volatile boolean done = false; + private volatile boolean aborted = false; @Override public final void run() { + if (aborted) { + return; + } + var task = next(); - while (task != null) { + while (!aborted && task != null) { try { task.run(); } catch (Throwable e) { @@ -35,7 +40,7 @@ protected void onSuccess() {} protected void onError(Throwable e) {} public synchronized boolean queue(RecordKey key, Runnable next) { - if (done) { + if (done || aborted) { return false; } @@ -45,6 +50,10 @@ public synchronized boolean queue(RecordKey key, Runnable next) { return true; } + public void abort() { + aborted = true; + } + private synchronized Runnable next() { var key = queue.poll(); if (key == null) {