diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeWorldDataStorage.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeWorldDataStorage.java index 5685f3856..1f249dde5 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeWorldDataStorage.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeWorldDataStorage.java @@ -45,7 +45,7 @@ public class SlimeWorldDataStorage implements WorldDataStorage { @SuppressWarnings("unchecked") @Override - public void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk) { + public void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk, boolean immediately) { SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z); if (slimeChunk == null) return; CompoundTag nbt = DefaultChunkSerializer.serialize(chunk); diff --git a/bukkit/loader/src/main/resources/config.yml b/bukkit/loader/src/main/resources/config.yml index 8f7adaeef..eda56d932 100644 --- a/bukkit/loader/src/main/resources/config.yml +++ b/bukkit/loader/src/main/resources/config.yml @@ -344,7 +344,7 @@ light-system: chunk-system: # Unloaded chunks may be loaded soon. Delaying serialization can improve performance, especially for those double-dimension mob farms. - delay-serialization: 20 # seconds -1 = disable + delay-serialization: 20 # seconds -1 = disable # 1 = NONE | Compression Speed | Decompress Speed | Compression Ratio | Memory Usage | # 2 = DEFLATE | Medium-Slow Medium Moderate Low | # 3 = GZIP | Medium-Slow Medium Moderate Low | diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java index ca2f2b15c..c77d28879 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java @@ -200,7 +200,9 @@ public class BukkitWorldManager implements WorldManager, Listener { @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onWorldSave(WorldSaveEvent event) { - // TODO Timely saving? + for (CEWorld world : this.worldArray) { + world.save(); + } } @Override @@ -275,7 +277,7 @@ public class BukkitWorldManager implements WorldManager, Listener { CEChunk ceChunk = world.getChunkAtIfLoaded(chunk.getX(), chunk.getZ()); if (ceChunk != null) { try { - world.worldDataStorage().writeChunkAt(pos, ceChunk); + world.worldDataStorage().writeChunkAt(pos, ceChunk, false); } catch (IOException e) { this.plugin.logger().warn("Failed to write chunk tag at " + chunk.getX() + " " + chunk.getZ(), e); } finally { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java b/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java index 67c268b0c..6817fd512 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java @@ -2,11 +2,13 @@ package net.momirealms.craftengine.core.world; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.world.chunk.CEChunk; import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; import org.jetbrains.annotations.Nullable; +import java.io.IOException; import java.util.Collections; import java.util.HashSet; import java.util.Map; @@ -41,6 +43,19 @@ public abstract class CEWorld { this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS; } + public void save() { + this.loadedChunkMapLock.readLock().lock(); + try { + for (Map.Entry entry : this.loadedChunkMap.entrySet()) { + worldDataStorage.writeChunkAt(new ChunkPos(entry.getKey()), entry.getValue(), true); + } + } catch (IOException e) { + CraftEngine.instance().logger().warn("Failed to save world chunks", e); + } finally { + this.loadedChunkMapLock.readLock().unlock(); + } + } + public World world() { return world; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DefaultRegionFileStorage.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DefaultRegionFileStorage.java index bfeef7a58..132bc699f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DefaultRegionFileStorage.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DefaultRegionFileStorage.java @@ -147,7 +147,7 @@ public class DefaultRegionFileStorage implements WorldDataStorage { } @Override - public void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk) throws IOException { + public void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk, boolean immediately) throws IOException { CompoundTag nbt = DefaultChunkSerializer.serialize(chunk); writeChunkTagAt(pos, nbt); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DelayedDefaultRegionFileStorage.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DelayedDefaultRegionFileStorage.java index e6a8ea791..04dceb3a0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DelayedDefaultRegionFileStorage.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DelayedDefaultRegionFileStorage.java @@ -26,7 +26,7 @@ public class DelayedDefaultRegionFileStorage extends DefaultRegionFileStorage { } if (cause == RemovalCause.EXPIRED || cause == RemovalCause.SIZE) { try { - super.writeChunkAt(key, value); + super.writeChunkAt(key, value, true); } catch (IOException e) { CraftEngine.instance().logger().warn("Failed to write chunk at " + key, e); } @@ -45,7 +45,11 @@ public class DelayedDefaultRegionFileStorage extends DefaultRegionFileStorage { } @Override - public void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk) throws IOException { + public void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk, boolean immediately) throws IOException { + if (immediately) { + super.writeChunkAt(pos, chunk, true); + return; + } if (chunk.isEmpty()) { super.writeChunkTagAt(pos, null); return; @@ -62,7 +66,7 @@ public class DelayedDefaultRegionFileStorage extends DefaultRegionFileStorage { private void saveCache() { try { for (var chunk : this.chunkCache.asMap().entrySet()) { - super.writeChunkAt(chunk.getKey(), chunk.getValue()); + super.writeChunkAt(chunk.getKey(), chunk.getValue(), true); } } catch (IOException e) { CraftEngine.instance().logger().warn("Failed to save chunks", e); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/WorldDataStorage.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/WorldDataStorage.java index bd44492a9..b3b4dd47d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/WorldDataStorage.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/WorldDataStorage.java @@ -12,7 +12,7 @@ public interface WorldDataStorage { @NotNull CEChunk readChunkAt(@NotNull CEWorld world, @NotNull ChunkPos pos) throws IOException; - void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk) throws IOException; + void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk, boolean immediately) throws IOException; void flush() throws IOException;