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 8e0a55696..5685f3856 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 @@ -2,11 +2,14 @@ package net.momirealms.craftengine.bukkit.compatibility.slimeworld; import com.infernalsuite.asp.api.world.SlimeChunk; import com.infernalsuite.asp.api.world.SlimeWorld; +import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.ChunkPos; +import net.momirealms.craftengine.core.world.chunk.CEChunk; +import net.momirealms.craftengine.core.world.chunk.serialization.DefaultChunkSerializer; import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.NBT; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.lang.ref.WeakReference; @@ -25,15 +28,16 @@ public class SlimeWorldDataStorage implements WorldDataStorage { return slimeWorld.get(); } - @Nullable @Override - public CompoundTag readChunkTagAt(ChunkPos pos) { + public @NotNull CEChunk readChunkAt(@NotNull CEWorld world, @NotNull ChunkPos pos) { SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z); - if (slimeChunk == null) return null; + if (slimeChunk == null) return new CEChunk(world, pos); Object tag = slimeChunk.getExtraData().get("craftengine"); - if (tag == null) return null; + if (tag == null) return new CEChunk(world, pos); try { - return NBT.fromBytes(adaptor.byteArrayTagToBytes(tag)); + CompoundTag compoundTag = NBT.fromBytes(adaptor.byteArrayTagToBytes(tag)); + if (compoundTag == null) return new CEChunk(world, pos); + return DefaultChunkSerializer.deserialize(world, pos, compoundTag); } catch (Exception e) { throw new RuntimeException("Failed to read chunk tag from slime world. " + pos, e); } @@ -41,9 +45,10 @@ public class SlimeWorldDataStorage implements WorldDataStorage { @SuppressWarnings("unchecked") @Override - public void writeChunkTagAt(ChunkPos pos, @Nullable CompoundTag nbt) { + public void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk) { SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z); if (slimeChunk == null) return; + CompoundTag nbt = DefaultChunkSerializer.serialize(chunk); if (nbt == null) { slimeChunk.getExtraData().remove("craftengine"); } else { diff --git a/bukkit/loader/src/main/resources/config.yml b/bukkit/loader/src/main/resources/config.yml index 63d41c3b3..8f7adaeef 100644 --- a/bukkit/loader/src/main/resources/config.yml +++ b/bukkit/loader/src/main/resources/config.yml @@ -343,6 +343,8 @@ light-system: force-update-light: false 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 # 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/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index 510b8346e..0c7a0c28c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -453,7 +453,11 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } targetLocation.setYaw(player.getLocation().getYaw()); targetLocation.setPitch(player.getLocation().getPitch()); - player.teleport(targetLocation); + if (VersionHelper.isFolia()) { + player.teleportAsync(targetLocation); + } else { + player.teleport(targetLocation); + } } protected boolean isSeatCarrierType(Entity entity) { 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 b402a8fad..ca2f2b15c 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 @@ -17,11 +17,9 @@ import net.momirealms.craftengine.core.world.SectionPos; import net.momirealms.craftengine.core.world.WorldManager; import net.momirealms.craftengine.core.world.chunk.CEChunk; import net.momirealms.craftengine.core.world.chunk.CESection; -import net.momirealms.craftengine.core.world.chunk.serialization.ChunkSerializer; import net.momirealms.craftengine.core.world.chunk.storage.DefaultStorageAdaptor; import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; -import net.momirealms.sparrow.nbt.CompoundTag; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.World; @@ -29,10 +27,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; -import org.bukkit.event.world.ChunkLoadEvent; -import org.bukkit.event.world.ChunkUnloadEvent; -import org.bukkit.event.world.WorldLoadEvent; -import org.bukkit.event.world.WorldUnloadEvent; +import org.bukkit.event.world.*; import org.jetbrains.annotations.NotNull; import java.io.IOException; @@ -148,6 +143,11 @@ public class BukkitWorldManager implements WorldManager, Listener { for (Chunk chunk : world.getLoadedChunks()) { handleChunkUnload(ceWorld, chunk); } + try { + ceWorld.worldDataStorage().close(); + } catch (IOException e) { + this.plugin.logger().warn("Error unloading world: " + world.getName(), e); + } } this.worlds.clear(); } @@ -198,6 +198,11 @@ public class BukkitWorldManager implements WorldManager, Listener { unloadWorld(new BukkitWorld(event.getWorld())); } + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onWorldSave(WorldSaveEvent event) { + // TODO Timely saving? + } + @Override public void unloadWorld(net.momirealms.craftengine.core.world.World world) { CEWorld ceWorld; @@ -270,10 +275,9 @@ public class BukkitWorldManager implements WorldManager, Listener { CEChunk ceChunk = world.getChunkAtIfLoaded(chunk.getX(), chunk.getZ()); if (ceChunk != null) { try { - world.worldDataStorage().writeChunkTagAt(pos, ChunkSerializer.serialize(ceChunk)); + world.worldDataStorage().writeChunkAt(pos, ceChunk); } catch (IOException e) { - plugin.logger().warn("Failed to write chunk tag at " + chunk.getX() + " " + chunk.getZ(), e); - return; + this.plugin.logger().warn("Failed to write chunk tag at " + chunk.getX() + " " + chunk.getZ(), e); } finally { if (Config.restoreVanillaBlocks()) { CESection[] ceSections = ceChunk.sections(); @@ -308,12 +312,7 @@ public class BukkitWorldManager implements WorldManager, Listener { if (ceWorld.isChunkLoaded(pos.longKey)) return; CEChunk ceChunk; try { - CompoundTag chunkNbt = ceWorld.worldDataStorage().readChunkTagAt(pos); - if (chunkNbt != null) { - ceChunk = ChunkSerializer.deserialize(ceWorld, pos, chunkNbt); - } else { - ceChunk = new CEChunk(ceWorld, pos); - } + ceChunk = ceWorld.worldDataStorage().readChunkAt(ceWorld, pos); try { CESection[] ceSections = ceChunk.sections(); Object worldServer = FastNMS.INSTANCE.field$CraftChunk$worldServer(chunk); diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/LootPool.java b/core/src/main/java/net/momirealms/craftengine/core/loot/LootPool.java index 04a196a1a..981093c85 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/LootPool.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/LootPool.java @@ -10,7 +10,7 @@ import net.momirealms.craftengine.core.loot.function.LootFunction; import net.momirealms.craftengine.core.loot.function.LootFunctions; import net.momirealms.craftengine.core.loot.number.NumberProvider; import net.momirealms.craftengine.core.util.MCUtils; -import org.apache.commons.lang3.mutable.MutableInt; +import net.momirealms.craftengine.core.util.MutableInt; import java.util.List; import java.util.Random; @@ -64,7 +64,7 @@ public class LootPool { private void addRandomItem(Consumer> lootConsumer, LootContext context) { Random randomSource = context.randomSource(); List> list = Lists.newArrayList(); - MutableInt mutableInt = new MutableInt(); + MutableInt mutableInt = new MutableInt(0); for (LootEntryContainer lootPoolEntryContainer : this.entryContainers) { lootPoolEntryContainer.expand(context, (choice) -> { int i = choice.getWeight(context.luck()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index fffc622f2..a1020c555 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java @@ -98,6 +98,7 @@ public class Config { protected boolean chunk_system$restore_vanilla_blocks_on_chunk_unload; protected boolean chunk_system$restore_custom_blocks_on_chunk_load; protected boolean chunk_system$sync_custom_blocks_on_chunk_load; + protected int chunk_system$delay_serialization; protected boolean furniture$handle_invalid_furniture_on_chunk_load$enable; protected Map furniture$handle_invalid_furniture_on_chunk_load$mapping; @@ -265,6 +266,7 @@ public class Config { chunk_system$restore_vanilla_blocks_on_chunk_unload = config.getBoolean("chunk-system.restore-vanilla-blocks-on-chunk-unload", true); chunk_system$restore_custom_blocks_on_chunk_load = config.getBoolean("chunk-system.restore-custom-blocks-on-chunk-load", true); chunk_system$sync_custom_blocks_on_chunk_load = config.getBoolean("chunk-system.sync-custom-blocks-on-chunk-load", false); + chunk_system$delay_serialization = config.getInt("chunk-system.delay-serialization", 20); // furniture furniture$handle_invalid_furniture_on_chunk_load$enable = config.getBoolean("furniture.handle-invalid-furniture-on-chunk-load.enable", false); @@ -683,6 +685,10 @@ public class Config { return instance.furniture$collision_entity_type; } + public static int delaySerialization() { + return instance.chunk_system$delay_serialization; + } + public YamlDocument loadOrCreateYamlData(String fileName) { File file = new File(this.plugin.dataFolderFile(), fileName); if (!file.exists()) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/Action.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/script/Action.java deleted file mode 100644 index f17aa9f7a..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/Action.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.momirealms.craftengine.core.plugin.script; - -import java.util.concurrent.CompletableFuture; - -public interface Action { - - CompletableFuture execute(T t); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/DefaultTokenStringReader.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/script/DefaultTokenStringReader.java deleted file mode 100644 index 629f19b97..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/DefaultTokenStringReader.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.momirealms.craftengine.core.plugin.script; - -import net.momirealms.craftengine.core.util.DefaultStringReader; - -public class DefaultTokenStringReader extends DefaultStringReader implements TokenStringReader { - - public DefaultTokenStringReader(String string) { - super(string); - } - - @Override - public String nextToken() { - skipWhitespaceAndComment(); - return readString(); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/Task.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/script/Task.java deleted file mode 100644 index 213c115a7..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/Task.java +++ /dev/null @@ -1,61 +0,0 @@ -package net.momirealms.craftengine.core.plugin.script; - -import net.momirealms.craftengine.core.util.Key; -import org.jetbrains.annotations.Nullable; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -public interface Task { - - Map blocks(); - - Key id(); - - @Nullable - TaskBlock byId(Key id); - - @Nullable - TaskBlock byAction(Action action); - - static Task create(Key key, Map blocks) { - return new TaskImpl(key, blocks); - } - - class TaskImpl implements Task { - private final Key id; - private final Map blocks = new LinkedHashMap<>(); - - public TaskImpl(Key id, Map blocks) { - this.blocks.putAll(blocks); - this.id = id; - } - - @Nullable - @Override - public TaskBlock byId(Key id) { - return this.blocks.get(id); - } - - @Override - public Map blocks() { - return Collections.unmodifiableMap(this.blocks); - } - - @Override - public Key id() { - return this.id; - } - - @Override - public @Nullable TaskBlock byAction(Action action) { - for (TaskBlock block : blocks.values()) { - if (block.contains(action)) { - return block; - } - } - return null; - } - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TaskActionParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TaskActionParser.java deleted file mode 100644 index 3bf720e3a..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TaskActionParser.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.momirealms.craftengine.core.plugin.script; - -public interface TaskActionParser { - - Action parse(TokenStringReader reader); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TaskActionParsers.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TaskActionParsers.java deleted file mode 100644 index d72978f04..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TaskActionParsers.java +++ /dev/null @@ -1,4 +0,0 @@ -package net.momirealms.craftengine.core.plugin.script; - -public class TaskActionParsers { -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TaskBlock.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TaskBlock.java deleted file mode 100644 index dd8d5a20b..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TaskBlock.java +++ /dev/null @@ -1,72 +0,0 @@ -package net.momirealms.craftengine.core.plugin.script; - -import net.momirealms.craftengine.core.util.Key; - -public interface TaskBlock { - - int size(); - - Action[] actions(); - - Key id(); - - Action byIndex(int index); - - int indexOf(Action action); - - boolean contains(Action action); - - static TaskBlock create(Key id, Action... actions) { - return new TaskBlockImpl(id, actions); - } - - class TaskBlockImpl implements TaskBlock { - private final Key id; - private final Action[] actions; - - public TaskBlockImpl(Key id, Action[] actions) { - this.actions = actions; - this.id = id; - } - - @Override - public int size() { - return actions.length; - } - - @Override - public Action[] actions() { - return this.actions; - } - - @Override - public Key id() { - return this.id; - } - - @Override - public boolean contains(Action action) { - for (Action value : this.actions) { - if (value.equals(action)) { - return true; - } - } - return false; - } - - @Override - public Action byIndex(int index) { - return this.actions[index]; - } - - @Override - public int indexOf(final Action action) { - for (int i = 0; i < this.actions.length; i++) { - if (this.actions[i].equals(action)) { - return i; - } - } - return -1; - } - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TaskContext.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TaskContext.java deleted file mode 100644 index 824568dec..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TaskContext.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.momirealms.craftengine.core.plugin.script; - -public interface TaskContext { - - Task task(); - - -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TokenStringReader.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TokenStringReader.java deleted file mode 100644 index f24355081..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/TokenStringReader.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.momirealms.craftengine.core.plugin.script; - -import net.momirealms.craftengine.core.util.StringReader; - -public interface TokenStringReader extends StringReader { - - static TokenStringReader of(String input) { - return new DefaultTokenStringReader(input); - } - - String nextToken(); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/ArgumentParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/ArgumentParser.java deleted file mode 100644 index 3965ec2a3..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/ArgumentParser.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.momirealms.craftengine.core.plugin.script.argument; - -import net.momirealms.craftengine.core.plugin.script.TokenStringReader; - -public interface ArgumentParser { - - T parse(TokenStringReader reader); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/ArgumentParsers.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/ArgumentParsers.java deleted file mode 100644 index 63ceaeb10..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/ArgumentParsers.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.momirealms.craftengine.core.plugin.script.argument; - -public class ArgumentParsers { - public static final ArgumentParser INT_PARSER = new IntArgumentParser(); - public static final ArgumentParser LONG_PARSER = new LongArgumentParser(); - public static final ArgumentParser DOUBLE_PARSER = new DoubleArgumentParser(); - - public static ArgumentParser intParser() { - return INT_PARSER; - } - - public static ArgumentParser longParser() { - return LONG_PARSER; - } - - public static ArgumentParser doubleParser() { - return DOUBLE_PARSER; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/DoubleArgumentParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/DoubleArgumentParser.java deleted file mode 100644 index 718e8cb8f..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/DoubleArgumentParser.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.momirealms.craftengine.core.plugin.script.argument; - -import net.momirealms.craftengine.core.plugin.script.TokenStringReader; - -public class DoubleArgumentParser implements ArgumentParser { - - @Override - public Double parse(TokenStringReader reader) { - String token = reader.nextToken(); - return Double.parseDouble(token); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/IntArgumentParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/IntArgumentParser.java deleted file mode 100644 index fd2a5c635..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/IntArgumentParser.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.momirealms.craftengine.core.plugin.script.argument; - -import net.momirealms.craftengine.core.plugin.script.TokenStringReader; - -public class IntArgumentParser implements ArgumentParser { - - @Override - public Integer parse(TokenStringReader reader) { - String token = reader.nextToken(); - return Integer.parseInt(token); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/LongArgumentParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/LongArgumentParser.java deleted file mode 100644 index b8c5b2355..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/script/argument/LongArgumentParser.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.momirealms.craftengine.core.plugin.script.argument; - -import net.momirealms.craftengine.core.plugin.script.TokenStringReader; - -public class LongArgumentParser implements ArgumentParser { - - @Override - public Long parse(TokenStringReader reader) { - String token = reader.nextToken(); - return Long.parseLong(token); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java index e52b5ba0f..59d7b9711 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java @@ -22,7 +22,6 @@ import net.momirealms.craftengine.core.pack.model.select.SelectPropertyFactory; import net.momirealms.craftengine.core.pack.model.special.SpecialModelFactory; import net.momirealms.craftengine.core.pack.model.tint.TintFactory; import net.momirealms.craftengine.core.plugin.config.template.TemplateArgumentFactory; -import net.momirealms.craftengine.core.plugin.script.TaskActionParser; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceKey; @@ -50,7 +49,6 @@ public class BuiltInRegistries { public static final Registry SMITHING_RESULT_PROCESSOR_FACTORY = createRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY); public static final Registry HITBOX_FACTORY = createRegistry(Registries.HITBOX_FACTORY); public static final Registry RESOURCE_PACK_HOST_FACTORY = createRegistry(Registries.RESOURCE_PACK_HOST_FACTORY); - public static final Registry TASK_ACTION_PARSER = createRegistry(Registries.TASK_ACTION_PARSER); private static Registry createRegistry(ResourceKey> key) { return new MappedRegistry<>(key); diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java index febb757b8..07e1e4ef8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java @@ -22,7 +22,6 @@ import net.momirealms.craftengine.core.pack.model.select.SelectPropertyFactory; import net.momirealms.craftengine.core.pack.model.special.SpecialModelFactory; import net.momirealms.craftengine.core.pack.model.tint.TintFactory; import net.momirealms.craftengine.core.plugin.config.template.TemplateArgumentFactory; -import net.momirealms.craftengine.core.plugin.script.TaskActionParser; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceKey; @@ -51,5 +50,4 @@ public class Registries { public static final ResourceKey> SMITHING_RESULT_PROCESSOR_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("smithing_result_processor_factory")); public static final ResourceKey> HITBOX_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("hitbox_factory")); public static final ResourceKey> RESOURCE_PACK_HOST_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("resource_pack_host_factory")); - public static final ResourceKey> TASK_ACTION_PARSER = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("task_action_parser")); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MutableInt.java b/core/src/main/java/net/momirealms/craftengine/core/util/MutableInt.java new file mode 100644 index 000000000..425f374fa --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MutableInt.java @@ -0,0 +1,21 @@ +package net.momirealms.craftengine.core.util; + +public class MutableInt { + private int value; + + public MutableInt(int value) { + this.value = value; + } + + public int intValue() { + return value; + } + + public void set(int value) { + this.value = value; + } + + public void add(final int value) { + this.value += value; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/context/ContextHolder.java b/core/src/main/java/net/momirealms/craftengine/core/util/context/ContextHolder.java index 1a259899d..7939014e9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/context/ContextHolder.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/context/ContextHolder.java @@ -1,7 +1,5 @@ package net.momirealms.craftengine.core.util.context; -import org.jetbrains.annotations.Contract; - import javax.annotation.Nullable; import java.util.HashMap; import java.util.Map; @@ -38,7 +36,6 @@ public class ContextHolder { @SuppressWarnings("unchecked") @Nullable - @Contract("_,!null->!null; _,_->_") public T getOrDefault(ContextKey parameter, @Nullable T defaultValue) { return (T) this.params.getOrDefault(parameter, defaultValue); } 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 59a4978f3..67c268b0c 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 @@ -77,16 +77,20 @@ public abstract class CEWorld { } @Nullable - public CEChunk getLoadedChunkImmediately(int x, int z) { - long longKey = ChunkPos.asLong(x, z); + public CEChunk getChunkAtIfLoaded(long chunkPos) { this.loadedChunkMapLock.readLock().lock(); try { - return this.loadedChunkMap.get(longKey); + return getChunkAtIfLoadedMainThread(chunkPos); } finally { this.loadedChunkMapLock.readLock().unlock(); } } + @Nullable + public CEChunk getChunkAtIfLoaded(int x, int z) { + return getChunkAtIfLoaded(ChunkPos.asLong(x, z)); + } + @Nullable public CEChunk getChunkAtIfLoadedMainThread(long chunkPos) { if (chunkPos == this.lastChunkPos) { @@ -105,12 +109,6 @@ public abstract class CEWorld { return getChunkAtIfLoadedMainThread(ChunkPos.asLong(x, z)); } - @Nullable - public CEChunk getChunkAtIfLoaded(int x, int z) { - long chunkPos = ChunkPos.asLong(x, z); - return this.getChunkAtIfLoadedMainThread(chunkPos); - } - public WorldHeight worldHeight() { return worldHeightAccessor; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java index f1b573d61..41667fae2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java @@ -44,6 +44,16 @@ public class CEChunk { this.fillEmptySection(); } + public boolean isEmpty() { + if (!this.entities.isEmpty()) return false; + for (CESection section : this.sections) { + if (section != null && !section.statesContainer().isEmpty()) { + return false; + } + } + return true; + } + private void fillEmptySection() { for (int i = 0; i < sections.length; ++i) { if (sections[i] == null) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java index 676968ed2..92eb3dd3e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java @@ -17,15 +17,15 @@ public class CESection { } public void setBlockState(BlockPos pos, ImmutableBlockState state) { - setBlockState(pos.x() & 15, pos.y() & 15, pos.z() & 15, state); + this.setBlockState(pos.x() & 15, pos.y() & 15, pos.z() & 15, state); } public void setBlockState(int x, int y, int z, ImmutableBlockState state) { - statesContainer.set((y << 4 | z) << 4 | x, state); + this.statesContainer.set((y << 4 | z) << 4 | x, state); } public void setBlockState(int index, ImmutableBlockState state) { - statesContainer.set(index, state); + this.statesContainer.set(index, state); } public ImmutableBlockState getBlockState(BlockPos pos) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/ChunkSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java similarity index 90% rename from core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/ChunkSerializer.java rename to core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java index 878c2ccaf..62dacbd48 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/ChunkSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java @@ -11,7 +11,7 @@ import org.jetbrains.annotations.Nullable; import java.util.List; -public class ChunkSerializer { +public class DefaultChunkSerializer { @Nullable public static CompoundTag serialize(@NotNull CEChunk chunk) { @@ -19,7 +19,7 @@ public class ChunkSerializer { CESection[] ceSections = chunk.sections(); for (CESection ceSection : ceSections) { if (ceSection != null) { - CompoundTag sectionNbt = SectionSerializer.serialize(ceSection); + CompoundTag sectionNbt = DefaultSectionSerializer.serialize(ceSection); if (sectionNbt != null) { sections.add(sectionNbt); } @@ -38,7 +38,7 @@ public class ChunkSerializer { CESection[] sectionArray = new CESection[world.worldHeight().getSectionsCount()]; for (int i = 0, size = sections.size(); i < size; ++i) { CompoundTag sectionTag = sections.getCompound(i); - CESection ceSection = SectionSerializer.deserialize(sectionTag); + CESection ceSection = DefaultSectionSerializer.deserialize(sectionTag); if (ceSection != null) { int sectionIndex = world.worldHeight().getSectionIndexFromSectionY(ceSection.sectionY()); if (sectionIndex >= 0 && sectionIndex < sectionArray.length) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/SectionSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java similarity index 98% rename from core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/SectionSerializer.java rename to core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java index dd93b67e5..2505618b3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/SectionSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java @@ -24,7 +24,7 @@ import java.util.List; import java.util.Optional; import java.util.stream.LongStream; -public class SectionSerializer { +public class DefaultSectionSerializer { @Nullable public static CompoundTag serialize(@NotNull CESection section) { 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 3881127f2..bfeef7a58 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 @@ -5,9 +5,13 @@ import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.ExceptionCollector; import net.momirealms.craftengine.core.util.FileUtils; +import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.ChunkPos; +import net.momirealms.craftengine.core.world.chunk.CEChunk; +import net.momirealms.craftengine.core.world.chunk.serialization.DefaultChunkSerializer; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.NBT; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.DataInputStream; @@ -117,15 +121,14 @@ public class DefaultRegionFileStorage implements WorldDataStorage { } @Override - @Nullable - public CompoundTag readChunkTagAt(ChunkPos pos) throws IOException { + public @NotNull CEChunk readChunkAt(@NotNull CEWorld world, @NotNull ChunkPos pos) throws IOException { RegionFile regionFile = this.getRegionFile(pos, false, true); try { DataInputStream dataInputStream = regionFile.getChunkDataInputStream(pos); CompoundTag tag; try { if (dataInputStream == null) { - return null; + return new CEChunk(world, pos); } tag = NBT.readCompound(dataInputStream, false); } catch (Throwable t1) { @@ -137,14 +140,19 @@ public class DefaultRegionFileStorage implements WorldDataStorage { throw t1; } dataInputStream.close(); - return tag; + return DefaultChunkSerializer.deserialize(world, pos, tag); } finally { regionFile.fileLock.unlock(); } } @Override - public void writeChunkTagAt(ChunkPos pos, @Nullable CompoundTag nbt) throws IOException { + public void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk) throws IOException { + CompoundTag nbt = DefaultChunkSerializer.serialize(chunk); + writeChunkTagAt(pos, nbt); + } + + public void writeChunkTagAt(@NotNull ChunkPos pos, @Nullable CompoundTag nbt) throws IOException { RegionFile regionFile = this.getRegionFile(pos, nbt == null, true); try { if (nbt == null) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DefaultStorageAdaptor.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DefaultStorageAdaptor.java index 9de5d571c..401ed6880 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DefaultStorageAdaptor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DefaultStorageAdaptor.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.world.chunk.storage; +import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.World; import org.jetbrains.annotations.NotNull; @@ -8,6 +9,10 @@ public class DefaultStorageAdaptor implements StorageAdaptor { @Override public @NotNull WorldDataStorage adapt(@NotNull World world) { - return new DefaultRegionFileStorage(world.directory().resolve(CEWorld.REGION_DIRECTORY)); + if (Config.delaySerialization() > 0) { + return new DelayedDefaultRegionFileStorage(world.directory().resolve(CEWorld.REGION_DIRECTORY), Config.delaySerialization()); + } else { + return new DefaultRegionFileStorage(world.directory().resolve(CEWorld.REGION_DIRECTORY)); + } } } 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 new file mode 100644 index 000000000..e6a8ea791 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/DelayedDefaultRegionFileStorage.java @@ -0,0 +1,72 @@ +package net.momirealms.craftengine.core.world.chunk.storage; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.RemovalCause; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.ChunkPos; +import net.momirealms.craftengine.core.world.chunk.CEChunk; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; + +public class DelayedDefaultRegionFileStorage extends DefaultRegionFileStorage { + private final Cache chunkCache; + + public DelayedDefaultRegionFileStorage(Path directory, int time) { + super(directory); + this.chunkCache = Caffeine.newBuilder() + .expireAfterWrite(time, TimeUnit.SECONDS) + .removalListener((ChunkPos key, CEChunk value, RemovalCause cause) -> { + if (key == null || value == null) { + return; + } + if (cause == RemovalCause.EXPIRED || cause == RemovalCause.SIZE) { + try { + super.writeChunkAt(key, value); + } catch (IOException e) { + CraftEngine.instance().logger().warn("Failed to write chunk at " + key, e); + } + } + }) + .build(); + } + + @Override + public @NotNull CEChunk readChunkAt(@NotNull CEWorld world, @NotNull ChunkPos pos) throws IOException { + CEChunk chunk = this.chunkCache.asMap().remove(pos); + if (chunk != null) { + return chunk; + } + return super.readChunkAt(world, pos); + } + + @Override + public void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk) throws IOException { + if (chunk.isEmpty()) { + super.writeChunkTagAt(pos, null); + return; + } + this.chunkCache.put(pos, chunk); + } + + @Override + public synchronized void close() throws IOException { + this.saveCache(); + super.close(); + } + + private void saveCache() { + try { + for (var chunk : this.chunkCache.asMap().entrySet()) { + super.writeChunkAt(chunk.getKey(), chunk.getValue()); + } + } catch (IOException e) { + CraftEngine.instance().logger().warn("Failed to save chunks", e); + } + this.chunkCache.invalidateAll(); + } +} \ No newline at end of file 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 a9e76effd..bd44492a9 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 @@ -1,17 +1,18 @@ package net.momirealms.craftengine.core.world.chunk.storage; +import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.ChunkPos; -import net.momirealms.sparrow.nbt.CompoundTag; -import org.jetbrains.annotations.Nullable; +import net.momirealms.craftengine.core.world.chunk.CEChunk; +import org.jetbrains.annotations.NotNull; import java.io.IOException; public interface WorldDataStorage { - @Nullable - CompoundTag readChunkTagAt(ChunkPos pos) throws IOException; + @NotNull + CEChunk readChunkAt(@NotNull CEWorld world, @NotNull ChunkPos pos) throws IOException; - void writeChunkTagAt(ChunkPos pos, @Nullable CompoundTag nbt) throws IOException; + void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk) throws IOException; void flush() throws IOException; diff --git a/gradle.properties b/gradle.properties index 0a73fcf03..913e020cc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=0.0.50 +project_version=0.0.51-beta.1 config_version=30 lang_version=7 project_group=net.momirealms @@ -45,7 +45,7 @@ sparrow_util_version=0.39 fastutil_version=8.5.15 netty_version=4.1.119.Final joml_version=1.10.8 -datafixerupper_version=1.0.20 +datafixerupper_version=6.0.8 mojang_brigadier_version=1.0.18 byte_buddy_version=1.17.5 ahocorasick_version=0.6.3