diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java index 2c56099ec..e2523d3f1 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java @@ -25,6 +25,7 @@ import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.ReflectionUtils; +import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.SectionPos; @@ -34,9 +35,7 @@ import org.bukkit.Bukkit; import java.io.IOException; import java.lang.reflect.Method; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; +import java.util.*; import static java.util.Objects.requireNonNull; @@ -44,7 +43,7 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { private final Set chunksToSave; private final CEWorld ceWorld; private static int[] ordinalToIbdID; - private static final Set chunksNeedInjection = new HashSet<>(); + private static final Set BROKEN_CHUNKS = Collections.synchronizedSet(new HashSet<>()); protected FastAsyncWorldEditDelegate(EditSessionEvent event) { super(event.getExtent()); @@ -69,40 +68,26 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { @Subscribe @SuppressWarnings("unused") public void onEditSessionEvent(EditSessionEvent event) { - if (event.getWorld() == null) return; + World weWorld = event.getWorld(); + if (weWorld == null) return; if (event.getStage() == EditSession.Stage.BEFORE_CHANGE) { event.setExtent(new FastAsyncWorldEditDelegate(event)); - } else if (event.getStage() == EditSession.Stage.BEFORE_HISTORY) { - event.setExtent(new AbstractDelegateExtent(event.getExtent()) { - @Override - public Operation commit() { - Set processedChunk = new HashSet<>(); - org.bukkit.World world = Bukkit.getWorld(requireNonNull(event.getWorld()).getName()); - CEWorld ceWorld = CraftEngine.instance().worldManager().getWorld(requireNonNull(world).getUID()); - chunksNeedInjection.forEach(ceChunk -> { - injectLevelChunk(requireNonNull(ceWorld), ceChunk); - processedChunk.add(ceChunk); - }); - processedChunk.forEach(chunksNeedInjection::remove); - return super.commit(); - } - }); } } }); } - private static void injectLevelChunk(CEWorld ceWorld, CEChunk ceChunk) { + private static void injectLevelChunk(CEChunk ceChunk) { ChunkPos pos = ceChunk.chunkPos(); CESection[] ceSections = ceChunk.sections(); - Object worldServer = ceWorld.world().serverWorld(); + Object worldServer = ceChunk.world().world().serverWorld(); Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer); Object levelChunk = FastNMS.INSTANCE.method$ServerChunkCache$getChunkAtIfLoadedMainThread(chunkSource, pos.x, pos.z); Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(levelChunk); for (int i = 0; i < ceSections.length; i++) { CESection ceSection = ceSections[i]; Object section = sections[i]; - BukkitInjector.injectLevelChunkSection(section, ceSection, ceWorld, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z)); + BukkitInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z)); } } @@ -158,7 +143,14 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { @Override protected Operation commitBefore() { - saveAllChunks(); + List chunks = new ArrayList<>(BROKEN_CHUNKS); + BROKEN_CHUNKS.clear(); + for (ChunkPos chunk : chunks) { + CEChunk loaded = this.ceWorld.getChunkAtIfLoaded(chunk.longKey()); + // only inject loaded chunks + if (loaded == null) continue; + injectLevelChunk(loaded); + } return super.commitBefore(); } @@ -179,6 +171,7 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { int chunkZ = blockZ >> 4; int newStateId = ordinalToIbdID[newBlock.getOrdinal()]; int oldStateId = ordinalToIbdID[oldBlock.getOrdinal()]; + BROKEN_CHUNKS.add(ChunkPos.of(chunkX, chunkZ)); //CraftEngine.instance().debug(() -> "Processing block at " + blockX + ", " + blockY + ", " + blockZ + ": " + oldStateId + " -> " + newStateId); if (BlockStateUtils.isVanillaBlock(newStateId) && BlockStateUtils.isVanillaBlock(oldStateId)) return; try { @@ -200,7 +193,6 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { try { for (CEChunk ceChunk : this.chunksToSave) { CraftEngine.instance().debug(() -> "Saving chunk " + ceChunk.chunkPos()); - chunksNeedInjection.add(ceChunk); this.ceWorld.worldDataStorage().writeChunkAt(ceChunk.chunkPos(), ceChunk, true); } this.chunksToSave.clear(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java index 9035d930c..501519f00 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java @@ -388,7 +388,7 @@ public class BukkitInjector { // } // } - public synchronized static void injectLevelChunkSection(Object targetSection, CESection ceSection, CEWorld ceWorld, CEChunk chunk, SectionPos pos) { + public synchronized static void injectLevelChunkSection(Object targetSection, CESection ceSection, CEChunk chunk, SectionPos pos) { try { Object container = FastNMS.INSTANCE.field$LevelChunkSection$states(targetSection); if (!(container instanceof InjectedPalettedContainerHolder)) { @@ -399,7 +399,7 @@ public class BukkitInjector { injectedObject = (InjectedPalettedContainerHolder) Reflections.UNSAFE.allocateInstance(clazz$InjectedPalettedContainer); varHandle$InjectedPalettedContainer$target.set(injectedObject, container); } - injectedObject.ceWorld(ceWorld); + injectedObject.ceWorld(chunk.world()); injectedObject.ceChunk(chunk); injectedObject.ceSection(ceSection); injectedObject.cePos(pos); @@ -422,7 +422,7 @@ public class BukkitInjector { if (states instanceof InjectedPalettedContainerHolder holder) { Reflections.field$LevelChunkSection$states.set(section, holder.target()); } - } catch (Exception e) { + } catch (ReflectiveOperationException e) { CraftEngine.instance().logger().severe("Failed to inject chunk section", e); } } 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 c644076c8..e6bd597df 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 @@ -383,7 +383,7 @@ public class BukkitWorldManager implements WorldManager, Listener { } } } - BukkitInjector.injectLevelChunkSection(section, ceSection, ceWorld, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z)); + BukkitInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z)); } if (Config.enableRecipeSystem()) { @SuppressWarnings("unchecked") diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/command/FlagKeys.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/command/FlagKeys.java index 5cde8bd5c..a53e05dfa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/command/FlagKeys.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/command/FlagKeys.java @@ -3,8 +3,10 @@ package net.momirealms.craftengine.core.plugin.command; import org.incendo.cloud.parser.flag.CommandFlag; public final class FlagKeys { + private FlagKeys() {} + public static final String SILENT = "silent"; - public static final CommandFlag SILENT_FLAG = CommandFlag.builder("silent").withAliases("s").build(); + public static final CommandFlag SILENT_FLAG = CommandFlag.builder(SILENT).withAliases("s").build(); public static final String TO_INVENTORY = "to-inventory"; - public static final CommandFlag TO_INVENTORY_FLAG = CommandFlag.builder("to-inventory").build(); + public static final CommandFlag TO_INVENTORY_FLAG = CommandFlag.builder(TO_INVENTORY).build(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java b/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java index d64ff68c0..6962e334b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java @@ -12,6 +12,10 @@ public class ChunkPos { this.longKey = asLong(this.x, this.z); } + public static ChunkPos of(final int x, final int z) { + return new ChunkPos(x, z); + } + public ChunkPos(BlockPos pos) { this.x = SectionPos.blockToSectionCoord(pos.x()); this.z = SectionPos.blockToSectionCoord(pos.z());