mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-12-23 01:37:37 +00:00
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
This commit is contained in:
parent
476ba4ab41
commit
0554b31f11
@ -622,10 +622,11 @@ public class Settings extends Config {
|
|||||||
public boolean PERSISTENT_BRUSHES = true;
|
public boolean PERSISTENT_BRUSHES = true;
|
||||||
|
|
||||||
@Comment({
|
@Comment({
|
||||||
"[SAFE] Keep entities that are positioned in non-air blocks when editing an area",
|
"[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"
|
" - 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({
|
@Comment({
|
||||||
"[SAFE] Attempt to remove entities from the world if they were not present in the expected chunk (default: true)",
|
"[SAFE] Attempt to remove entities from the world if they were not present in the expected chunk (default: true)",
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -7,7 +7,8 @@ package com.fastasyncworldedit.core.extent.processor;
|
|||||||
* - CHANGING_BLOCKS (processors that may ADD or CHANGE blocks being set)
|
* - CHANGING_BLOCKS (processors that may ADD or CHANGE blocks being set)
|
||||||
* - REMOVING_BLOCKS (processors that may ADD, CHANGE or REMOVE blocks being set)
|
* - REMOVING_BLOCKS (processors that may ADD, CHANGE or REMOVE blocks being set)
|
||||||
* - CUSTOM (processors that do not specify a SCOPE)
|
* - 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 {
|
public enum ProcessorScope {
|
||||||
ADDING_BLOCKS(0),
|
ADDING_BLOCKS(0),
|
||||||
|
@ -32,6 +32,7 @@ import com.fastasyncworldedit.core.extent.NullExtent;
|
|||||||
import com.fastasyncworldedit.core.extent.SingleRegionExtent;
|
import com.fastasyncworldedit.core.extent.SingleRegionExtent;
|
||||||
import com.fastasyncworldedit.core.extent.SlowExtent;
|
import com.fastasyncworldedit.core.extent.SlowExtent;
|
||||||
import com.fastasyncworldedit.core.extent.StripNBTExtent;
|
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.heightmap.HeightmapProcessor;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
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
|
// 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 (placeChunks) {
|
||||||
if (((relightMode != null && relightMode != RelightMode.NONE) || (relightMode == null && Settings.settings().LIGHTING.MODE > 0))) {
|
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 RelightProcessor(relighter));
|
||||||
}
|
}
|
||||||
queue.addProcessor(new HeightmapProcessor(world.getMinY(), world.getMaxY()));
|
queue.addProcessor(new HeightmapProcessor(world.getMinY(), world.getMaxY()));
|
||||||
|
|
||||||
|
if (!Settings.settings().EXPERIMENTAL.KEEP_ENTITIES_IN_BLOCKS) {
|
||||||
|
queue.addProcessor(new EntityInBlockRemovingProcessor());
|
||||||
|
}
|
||||||
|
|
||||||
IBatchProcessor platformProcessor = WorldEdit
|
IBatchProcessor platformProcessor = WorldEdit
|
||||||
.getInstance()
|
.getInstance()
|
||||||
.getPlatformManager()
|
.getPlatformManager()
|
||||||
@ -590,24 +577,13 @@ public final class EditSessionBuilder {
|
|||||||
} else {
|
} else {
|
||||||
relighter = NullRelighter.INSTANCE;
|
relighter = NullRelighter.INSTANCE;
|
||||||
}
|
}
|
||||||
Consumer<Component> 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()) {
|
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) {
|
if (placeChunks) {
|
||||||
queue.addProcessor((IBatchProcessor) this.extent);
|
queue.addProcessor(ext);
|
||||||
|
}
|
||||||
|
if (!placeChunks || !combineStages) {
|
||||||
|
this.extent = ext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.limit != null && !this.limit.isUnlimited()) {
|
if (this.limit != null && !this.limit.isUnlimited()) {
|
||||||
@ -620,12 +596,50 @@ public final class EditSessionBuilder {
|
|||||||
}
|
}
|
||||||
Set<PropertyRemap<?>> remaps = this.limit.REMAP_PROPERTIES;
|
Set<PropertyRemap<?>> remaps = this.limit.REMAP_PROPERTIES;
|
||||||
if (!limitBlocks.isEmpty() || (remaps != null && !remaps.isEmpty())) {
|
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) {
|
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<Component> 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);
|
this.extent = wrapExtent(this.extent, eventBus, event, EditSession.Stage.BEFORE_HISTORY);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
@ -160,17 +160,7 @@ public class ExtentEntityCopy implements EntityFunction {
|
|||||||
// Remove
|
// Remove
|
||||||
if (isRemoving() && success) {
|
if (isRemoving() && success) {
|
||||||
//FAWE start
|
//FAWE start
|
||||||
UUID uuid = null;
|
UUID uuid = entity.getState().getNbtData().getUUID();
|
||||||
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"));
|
|
||||||
}
|
|
||||||
if (uuid != null) {
|
if (uuid != null) {
|
||||||
if (source != null) {
|
if (source != null) {
|
||||||
source.removeEntity(
|
source.removeEntity(
|
||||||
|
Loading…
Reference in New Issue
Block a user