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..901a741ae 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 @@ -34,9 +34,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 +42,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 +67,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(Object chunkSource, CEChunk ceChunk) { ChunkPos pos = ceChunk.chunkPos(); - CESection[] ceSections = ceChunk.sections(); - Object worldServer = ceWorld.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)); + Object levelChunk = FastNMS.INSTANCE.method$ServerChunkCache$getChunk(chunkSource, pos.x, pos.z, false); + if (levelChunk != null) { + Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(levelChunk); + CESection[] ceSections = ceChunk.sections(); + for (int i = 0; i < ceSections.length; i++) { + CESection ceSection = ceSections[i]; + Object section = sections[i]; + BukkitInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z)); + } } } @@ -159,6 +143,16 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { @Override protected Operation commitBefore() { saveAllChunks(); + List chunks = new ArrayList<>(BROKEN_CHUNKS); + BROKEN_CHUNKS.clear(); + Object worldServer = this.ceWorld.world().serverWorld(); + Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer); + for (ChunkPos chunk : chunks) { + CEChunk loaded = this.ceWorld.getChunkAtIfLoaded(chunk.longKey()); + // only inject loaded chunks + if (loaded == null) continue; + injectLevelChunk(chunkSource, loaded); + } return super.commitBefore(); } @@ -171,7 +165,6 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { int blockZ = position.z(); this.processBlock(blockX, blockY, blockZ, blockState, oldBlockState); } - saveAllChunks(); } private void processBlock(int blockX, int blockY, int blockZ, BaseBlock newBlock, BaseBlock oldBlock) { @@ -179,6 +172,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 +194,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/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index f61092a23..ff062ebec 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -120,10 +120,6 @@ public class FurnitureItemBehavior extends ItemBehavior { return InteractionResult.FAIL; } - if (!BukkitCraftEngine.instance().antiGrief().canPlace(bukkitPlayer, furnitureLocation)) { - return InteractionResult.FAIL; - } - FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, anchorType, furnitureLocation.clone(), DirectionUtils.toBlockFace(clickedFace), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z())); if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) { 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/plugin/scheduler/AbstractJavaScheduler.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/AbstractJavaScheduler.java index 1198fa6ea..c728459ac 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/AbstractJavaScheduler.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/AbstractJavaScheduler.java @@ -19,7 +19,6 @@ public abstract class AbstractJavaScheduler implements SchedulerAdapter { public AbstractJavaScheduler(Plugin plugin) { this.plugin = plugin; - this.scheduler = new ScheduledThreadPoolExecutor(4, r -> { Thread thread = Executors.defaultThreadFactory().newThread(r); thread.setName("craft-engine-scheduler"); 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()); 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 1df369736..f711fbbb3 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 @@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.world.chunk.CEChunk; import org.jetbrains.annotations.NotNull; import java.io.IOException; +import java.nio.channels.ClosedChannelException; import java.nio.file.Path; import java.util.concurrent.TimeUnit; @@ -28,6 +29,11 @@ public class DelayedDefaultRegionFileStorage extends DefaultRegionFileStorage { if (cause == RemovalCause.EXPIRED || cause == RemovalCause.SIZE) { try { super.writeChunkAt(key, value, true); + } catch (ClosedChannelException e) { + if (this.isClosed) { + return; + } + CraftEngine.instance().logger().warn("Failed to write chunk at " + key, e); } catch (IOException e) { CraftEngine.instance().logger().warn("Failed to write chunk at " + key, e); } @@ -60,9 +66,9 @@ public class DelayedDefaultRegionFileStorage extends DefaultRegionFileStorage { @Override public synchronized void close() throws IOException { + this.isClosed = true; this.saveCache(); this.chunkCache.cleanUp(); - this.isClosed = true; super.close(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/RegionFile.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/RegionFile.java index b2e8ad51b..b50452bae 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/RegionFile.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/storage/RegionFile.java @@ -11,6 +11,7 @@ import java.io.*; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.IntBuffer; +import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; @@ -308,6 +309,9 @@ public class RegionFile implements AutoCloseable { } public void clear(ChunkPos pos) throws IOException { + if (!this.fileChannel.isOpen()) { + throw new ClosedChannelException(); + } int chunkLocation = RegionFile.getChunkLocation(pos); int sectorInfo = this.sectorInfo.get(chunkLocation); if (sectorInfo != INFO_NOT_PRESENT) { @@ -321,6 +325,9 @@ public class RegionFile implements AutoCloseable { @SuppressWarnings("ResultOfMethodCallIgnored") protected synchronized void write(ChunkPos pos, ByteBuffer buf) throws IOException { + if (!this.fileChannel.isOpen()) { + throw new ClosedChannelException(); + } // get old offset info int offsetIndex = RegionFile.getChunkLocation(pos); int previousSectorInfo = this.sectorInfo.get(offsetIndex); diff --git a/gradle.properties b/gradle.properties index 749c4cce7..980c2704a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -50,7 +50,7 @@ byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 snake_yaml_version=2.4 anti_grief_version=0.15 -nms_helper_version=0.64.5 +nms_helper_version=0.64.6 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.31.23