From 0554b31f1195c7561564ef066058ecf2f6d73e80 Mon Sep 17 00:00:00 2001 From: Jordan Date: Tue, 27 Jun 2023 17:37:09 +0100 Subject: [PATCH] feat: implement removal of entities if they would be in a block after the edit (#2311) - Includes some refactoring to EditSessionBuilder to prevent doubling-up of processors that are also extents - Better ordering of the EditSessionBuilder process/extent code to match where extents actually end up in the stack - Fixes #1941 --- .../core/configuration/Settings.java | 7 +- .../EntityInBlockRemovingProcessor.java | 57 ++++++++++++ .../core/extent/processor/ProcessorScope.java | 3 +- .../sk89q/worldedit/EditSessionBuilder.java | 88 +++++++++++-------- .../function/entity/ExtentEntityCopy.java | 12 +-- 5 files changed, 115 insertions(+), 52 deletions(-) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/EntityInBlockRemovingProcessor.java diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java index 90ed792fa..a8d28d048 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java @@ -622,10 +622,11 @@ public class Settings extends Config { public boolean PERSISTENT_BRUSHES = true; @Comment({ - "[SAFE] Keep entities that are positioned in non-air blocks when editing an area", - "Might cause client-side FPS lag in some situations" + "[SAFE] Keep entities that are positioned in non-air blocks when editing an area (default: true)", + " - Might cause client-side FPS lag in some situations", + " - Requires fast-placement to be true" }) - public boolean KEEP_ENTITIES_IN_BLOCKS = false; + public boolean KEEP_ENTITIES_IN_BLOCKS = true; @Comment({ "[SAFE] Attempt to remove entities from the world if they were not present in the expected chunk (default: true)", diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/EntityInBlockRemovingProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/EntityInBlockRemovingProcessor.java new file mode 100644 index 000000000..0a8839409 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/EntityInBlockRemovingProcessor.java @@ -0,0 +1,57 @@ +package com.fastasyncworldedit.core.extent.processor; + +import com.fastasyncworldedit.core.queue.IBatchProcessor; +import com.fastasyncworldedit.core.queue.IChunk; +import com.fastasyncworldedit.core.queue.IChunkGet; +import com.fastasyncworldedit.core.queue.IChunkSet; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.block.BlockTypes; +import org.jetbrains.annotations.Nullable; + +/** + * Processor that removes existing entities that would not be in air after the edit + * + * @since TODO + */ +public class EntityInBlockRemovingProcessor implements IBatchProcessor { + + @Override + public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { + for (CompoundTag tag : get.getEntities()) { + // Empty tags for seemingly non-existent entities can exist? + if (tag.getList("Pos").size() == 0) { + continue; + } + BlockVector3 pos = tag.getEntityPosition().toBlockPoint(); + int x = pos.getX() & 15; + int y = pos.getY(); + int z = pos.getZ() & 15; + if (!set.hasSection(y >> 4)) { + continue; + } + if (set.getBlock(x, y, z).getBlockType() != BlockTypes.__RESERVED__ && !set + .getBlock(x, y, z) + .getBlockType() + .getMaterial() + .isAir()) { + set.removeEntity(tag.getUUID()); + } + } + return set; + } + + @Nullable + @Override + public Extent construct(final Extent child) { + throw new UnsupportedOperationException("Processing only"); + } + + @Override + public ProcessorScope getScope() { + // After block removal but before history + return ProcessorScope.CUSTOM; + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/ProcessorScope.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/ProcessorScope.java index e3f09e0a3..503351fb7 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/ProcessorScope.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/ProcessorScope.java @@ -7,7 +7,8 @@ package com.fastasyncworldedit.core.extent.processor; * - CHANGING_BLOCKS (processors that may ADD or CHANGE blocks being set) * - REMOVING_BLOCKS (processors that may ADD, CHANGE or REMOVE blocks being set) * - CUSTOM (processors that do not specify a SCOPE) - * - READING_SET_BLOCKS (processors that do not alter blocks at all, and read the blocks that are actually going to set, e.g. history processors) + * - READING_SET_BLOCKS (processors that do not alter blocks at all, and read the blocks that are actually going to set, e.g. + * history processors). There is no guarantee that changes made here will be stored in history. */ public enum ProcessorScope { ADDING_BLOCKS(0), diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java index ea27f03b5..e5c53a45b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java @@ -32,6 +32,7 @@ import com.fastasyncworldedit.core.extent.NullExtent; import com.fastasyncworldedit.core.extent.SingleRegionExtent; import com.fastasyncworldedit.core.extent.SlowExtent; import com.fastasyncworldedit.core.extent.StripNBTExtent; +import com.fastasyncworldedit.core.extent.processor.EntityInBlockRemovingProcessor; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightmapProcessor; import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter; import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode; @@ -543,25 +544,6 @@ public final class EditSessionBuilder { } } } - FaweRegionExtent regionExtent = null; - if (disallowedRegions != null) { // Always use MultiRegionExtent if we have blacklist regions - regionExtent = new MultiRegionExtent(this.extent, this.limit, allowedRegions, disallowedRegions); - } else if (allowedRegions == null) { - allowedRegions = new Region[]{RegionWrapper.GLOBAL()}; - } else { - if (allowedRegions.length == 0) { - regionExtent = new NullExtent(this.extent, FaweCache.NO_REGION); - } else { - if (allowedRegions.length == 1) { - regionExtent = new SingleRegionExtent(this.extent, this.limit, allowedRegions[0]); - } else { - regionExtent = new MultiRegionExtent(this.extent, this.limit, allowedRegions, null); - } - } - } - if (placeChunks && regionExtent != null) { - queue.addProcessor(regionExtent); - } // There's no need to do the below (and it'll also just be a pain to implement) if we're not placing chunks if (placeChunks) { if (((relightMode != null && relightMode != RelightMode.NONE) || (relightMode == null && Settings.settings().LIGHTING.MODE > 0))) { @@ -571,6 +553,11 @@ public final class EditSessionBuilder { queue.addProcessor(new RelightProcessor(relighter)); } queue.addProcessor(new HeightmapProcessor(world.getMinY(), world.getMaxY())); + + if (!Settings.settings().EXPERIMENTAL.KEEP_ENTITIES_IN_BLOCKS) { + queue.addProcessor(new EntityInBlockRemovingProcessor()); + } + IBatchProcessor platformProcessor = WorldEdit .getInstance() .getPlatformManager() @@ -590,24 +577,13 @@ public final class EditSessionBuilder { } else { relighter = NullRelighter.INSTANCE; } - Consumer onErrorMessage; - if (getActor() != null) { - onErrorMessage = c -> getActor().print(Caption.of("fawe.error.occurred-continuing", c)); - } else { - onErrorMessage = c -> { - }; - } - if (limit != null && !limit.isUnlimited() && regionExtent != null) { - this.extent = new LimitExtent(regionExtent, limit, onErrorMessage); - } else if (limit != null && !limit.isUnlimited()) { - this.extent = new LimitExtent(this.extent, limit, onErrorMessage); - } else if (regionExtent != null) { - this.extent = regionExtent; - } if (this.limit != null && this.limit.STRIP_NBT != null && !this.limit.STRIP_NBT.isEmpty()) { - this.extent = new StripNBTExtent(this.extent, this.limit.STRIP_NBT); + StripNBTExtent ext = new StripNBTExtent(this.extent, this.limit.STRIP_NBT); if (placeChunks) { - queue.addProcessor((IBatchProcessor) this.extent); + queue.addProcessor(ext); + } + if (!placeChunks || !combineStages) { + this.extent = ext; } } if (this.limit != null && !this.limit.isUnlimited()) { @@ -620,12 +596,50 @@ public final class EditSessionBuilder { } Set> remaps = this.limit.REMAP_PROPERTIES; if (!limitBlocks.isEmpty() || (remaps != null && !remaps.isEmpty())) { - this.extent = new DisallowedBlocksExtent(this.extent, limitBlocks, remaps); + DisallowedBlocksExtent ext = new DisallowedBlocksExtent(this.extent, limitBlocks, remaps); if (placeChunks) { - queue.addProcessor((IBatchProcessor) this.extent); + queue.addProcessor(ext); + } + if (!placeChunks || !combineStages) { + this.extent = ext; } } } + + FaweRegionExtent regionExtent = null; + if (disallowedRegions != null) { // Always use MultiRegionExtent if we have blacklist regions + regionExtent = new MultiRegionExtent(this.extent, this.limit, allowedRegions, disallowedRegions); + } else if (allowedRegions == null) { + allowedRegions = new Region[]{RegionWrapper.GLOBAL()}; + } else { + if (allowedRegions.length == 0) { + regionExtent = new NullExtent(this.extent, FaweCache.NO_REGION); + } else { + if (allowedRegions.length == 1) { + regionExtent = new SingleRegionExtent(this.extent, this.limit, allowedRegions[0]); + } else { + regionExtent = new MultiRegionExtent(this.extent, this.limit, allowedRegions, null); + } + } + } + if (regionExtent != null) { + if (placeChunks) { + queue.addProcessor(regionExtent); + } + if (!placeChunks || !combineStages) { + this.extent = regionExtent; + } + } + Consumer onErrorMessage; + if (getActor() != null) { + onErrorMessage = c -> getActor().print(Caption.of("fawe.error.occurred-continuing", c)); + } else { + onErrorMessage = c -> { + }; + } + if (limit != null && !limit.isUnlimited()) { + this.extent = new LimitExtent(this.extent, limit, onErrorMessage); + } this.extent = wrapExtent(this.extent, eventBus, event, EditSession.Stage.BEFORE_HISTORY); } return this; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java index 88f151e28..7a034d7bd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java @@ -160,17 +160,7 @@ public class ExtentEntityCopy implements EntityFunction { // Remove if (isRemoving() && success) { //FAWE start - UUID uuid = null; - if (tag.containsKey("UUID")) { - int[] arr = tag.getIntArray("UUID"); - uuid = new UUID((long) arr[0] << 32 | (arr[1] & 0xFFFFFFFFL), (long) arr[2] << 32 | (arr[3] & 0xFFFFFFFFL)); - } else if (tag.containsKey("UUIDMost")) { - uuid = new UUID(tag.getLong("UUIDMost"), tag.getLong("UUIDLeast")); - } else if (tag.containsKey("WorldUUIDMost")) { - uuid = new UUID(tag.getLong("WorldUUIDMost"), tag.getLong("WorldUUIDLeast")); - } else if (tag.containsKey("PersistentIDMSB")) { - uuid = new UUID(tag.getLong("PersistentIDMSB"), tag.getLong("PersistentIDLSB")); - } + UUID uuid = entity.getState().getNbtData().getUUID(); if (uuid != null) { if (source != null) { source.removeEntity(