From 42cbc195fec9ed20993d45c1590c24406624c453 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Fri, 14 Jul 2023 17:08:05 +0100 Subject: [PATCH 1/8] feat: implement "unloaded-entity" operations - Add new extent that does an action on chunk GET load - closes #1826 --- .../fawe/v1_20_R2/PaperweightGetBlocks.java | 57 ++- .../v1_20_R2/PaperweightGetBlocks_Copy.java | 19 + .../v1_20_R2/PaperweightPostProcessor.java | 2 +- .../fawe/v1_20_R3/PaperweightGetBlocks.java | 57 ++- .../v1_20_R3/PaperweightGetBlocks_Copy.java | 19 + .../v1_20_R3/PaperweightPostProcessor.java | 2 +- .../fawe/v1_20_R4/PaperweightGetBlocks.java | 57 ++- .../v1_20_R4/PaperweightGetBlocks_Copy.java | 19 + .../v1_20_R4/PaperweightPostProcessor.java | 2 +- .../fawe/v1_21_R1/PaperweightGetBlocks.java | 59 ++- .../v1_21_R1/PaperweightGetBlocks_Copy.java | 19 + .../v1_21_R1/PaperweightPostProcessor.java | 2 +- .../core/configuration/Settings.java | 7 + .../core/extent/OncePerChunkExtent.java | 171 ++++++++ .../processor/BatchProcessorHolder.java | 10 + .../extent/processor/MultiBatchProcessor.java | 12 +- .../core/extent/processor/ProcessorScope.java | 28 +- .../heightmap/HeightmapProcessor.java | 2 +- .../processor/lighting/RelightProcessor.java | 4 +- .../history/changeset/AbstractChangeSet.java | 2 +- .../core/math/LocalBlockVector2Set.java | 382 ++++++++++++++++++ .../core/math/LocalBlockVectorSet.java | 4 +- .../core/math/MutableBlockVector2.java | 9 + .../fastasyncworldedit/core/queue/Filter.java | 5 +- .../core/queue/IBatchProcessor.java | 13 +- .../core/queue/IBlocks.java | 12 +- .../core/queue/IChunkGet.java | 9 + .../SingleThreadQueueExtent.java | 4 +- .../implementation/blocks/BitSetBlocks.java | 22 + .../implementation/blocks/CharBlocks.java | 17 + .../implementation/blocks/CharSetBlocks.java | 24 +- .../implementation/blocks/NullChunkGet.java | 17 + .../blocks/ThreadUnsafeCharBlocks.java | 22 +- .../implementation/chunk/ChunkHolder.java | 6 + .../queue/implementation/chunk/NullChunk.java | 6 + .../com/sk89q/worldedit/extent/Extent.java | 2 +- .../function/operation/ForwardExtentCopy.java | 106 ++++- 37 files changed, 1145 insertions(+), 65 deletions(-) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java index a325bb08bc..8251ee0e37 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java @@ -17,6 +17,7 @@ import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.BukkitEntity; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.util.LogManagerCompat; @@ -69,6 +70,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.AbstractCollection; +import java.util.AbstractSet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -88,6 +90,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; +import java.util.stream.Collectors; import static net.minecraft.core.registries.Registries.BIOME; @@ -145,11 +148,13 @@ public PaperweightGetBlocks(ServerLevel serverLevel, int chunkX, int chunkZ) { this.chunkPos = new IntPair(chunkX, chunkZ); } - public int getChunkX() { + @Override + public int getX() { return chunkX; } - public int getChunkZ() { + @Override + public int getZ() { return chunkZ; } @@ -368,8 +373,7 @@ public int[] getHeightMap(HeightMapType type) { @Override public Collection entities() { - ensureLoaded(serverLevel, chunkX, chunkZ); - List entities = PaperweightPlatformAdapter.getEntities(getChunk()); + List entities = PaperweightPlatformAdapter.getEntities(ensureLoaded(serverLevel, chunkX, chunkZ)); if (entities.isEmpty()) { return Collections.emptyList(); } @@ -413,6 +417,51 @@ public Iterator iterator() { }; } + @Override + public Set getFullEntities() { + List entities = PaperweightPlatformAdapter.getEntities(ensureLoaded(serverLevel, chunkX, chunkZ)); + if (entities.isEmpty()) { + return Collections.emptySet(); + } + int size = entities.size(); + return new AbstractSet<>() { + @Override + public int size() { + return size; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object get) { + if (!(get instanceof com.sk89q.worldedit.entity.Entity e)) { + return false; + } + UUID getUUID = e.getState().getNbtData().getUUID(); + for (Entity entity : entities) { + UUID uuid = entity.getUUID(); + if (uuid.equals(getUUID)) { + return true; + } + } + return false; + } + + @Nonnull + @Override + public Iterator iterator() { + Iterable result = entities + .stream() + .map(input -> new BukkitEntity(input.getBukkitEntity())) + .collect(Collectors.toList()); + return result.iterator(); + } + }; + } + private void removeEntity(Entity entity) { entity.discard(); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks_Copy.java index e655dd206c..07528c438a 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks_Copy.java @@ -46,6 +46,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { private final char[][] blocks; private final int minHeight; private final int maxHeight; + private final int chunkX; + private final int chunkZ; final ServerLevel serverLevel; final LevelChunk levelChunk; private Holder[][] biomes = null; @@ -56,6 +58,8 @@ protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) { this.minHeight = serverLevel.getMinBuildHeight(); this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. this.blocks = new char[getSectionCount()][]; + this.chunkX = levelChunk.locX; + this.chunkZ = levelChunk.locZ; } protected void storeTile(BlockEntity blockEntity) { @@ -94,6 +98,11 @@ public Collection entities() { return null; } + @Override + public Set getFullEntities() { + throw new UnsupportedOperationException("Cannot get full entities from GET copy."); + } + @Override public boolean isCreateCopy() { return false; @@ -136,6 +145,16 @@ public int getMinSectionPosition() { return minHeight >> 4; } + @Override + public int getX() { + return chunkX; + } + + @Override + public int getZ() { + return chunkZ; + } + @Override public BiomeType getBiomeType(int x, int y, int z) { Holder biome = biomes[(y >> 4) - getMinSectionPosition()][(y & 12) << 2 | (z & 12) | (x & 12) >> 2]; diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPostProcessor.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPostProcessor.java index 5fafdc4398..aff4f8fe79 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPostProcessor.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPostProcessor.java @@ -112,7 +112,7 @@ public Extent construct(final Extent child) { @Override public ProcessorScope getScope() { - return ProcessorScope.READING_SET_BLOCKS; + return ProcessorScope.READING_BLOCKS; } private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) { diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java index b542b3e087..a04f7d2d49 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java @@ -17,6 +17,7 @@ import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.BukkitEntity; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.util.LogManagerCompat; @@ -69,6 +70,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.AbstractCollection; +import java.util.AbstractSet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -88,6 +90,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; +import java.util.stream.Collectors; import static net.minecraft.core.registries.Registries.BIOME; @@ -145,11 +148,13 @@ public PaperweightGetBlocks(ServerLevel serverLevel, int chunkX, int chunkZ) { this.chunkPos = new IntPair(chunkX, chunkZ); } - public int getChunkX() { + @Override + public int getX() { return chunkX; } - public int getChunkZ() { + @Override + public int getZ() { return chunkZ; } @@ -368,8 +373,7 @@ public int[] getHeightMap(HeightMapType type) { @Override public Collection entities() { - ensureLoaded(serverLevel, chunkX, chunkZ); - List entities = PaperweightPlatformAdapter.getEntities(getChunk()); + List entities = PaperweightPlatformAdapter.getEntities(ensureLoaded(serverLevel, chunkX, chunkZ)); if (entities.isEmpty()) { return Collections.emptyList(); } @@ -413,6 +417,51 @@ public Iterator iterator() { }; } + @Override + public Set getFullEntities() { + List entities = PaperweightPlatformAdapter.getEntities(ensureLoaded(serverLevel, chunkX, chunkZ)); + if (entities.isEmpty()) { + return Collections.emptySet(); + } + int size = entities.size(); + return new AbstractSet<>() { + @Override + public int size() { + return size; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object get) { + if (!(get instanceof com.sk89q.worldedit.entity.Entity e)) { + return false; + } + UUID getUUID = e.getState().getNbtData().getUUID(); + for (Entity entity : entities) { + UUID uuid = entity.getUUID(); + if (uuid.equals(getUUID)) { + return true; + } + } + return false; + } + + @Nonnull + @Override + public Iterator iterator() { + Iterable result = entities + .stream() + .map(input -> new BukkitEntity(input.getBukkitEntity())) + .collect(Collectors.toList()); + return result.iterator(); + } + }; + } + private void removeEntity(Entity entity) { entity.discard(); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java index 1163c2d331..b7f5362417 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java @@ -46,6 +46,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { private final char[][] blocks; private final int minHeight; private final int maxHeight; + private final int chunkX; + private final int chunkZ; final ServerLevel serverLevel; final LevelChunk levelChunk; private Holder[][] biomes = null; @@ -56,6 +58,8 @@ protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) { this.minHeight = serverLevel.getMinBuildHeight(); this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. this.blocks = new char[getSectionCount()][]; + this.chunkX = levelChunk.locX; + this.chunkZ = levelChunk.locZ; } protected void storeTile(BlockEntity blockEntity) { @@ -94,6 +98,11 @@ public Collection entities() { return null; } + @Override + public Set getFullEntities() { + throw new UnsupportedOperationException("Cannot get full entities from GET copy."); + } + @Override public boolean isCreateCopy() { return false; @@ -136,6 +145,16 @@ public int getMinSectionPosition() { return minHeight >> 4; } + @Override + public int getX() { + return chunkX; + } + + @Override + public int getZ() { + return chunkZ; + } + @Override public BiomeType getBiomeType(int x, int y, int z) { Holder biome = biomes[(y >> 4) - getMinSectionPosition()][(y & 12) << 2 | (z & 12) | (x & 12) >> 2]; diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPostProcessor.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPostProcessor.java index cfd9e27536..6ed321a8d7 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPostProcessor.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPostProcessor.java @@ -112,7 +112,7 @@ public Extent construct(final Extent child) { @Override public ProcessorScope getScope() { - return ProcessorScope.READING_SET_BLOCKS; + return ProcessorScope.READING_BLOCKS; } private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) { diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java index ff05c453d4..453801190c 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java @@ -17,6 +17,7 @@ import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.BukkitEntity; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.util.LogManagerCompat; @@ -70,6 +71,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.AbstractCollection; +import java.util.AbstractSet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -89,6 +91,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; +import java.util.stream.Collectors; import static net.minecraft.core.registries.Registries.BIOME; @@ -146,11 +149,13 @@ public PaperweightGetBlocks(ServerLevel serverLevel, int chunkX, int chunkZ) { this.chunkPos = new IntPair(chunkX, chunkZ); } - public int getChunkX() { + @Override + public int getX() { return chunkX; } - public int getChunkZ() { + @Override + public int getZ() { return chunkZ; } @@ -369,8 +374,7 @@ public int[] getHeightMap(HeightMapType type) { @Override public Collection entities() { - ensureLoaded(serverLevel, chunkX, chunkZ); - List entities = PaperweightPlatformAdapter.getEntities(getChunk()); + List entities = PaperweightPlatformAdapter.getEntities(ensureLoaded(serverLevel, chunkX, chunkZ)); if (entities.isEmpty()) { return Collections.emptyList(); } @@ -414,6 +418,51 @@ public Iterator iterator() { }; } + @Override + public Set getFullEntities() { + List entities = PaperweightPlatformAdapter.getEntities(ensureLoaded(serverLevel, chunkX, chunkZ)); + if (entities.isEmpty()) { + return Collections.emptySet(); + } + int size = entities.size(); + return new AbstractSet<>() { + @Override + public int size() { + return size; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object get) { + if (!(get instanceof com.sk89q.worldedit.entity.Entity e)) { + return false; + } + UUID getUUID = e.getState().getNbtData().getUUID(); + for (Entity entity : entities) { + UUID uuid = entity.getUUID(); + if (uuid.equals(getUUID)) { + return true; + } + } + return false; + } + + @Nonnull + @Override + public Iterator iterator() { + Iterable result = entities + .stream() + .map(input -> new BukkitEntity(input.getBukkitEntity())) + .collect(Collectors.toList()); + return result.iterator(); + } + }; + } + private void removeEntity(Entity entity) { entity.discard(); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks_Copy.java index 7d199c7f63..8504c4fc2c 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks_Copy.java @@ -47,6 +47,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { private final char[][] blocks; private final int minHeight; private final int maxHeight; + private final int chunkX; + private final int chunkZ; final ServerLevel serverLevel; final LevelChunk levelChunk; private Holder[][] biomes = null; @@ -57,6 +59,8 @@ protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) { this.minHeight = serverLevel.getMinBuildHeight(); this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. this.blocks = new char[getSectionCount()][]; + this.chunkX = levelChunk.locX; + this.chunkZ = levelChunk.locZ; } protected void storeTile(BlockEntity blockEntity) { @@ -97,6 +101,11 @@ public Collection entities() { return null; } + @Override + public Set getFullEntities() { + throw new UnsupportedOperationException("Cannot get full entities from GET copy."); + } + @Override public boolean isCreateCopy() { return false; @@ -140,6 +149,16 @@ public int getMinSectionPosition() { } @Override + public int getX() { + return chunkX; + } + + @Override + public int getZ() { + return chunkZ; + } + + @Override public BiomeType getBiomeType(int x, int y, int z) { Holder biome = biomes[(y >> 4) - getMinSectionPosition()][(y & 12) << 2 | (z & 12) | (x & 12) >> 2]; return PaperweightPlatformAdapter.adapt(biome, serverLevel); diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPostProcessor.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPostProcessor.java index bc094a916f..301ed8912b 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPostProcessor.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPostProcessor.java @@ -112,7 +112,7 @@ public Extent construct(final Extent child) { @Override public ProcessorScope getScope() { - return ProcessorScope.READING_SET_BLOCKS; + return ProcessorScope.READING_BLOCKS; } private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) { diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java index 70e7a4f22f..095b17b63e 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java @@ -17,6 +17,7 @@ import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.BukkitEntity; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.util.LogManagerCompat; @@ -70,6 +71,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.AbstractCollection; +import java.util.AbstractSet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -89,6 +91,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; +import java.util.stream.Collectors; import static net.minecraft.core.registries.Registries.BIOME; @@ -146,11 +149,13 @@ public PaperweightGetBlocks(ServerLevel serverLevel, int chunkX, int chunkZ) { this.chunkPos = new IntPair(chunkX, chunkZ); } - public int getChunkX() { + @Override + public int getX() { return chunkX; } - public int getChunkZ() { + @Override + public int getZ() { return chunkZ; } @@ -369,8 +374,7 @@ public int[] getHeightMap(HeightMapType type) { @Override public Collection entities() { - ensureLoaded(serverLevel, chunkX, chunkZ); - List entities = PaperweightPlatformAdapter.getEntities(getChunk()); + List entities = PaperweightPlatformAdapter.getEntities(ensureLoaded(serverLevel, chunkX, chunkZ)); if (entities.isEmpty()) { return Collections.emptyList(); } @@ -415,6 +419,53 @@ public Iterator iterator() { } + @Override + public Set getFullEntities() { + getSections(true); + getChunk(); + List entities = PaperweightPlatformAdapter.getEntities(ensureLoaded(serverLevel, chunkX, chunkZ)); + if (entities.isEmpty()) { + return Collections.emptySet(); + } + int size = entities.size(); + return new AbstractSet<>() { + @Override + public int size() { + return size; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object get) { + if (!(get instanceof com.sk89q.worldedit.entity.Entity e)) { + return false; + } + UUID getUUID = e.getState().getNbtData().getUUID(); + for (Entity entity : entities) { + UUID uuid = entity.getUUID(); + if (uuid.equals(getUUID)) { + return true; + } + } + return false; + } + + @Nonnull + @Override + public Iterator iterator() { + Iterable result = entities + .stream() + .map(input -> new BukkitEntity(input.getBukkitEntity())) + .collect(Collectors.toList()); + return result.iterator(); + } + }; + } + private void removeEntity(Entity entity) { entity.discard(); } diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks_Copy.java index 18b557b977..479b8f50ad 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks_Copy.java @@ -47,6 +47,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { private final char[][] blocks; private final int minHeight; private final int maxHeight; + private final int chunkX; + private final int chunkZ; final ServerLevel serverLevel; final LevelChunk levelChunk; private Holder[][] biomes = null; @@ -57,6 +59,8 @@ protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) { this.minHeight = serverLevel.getMinBuildHeight(); this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. this.blocks = new char[getSectionCount()][]; + this.chunkX = levelChunk.locX; + this.chunkZ = levelChunk.locZ; } protected void storeTile(BlockEntity blockEntity) { @@ -97,6 +101,11 @@ public Collection entities() { return null; } + @Override + public Set getFullEntities() { + throw new UnsupportedOperationException("Cannot get full entities from GET copy."); + } + @Override public boolean isCreateCopy() { return false; @@ -139,6 +148,16 @@ public int getMinSectionPosition() { return minHeight >> 4; } + @Override + public int getX() { + return chunkX; + } + + @Override + public int getZ() { + return chunkZ; + } + @Override public BiomeType getBiomeType(int x, int y, int z) { Holder biome = biomes[(y >> 4) - getMinSectionPosition()][(y & 12) << 2 | (z & 12) | (x & 12) >> 2]; diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPostProcessor.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPostProcessor.java index 3b4c47087e..9b1e4f3b6d 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPostProcessor.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPostProcessor.java @@ -112,7 +112,7 @@ public Extent construct(final Extent child) { @Override public ProcessorScope getScope() { - return ProcessorScope.READING_SET_BLOCKS; + return ProcessorScope.READING_BLOCKS; } private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) { 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 60ec9311a3..1be6bfb4bf 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 @@ -665,6 +665,13 @@ public static class EXPERIMENTAL { }) public boolean REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL = true; + @Comment({ + "[SAFE] Perform operations involving entities on chunk load", + " - Allows entities that might not otherwise be captured due to unloaded chunks to be captured", + " - Main use-case is copying larger areas with entities" + }) + public boolean IMPROVED_ENTITY_EDITS = true; + @Comment({ "Increased debug logging for brush actions and processor setup" }) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java new file mode 100644 index 0000000000..cd2d68f9a6 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java @@ -0,0 +1,171 @@ +package com.fastasyncworldedit.core.extent; + +import com.fastasyncworldedit.core.extent.processor.ProcessorScope; +import com.fastasyncworldedit.core.math.LocalBlockVector2Set; +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.fastasyncworldedit.core.queue.IQueueChunk; +import com.fastasyncworldedit.core.queue.IQueueExtent; +import com.fastasyncworldedit.core.util.ExtentTraverser; +import com.sk89q.worldedit.extent.AbstractDelegateExtent; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Consumer; + +/** + * Extent/processor that runs a t + */ +public class OncePerChunkExtent extends AbstractDelegateExtent implements IBatchProcessor { + + private final LocalBlockVector2Set set = new LocalBlockVector2Set(); + private final IQueueExtent queue; + private final Consumer task; + private volatile long lastPair = Long.MAX_VALUE; + private volatile boolean isProcessing; + + /** + * Create a new instance. + * + * @param extent the extent + */ + public OncePerChunkExtent(Extent extent, IQueueExtent queue, Consumer task) { + super(extent); + this.queue = queue; + this.task = task; + } + + private boolean shouldRun(int chunkX, int chunkZ) { + final long pair = (long) chunkX << 32 | chunkZ & 0xffffffffL; + if (pair == lastPair) { + return false; + } + lastPair = pair; + synchronized (set) { + if (!set.contains(chunkX, chunkZ)) { + set.add(chunkX, chunkZ); + return true; + } + } + return false; + } + + private void checkAndRun(int chunkX, int chunkZ) { + if (!isProcessing && shouldRun(chunkX, chunkZ)) { + task.accept(queue.getCachedGet(chunkX, chunkZ)); + } + } + + @Override + public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { + return set; + } + + @Override + public IChunkGet processGet(final IChunkGet get) { + isProcessing = true; + if (shouldRun(get.getX(), get.getZ())) { + task.accept(get); + } + return get; + } + + @Nullable + @Override + public Extent construct(final Extent child) { + if (getExtent() != child) { + new ExtentTraverser(this).setNext(child); + } + return this; + } + + @Override + public ProcessorScope getScope() { + return ProcessorScope.READING_BLOCKS; + } + + @Override + public BlockState getBlock(final BlockVector3 position) { + checkAndRun(position.getBlockX() >> 4, position.getBlockZ() >> 4); + return super.getBlock(position); + } + + @Override + public BlockState getBlock(final int x, final int y, final int z) { + checkAndRun(x >> 4, z >> 4); + return super.getBlock(x, y, z); + } + + @Override + public BaseBlock getFullBlock(final BlockVector3 position) { + checkAndRun(position.getBlockX() >> 4, position.getBlockZ() >> 4); + return super.getFullBlock(position); + } + + @Override + public BaseBlock getFullBlock(final int x, final int y, final int z) { + checkAndRun(x >> 4, z >> 4); + return super.getFullBlock(x, y, z); + } + + @Override + public BiomeType getBiomeType(final int x, final int y, final int z) { + checkAndRun(x >> 4, z >> 4); + return super.getBiomeType(x, y, z); + } + + @Override + public BiomeType getBiome(final BlockVector3 position) { + checkAndRun(position.getBlockX() >> 4, position.getBlockZ() >> 4); + return super.getBiome(position); + } + + @Override + public int getEmittedLight(final int x, final int y, final int z) { + checkAndRun(x >> 4, z >> 4); + return super.getEmittedLight(x, y, z); + } + + @Override + public int getSkyLight(final int x, final int y, final int z) { + checkAndRun(x >> 4, z >> 4); + return super.getSkyLight(x, y, z); + } + + @Override + public int getBrightness(final int x, final int y, final int z) { + checkAndRun(x >> 4, z >> 4); + return super.getBrightness(x, y, z); + } + + @Override + public boolean setBiome(final int x, final int y, final int z, final BiomeType biome) { + checkAndRun(x >> 4, z >> 4); + return super.setBiome(x, y, z, biome); + } + + @Override + public boolean setBiome(final BlockVector3 position, final BiomeType biome) { + checkAndRun(position.getBlockX() >> 4, position.getBlockZ() >> 4); + return super.setBiome(position, biome); + } + + @Override + public void setBlockLight(final int x, final int y, final int z, final int value) { + checkAndRun(x >> 4, z >> 4); + super.setBlockLight(x, y, z, value); + } + + @Override + public void setSkyLight(final int x, final int y, final int z, final int value) { + checkAndRun(x >> 4, z >> 4); + super.setSkyLight(x, y, z, value); + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/BatchProcessorHolder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/BatchProcessorHolder.java index 540e933b0a..221117260e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/BatchProcessorHolder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/BatchProcessorHolder.java @@ -37,6 +37,16 @@ public void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) { getPostProcessor().postProcess(chunk, get, set); } + @Override + public boolean processGet(final int chunkX, final int chunkZ) { + return getProcessor().processGet(chunkX, chunkZ); + } + + @Override + public IChunkGet processGet(final IChunkGet get) { + return getProcessor().processGet(get); + } + @Override public void flush() { getProcessor().flush(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/MultiBatchProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/MultiBatchProcessor.java index c266fd5441..4d42feb0ed 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/MultiBatchProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/MultiBatchProcessor.java @@ -121,7 +121,7 @@ public Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { for (IBatchProcessor processor : processors) { try { // We do NOT want to edit blocks in post processing - if (processor.getScope() != ProcessorScope.READING_SET_BLOCKS) { + if (processor.getScope() != ProcessorScope.READING_BLOCKS) { continue; } futures.add(processor.postProcessSet(chunk, get, set)); @@ -152,7 +152,7 @@ public void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) { for (IBatchProcessor processor : processors) { try { // We do NOT want to edit blocks in post processing - if (processor.getScope() != ProcessorScope.READING_SET_BLOCKS) { + if (processor.getScope() != ProcessorScope.READING_BLOCKS) { continue; } processor.postProcess(chunk, get, set); @@ -187,6 +187,14 @@ public boolean processGet(int chunkX, int chunkZ) { return true; } + @Override + public IChunkGet processGet(IChunkGet get) { + for (IBatchProcessor processor : this.processors) { + get = processor.processGet(get); + } + return get; + } + @Override public Extent construct(Extent child) { for (IBatchProcessor processor : processors) { 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 503351fb70..6e97800aa6 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,7 @@ * - 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. + * - READING_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 { @@ -15,6 +15,11 @@ public enum ProcessorScope { CHANGING_BLOCKS(1), REMOVING_BLOCKS(2), CUSTOM(3), + READING_BLOCKS(5), + /** + * @deprecated use {@link ProcessorScope#READING_BLOCKS} + */ + @Deprecated(forRemoval = true, since = "TODO") READING_SET_BLOCKS(4); private final int value; @@ -28,18 +33,13 @@ public int intValue() { } public static ProcessorScope valueOf(int value) { - switch (value) { - case 0: - return ProcessorScope.ADDING_BLOCKS; - case 1: - return ProcessorScope.CHANGING_BLOCKS; - case 2: - return ProcessorScope.REMOVING_BLOCKS; - case 4: - return ProcessorScope.READING_SET_BLOCKS; - case 3: - default: - return ProcessorScope.CUSTOM; - } + return switch (value) { + case 0 -> ProcessorScope.ADDING_BLOCKS; + case 1 -> ProcessorScope.CHANGING_BLOCKS; + case 2 -> ProcessorScope.REMOVING_BLOCKS; + case 4 -> ProcessorScope.READING_SET_BLOCKS; + case 5 -> ProcessorScope.READING_BLOCKS; + default -> ProcessorScope.CUSTOM; + }; } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightmapProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightmapProcessor.java index 5d8f81e39d..dec8f46c03 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightmapProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightmapProcessor.java @@ -142,7 +142,7 @@ public Extent construct(Extent child) { @Override public ProcessorScope getScope() { - return ProcessorScope.READING_SET_BLOCKS; + return ProcessorScope.READING_BLOCKS; } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/RelightProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/RelightProcessor.java index 6a9f16dee3..6951ab4e09 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/RelightProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/RelightProcessor.java @@ -9,8 +9,6 @@ import com.sk89q.worldedit.extent.Extent; import javax.annotation.Nullable; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Future; public class RelightProcessor implements IBatchProcessor { @@ -54,7 +52,7 @@ Extent construct(Extent child) { @Override public ProcessorScope getScope() { - return ProcessorScope.READING_SET_BLOCKS; + return ProcessorScope.READING_BLOCKS; } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java index 33931aac62..faf074e057 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java @@ -239,7 +239,7 @@ public Future postProcessSet(final IChunk chunk, final IChunkGet get, final I @Override public ProcessorScope getScope() { - return ProcessorScope.READING_SET_BLOCKS; + return ProcessorScope.READING_BLOCKS; } @Deprecated(forRemoval = true, since = "2.11.2") diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java new file mode 100644 index 0000000000..7937f78136 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java @@ -0,0 +1,382 @@ +package com.fastasyncworldedit.core.math; + +import com.fastasyncworldedit.core.util.MathMan; +import com.sk89q.worldedit.math.BlockVector2; +import com.zaxxer.sparsebits.SparseBitSet; + +import javax.annotation.Nonnull; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +/** + * The LocalBlockVectorSet is a Memory and CPU optimized Set for storing BlockVectors which are all in a local region + * - All vectors must be in a 2048 * 512 * 2048 area centered around the first entry + * - This will use 8 bytes for every 64 BlockVectors (about 800x less than a HashSet) + */ +public class LocalBlockVector2Set implements Set { + + private final SparseBitSet set; + private int offsetX; + private int offsetZ; + + /** + * New LocalBlockVectorSet that will set the offset x and z to the first value given. The y offset will default to 128 to + * allow -64 -> 320 world height. + */ + public LocalBlockVector2Set() { + offsetX = offsetZ = Integer.MAX_VALUE; + this.set = new SparseBitSet(); + } + + /** + * New LocalBlockVectorSet with a given offset. Defaults y offset to 128. + * + * @param x x offset + * @param z z offset + */ + public LocalBlockVector2Set(int x, int z) { + this.offsetX = x; + this.offsetZ = z; + this.set = new SparseBitSet(); + } + + private LocalBlockVector2Set(int x, int z, SparseBitSet set) { + this.offsetX = x; + this.offsetZ = z; + this.set = set; + } + + @Override + public int size() { + return set.cardinality(); + } + + @Override + public boolean isEmpty() { + return set.isEmpty(); + } + + /** + * If the set contains a position + * + * @param x x position + * @param z z position + * @return if the set contains the position + */ + public boolean contains(int x, int z) { + if (offsetX == Integer.MAX_VALUE) { + return false; + } + short sx = (short) (x - offsetX); + short sz = (short) (z - offsetZ); + if (sx > 32767 || sx < -32768 || sz > 32767 || sz < -32768) { + return false; + } + return set.get(MathMan.pairSearchCoords(sx, sz)); + } + + @Override + public boolean contains(Object o) { + if (o instanceof BlockVector2 v) { + return contains(v.getBlockX(), v.getBlockZ()); + } + return false; + } + + @SuppressWarnings("MethodDoesntCallSuperMethod") + @Override + public LocalBlockVector2Set clone() { + return new LocalBlockVector2Set(offsetX, offsetZ, set.clone()); + } + + /** + * If a radius is contained by the set + * + * @param x x radius center + * @param z z radius center + * @return if radius is contained by the set + */ + public boolean containsRadius(int x, int z, int radius) { + if (radius <= 0) { + return contains(x, z); + } + int length = radius * 2; + if (size() < length * length * length) { + int index = -1; + while ((index = set.nextSetBit(index + 1)) != -1) { + int ix = offsetX + MathMan.unpairSearchCoordsX(index); + int iz = offsetZ + MathMan.unpairSearchCoordsY(index); + if (Math.abs(ix - x) <= radius && Math.abs(iz - z) <= radius) { + return true; + } + } + return false; + } + for (int xx = -radius; xx <= radius; xx++) { + for (int zz = -radius; zz <= radius; zz++) { + if (contains(x + xx, z + zz)) { + return true; + } + } + } + return false; + } + + /** + * Set the offset applied to values when storing and reading to keep the values within -1024 to 1023. Uses default y offset + * of 128 to allow -64 -> 320 world height use. + * + * @param x x offset + * @param z z offset + */ + public void setOffset(int x, int z) { + this.offsetX = x; + this.offsetZ = z; + } + + protected MutableBlockVector2 getIndex(int getIndex) { + int size = size(); + if (getIndex > size) { + return null; + } + int index = -1; + for (int i = 0; i <= getIndex; i++) { + index = set.nextSetBit(index + 1); + } + if (index != -1) { + int x = offsetX + MathMan.unpairSearchCoordsX(index); + int z = offsetZ + MathMan.unpairSearchCoordsY(index); + return MutableBlockVector2.get(x, z); + } + return null; + } + + @Nonnull + @Override + public Iterator iterator() { + return new Iterator<>() { + final MutableBlockVector2 mutable = new MutableBlockVector2(0, 0); + int index = set.nextSetBit(0); + int previous = -1; + + @Override + public void remove() { + set.clear(previous); + } + + @Override + public boolean hasNext() { + return index != -1; + } + + @Override + public BlockVector2 next() { + if (index != -1) { + int x = offsetX + MathMan.unpairSearchCoordsX(index); + int z = offsetZ + MathMan.unpairSearchCoordsY(index); + mutable.mutX(x); + mutable.mutZ(z); + previous = index; + index = set.nextSetBit(index + 1); + return mutable; + } + return null; + } + }; + } + + @Nonnull + @Override + public BlockVector2[] toArray() { + return toArray(new BlockVector2[0]); + } + + @SuppressWarnings("unchecked") + @Nonnull + @Override + public T[] toArray(T[] array) { + int size = size(); + if (array.length < size) { + array = Arrays.copyOf(array, size); + } else if (array.length > size) { + array[size] = null; // mark as end to comply with the method contract + } + int index = 0; + for (int i = 0; i < size; i++) { + int x = offsetX + MathMan.unpairSearchCoordsX(index); + int z = offsetZ + MathMan.unpairSearchCoordsY(index); + array[i] = (T) BlockVector2.at(x, z); + index++; + } + return array; + } + + /** + * If a position is contained by the bounds of the set + * + * @param x x position + * @param z z position + * @return true if position is contained by the bounds of the set + */ + public boolean canAdd(int x, int z) { + if (offsetX == Integer.MAX_VALUE) { + return false; + } + int relX = x - offsetX; + int relZ = z - offsetZ; + return relX <= 32767 && relX >= -32768 && relZ <= 32727 && relZ >= -32768; + } + + /** + * Add a position to the set if not present + * + * @param x x position + * @param z z position + * @return true if not already present + */ + public boolean add(int x, int z) { + if (offsetX == Integer.MAX_VALUE) { + offsetX = x; + offsetZ = z; + } + int relX = x - offsetX; + int relZ = z - offsetZ; + if (relX > 32767 || relX < -32768 || relZ > 32767 || relZ < -32768) { + throw new UnsupportedOperationException( + "LocalBlockVector2Set can only contain vectors within 32768 blocks (cuboid) of the first entry. Attempted " + "to set block at " + x + ", " + z + ". With origin " + offsetX + " " + offsetZ); + } + int index = getIndex(x, z); + if (set.get(index)) { + return false; + } else { + set.set(index); + return true; + } + } + + /** + * Add a position to the set if not present + * + * @param vector position + * @return true if not already present + */ + @Override + public boolean add(BlockVector2 vector) { + return add(vector.getBlockX(), vector.getBlockZ()); + } + + private int getIndex(BlockVector2 vector) { + return getIndex(vector.getX(), vector.getZ()); + } + + private int getIndex(int x, int z) { + return MathMan.pairSearchCoords((short) (x - offsetX), (short) (z - offsetZ)); + } + + /** + * Remove a position from the set. + * + * @param x x position + * @param z z position + * @return true if value was present. + */ + public boolean remove(int x, int z) { + int relX = x - offsetX; + int relZ = z - offsetZ; + if (relX > 1023 || relX < -1024 || relZ > 1023 || relZ < -1024) { + return false; + } + int index = MathMan.pairSearchCoords((short) (x - offsetX), (short) (z - offsetZ)); + boolean value = set.get(index); + set.clear(index); + return value; + } + + @Override + public boolean remove(Object o) { + if (o instanceof BlockVector2 v) { + return remove(v.getBlockX(), v.getBlockZ()); + } + return false; + } + + @Override + public boolean containsAll(Collection c) { + for (Object o : c) { + if (!contains(o)) { + return false; + } + } + return true; + } + + @Override + public boolean addAll(Collection c) { + boolean result = false; + for (BlockVector2 v : c) { + result |= add(v); + } + return result; + } + + @Override + public boolean retainAll(@Nonnull Collection c) { + boolean result = false; + int size = size(); + int index = -1; + MutableBlockVector2 mVec = MutableBlockVector2.get(0, 0); + for (int i = 0; i < size; i++) { + index = set.nextSetBit(index + 1); + int x = offsetX + MathMan.unpairSearchCoordsX(index); + int z = offsetZ + MathMan.unpairSearchCoordsY(index); + mVec.mutX(x); + mVec.mutZ(z); + if (!c.contains(mVec)) { + result = true; + set.clear(index); + } + } + return result; + } + + @Override + public boolean removeAll(Collection c) { + boolean result = false; + for (Object o : c) { + result |= remove(o); + } + return result; + } + + /** + * Visit each point contained in the set + * + * @param visitor visitor to use + */ + public void forEach(BlockVector2SetVisitor visitor) { + int size = size(); + int index = -1; + for (int i = 0; i < size; i++) { + index = set.nextSetBit(index + 1); + int x = offsetX + MathMan.unpairSearchCoordsX(index); + int z = offsetZ + MathMan.unpairSearchCoordsY(index); + visitor.run(x, z, index); + } + } + + @Override + public void clear() { + offsetZ = Integer.MAX_VALUE; + offsetX = Integer.MAX_VALUE; + set.clear(); + } + + public interface BlockVector2SetVisitor { + + void run(int x, int z, int index); + + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVectorSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVectorSet.java index 72287b0a1e..e0c9b541ab 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVectorSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVectorSet.java @@ -213,8 +213,8 @@ public BlockVector3 next() { @Nonnull @Override - public Object[] toArray() { - return toArray((Object[]) null); + public BlockVector3[] toArray() { + return toArray(new BlockVector3[0]); } @SuppressWarnings("unchecked") diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/MutableBlockVector2.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/MutableBlockVector2.java index 0d755eec6f..c6decdaff0 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/MutableBlockVector2.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/MutableBlockVector2.java @@ -48,4 +48,13 @@ public MutableBlockVector2 mutZ(int z) { return this; } + /** + * Create a new {@link BlockVector2} with the current x and z. + * + * @since TODO + */ + public BlockVector2 toImmutable() { + return BlockVector2.at(x, z); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/Filter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/Filter.java index f308d4806c..4c89514cfc 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/Filter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/Filter.java @@ -17,10 +17,7 @@ public interface Filter { // * @param chunkX the x coordinate in the chunk // * @param chunkZ the z coordinate in the chunk // */ -// default boolean appliesChunk( -// int chunkX, -// int chunkZ -// ) { +// default boolean appliesChunk(int chunkX, int chunkZ) { // return true; // } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java index faec2f9f51..30f3fd6ba4 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java @@ -50,7 +50,18 @@ default boolean processGet(int chunkX, int chunkZ) { } /** - * Convert this processor into an Extent based processor instead of a queue batch based one. + * Process a chunk GET. Method typically only called when a chunk is loaded into memory (miss from + * {@link com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent cache}). + * + * @param get GET chunk loaded + * @return processed get chunk + */ + default IChunkGet processGet(IChunkGet get) { + return get; + } + + /** + * Convert this processor into an Extent based processor instead of a queue batch based on. */ @Nullable Extent construct(Extent child); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java index bda12dbe31..9664d4324f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java @@ -24,7 +24,7 @@ import java.util.stream.IntStream; /** - * A shared interface for IGetBlocks and ISetBlocks. + * A shared interface for IGetBlocks and ISetBlocks. Represents a chunk. */ public interface IBlocks extends Trimable { @@ -124,6 +124,16 @@ default int getBitMask() { */ int getMinSectionPosition(); + /** + * Get the chunk x coordinate + */ + int getX(); + + /** + * Get the chunk z coordinate + */ + int getZ(); + default byte[] toByteArray(boolean full, boolean stretched) { return toByteArray(null, getBitMask(), full, stretched); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java index b726900299..291899844d 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java @@ -3,6 +3,7 @@ import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.InputExtent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; @@ -10,6 +11,7 @@ import com.sk89q.worldedit.world.block.BlockState; import javax.annotation.Nullable; +import java.util.Set; import java.util.UUID; import java.util.concurrent.Future; @@ -68,6 +70,13 @@ default CompoundTag getTile(int x, int y, int z) { return IBlocks.super.getTile(x, y, z); } + /** + * Get the entities in the chunk as "full" entities. + * + * @since TODO; + */ + Set getFullEntities(); + boolean isCreateCopy(); /** diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java index b0239a5a3c..bf4db8099f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java @@ -92,7 +92,7 @@ public void disableQueue() { @Override public IChunkGet getCachedGet(int chunkX, int chunkZ) { - return cacheGet.get(chunkX, chunkZ); + return processGet(cacheGet.get(chunkX, chunkZ)); } @Override @@ -171,7 +171,7 @@ public synchronized void init(Extent extent, IChunkCache get, IChunkC }; } if (set == null) { - set = (x, z) -> CharSetBlocks.newInstance(); + set = (x, z) -> CharSetBlocks.newInstance(x, z); } this.cacheGet = get; this.cacheSet = set; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/BitSetBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/BitSetBlocks.java index 76f2191ed6..f8e4889aca 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/BitSetBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/BitSetBlocks.java @@ -26,13 +26,25 @@ public class BitSetBlocks implements IChunkSet { private final int minSectionPosition; private final int maxSectionPosition; private final int layers; + private final int chunkX; + private final int chunkZ; + /** + * @deprecated use {@link BitSetBlocks#BitSetBlocks(BlockState, int, int, int, int)} + */ + @Deprecated(forRemoval = true, since = "TODO") public BitSetBlocks(BlockState blockState, int minSectionPosition, int maxSectionPosition) { + this(blockState, minSectionPosition, maxSectionPosition, 0, 0); + } + + public BitSetBlocks(BlockState blockState, int minSectionPosition, int maxSectionPosition, int chunkX, int chunkZ) { this.row = new MemBlockSet.RowZ(minSectionPosition, maxSectionPosition); this.blockState = blockState; this.minSectionPosition = minSectionPosition; this.maxSectionPosition = maxSectionPosition; this.layers = maxSectionPosition - minSectionPosition + 1; + this.chunkX = chunkX; + this.chunkZ = chunkZ; } @Override @@ -227,6 +239,16 @@ public int getMinSectionPosition() { return maxSectionPosition; } + @Override + public int getX() { + return chunkX; + } + + @Override + public int getZ() { + return chunkZ; + } + @Override public boolean trim(boolean aggressive) { return false; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java index c338033f0a..73140c09c7 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java @@ -71,6 +71,8 @@ public boolean isFull() { protected int minSectionPosition; protected int maxSectionPosition; protected int sectionCount; + private int chunkX; + private int chunkZ; /** * New instance given initial min/max section indices. Can be negative. @@ -88,6 +90,11 @@ public CharBlocks(int minSectionPosition, int maxSectionPosition) { } } + public void init(int chunkX, int chunkZ) { + this.chunkX = chunkX; + this.chunkZ = chunkZ; + } + @Override public synchronized boolean trim(boolean aggressive) { boolean result = true; @@ -194,6 +201,16 @@ public char get(int x, int y, int z) { return get(layer, index); } + @Override + public int getX() { + return chunkX; + } + + @Override + public int getZ() { + return chunkZ; + } + /** * Default char value to be used when "updating"/resetting data arrays */ diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java index 8d7c059edf..87e6aa500d 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java @@ -27,14 +27,30 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { private static final Pool POOL = FaweCache.INSTANCE.registerPool( CharSetBlocks.class, - CharSetBlocks::new, - Settings.settings().QUEUE.POOL + CharSetBlocks::new, Settings.settings().QUEUE.POOL ); + /** + * @deprecated Use {@link CharSetBlocks#newInstance(int, int)} + */ + @Deprecated(forRemoval = true, since = "TODO") public static CharSetBlocks newInstance() { return POOL.poll(); } + /** + * Create a new {@link CharSetBlocks} instance + * + * @param x chunk x + * @param z chunk z + * @return New pooled CharSetBlocks instance. + */ + public static CharSetBlocks newInstance(int x, int z) { + CharSetBlocks set = POOL.poll(); + set.init(x, z); + return set; + } + public BiomeType[][] biomes; public char[][] light; public char[][] skyLight; @@ -374,7 +390,9 @@ public ThreadUnsafeCharBlocks createCopy() { heightMaps != null ? new EnumMap<>(heightMaps) : null, defaultOrdinal(), fastMode, - bitMask + bitMask, + getX(), + getZ() ); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java index 273f449523..ad20ed0c99 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java @@ -6,6 +6,7 @@ import com.fastasyncworldedit.core.queue.IBlocks; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; @@ -18,6 +19,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.concurrent.Future; @@ -63,6 +65,11 @@ public Collection entities() { return Collections.emptyList(); } + @Nullable + public Set getFullEntities() { + return null; + } + @Override public int setCreateCopy(boolean createCopy) { return -1; @@ -105,6 +112,16 @@ public int getMinSectionPosition() { return 0; } + @Override + public int getX() { + return 0; + } + + @Override + public int getZ() { + return 0; + } + public boolean trim(boolean aggressive) { return true; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java index c21e932a9d..9e5c4d3efd 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java @@ -38,6 +38,8 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { private static final Logger LOGGER = LogManagerCompat.getLogger(); private final char defaultOrdinal; + private final int chunkX; + private final int chunkZ; private char[][] blocks; private int minSectionPosition; private int maxSectionPosition; @@ -71,7 +73,9 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { Map heightMaps, char defaultOrdinal, boolean fastMode, - int bitMask + int bitMask, + int chunkX, + int chunkZ ) { this.blocks = blocks; this.minSectionPosition = minSectionPosition; @@ -87,6 +91,8 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { this.defaultOrdinal = defaultOrdinal; this.fastMode = fastMode; this.bitMask = bitMask; + this.chunkX = chunkX; + this.chunkZ = chunkZ; } @Override @@ -178,6 +184,16 @@ public int getMinSectionPosition() { return minSectionPosition; } + @Override + public int getX() { + return chunkX; + } + + @Override + public int getZ() { + return chunkZ; + } + public char get(int x, int y, int z) { int layer = (y >> 4); if (!hasSection(layer)) { @@ -480,7 +496,9 @@ public IChunkSet createCopy() { heightMaps != null ? new HashMap<>(heightMaps) : null, defaultOrdinal, fastMode, - bitMask + bitMask, + chunkX, + chunkZ ); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java index 7c36eb94cc..192793cf23 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java @@ -12,6 +12,7 @@ import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent; import com.fastasyncworldedit.core.util.MemUtil; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; @@ -1089,6 +1090,11 @@ public Collection entities() { return delegate.get(this).entities(); } + @Override + public Set getFullEntities() { + return delegate.get(this).getFullEntities(); + } + @Override public BaseBlock getFullBlock(int x, int y, int z) { return delegate.getFullBlock(this, x, y, z); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java index 14107e83a4..f430a5b349 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java @@ -6,6 +6,7 @@ import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IQueueChunk; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.biome.BiomeType; @@ -187,6 +188,11 @@ public int setCreateCopy(boolean createCopy) { return -1; } + @Override + public Set getFullEntities() { + return Collections.emptySet(); + } + @Override public boolean isCreateCopy() { return false; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java index 9f73c8f5e0..45f604f715 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java @@ -889,7 +889,7 @@ default Extent addProcessor(IBatchProcessor processor) { } default Extent addPostProcessor(IBatchProcessor processor) { - if (processor.getScope() != ProcessorScope.READING_SET_BLOCKS) { + if (processor.getScope() != ProcessorScope.READING_BLOCKS) { throw new IllegalArgumentException("You cannot alter blocks in a PostProcessor"); } return processor.construct(this); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java index f98d5f9241..5403b38074 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java @@ -20,21 +20,29 @@ package com.sk89q.worldedit.function.operation; import com.fastasyncworldedit.core.configuration.Caption; +import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.BlockTranslateExtent; +import com.fastasyncworldedit.core.extent.OncePerChunkExtent; import com.fastasyncworldedit.core.extent.PositionTransformExtent; +import com.fastasyncworldedit.core.extent.processor.ExtentBatchProcessorHolder; import com.fastasyncworldedit.core.function.RegionMaskTestFunction; import com.fastasyncworldedit.core.function.block.BiomeCopy; import com.fastasyncworldedit.core.function.block.CombinedBlockCopy; import com.fastasyncworldedit.core.function.block.SimpleBlockCopy; import com.fastasyncworldedit.core.function.visitor.IntersectRegionFunction; +import com.fastasyncworldedit.core.queue.IQueueChunk; +import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent; +import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent; import com.fastasyncworldedit.core.util.ExtentTraverser; import com.fastasyncworldedit.core.util.MaskTraverser; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.metadata.EntityProperties; +import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.CombinedRegionFunction; import com.sk89q.worldedit.function.RegionFunction; @@ -44,17 +52,24 @@ import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.function.visitor.EntityVisitor; import com.sk89q.worldedit.function.visitor.RegionVisitor; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.math.transform.Identity; import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.regions.FlatRegion; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -68,6 +83,8 @@ */ public class ForwardExtentCopy implements Operation { + private static final Logger LOGGER = LogManagerCompat.getLogger(); + private final Extent source; private final Extent destination; private final Region region; @@ -405,16 +422,53 @@ public Operation resume(RunContext run) throws WorldEditException { blockCopy = new RegionVisitor(region, copy, preloader); } - List entities; + Collection entities; if (copyingEntities) { - // filter players since they can't be copied - entities = source.getEntities(region); - entities.removeIf(entity -> { - EntityProperties properties = entity.getFacet(EntityProperties.class); - return properties != null && !properties.isPasteable(); - }); + IQueueExtent queue; + Extent ext = source instanceof AbstractDelegateExtent ex ? ex.getExtent() : source; + ParallelQueueExtent parallel = new ExtentTraverser<>(source).findAndGet(ParallelQueueExtent.class); + if (parallel != null) { + queue = parallel.getExtent(); + } else { + queue = new ExtentTraverser<>(source).findAndGet(SingleThreadQueueExtent.class); + } + if (Settings.settings().EXPERIMENTAL.IMPROVED_ENTITY_EDITS && queue != null) { + entities = new LinkedBlockingQueue<>(); + OncePerChunkExtent oncePer = new OncePerChunkExtent( + ext, + queue, + (get) -> { + if (region.containsChunk(get.getX(), get.getZ())) { + entities.addAll(get.getFullEntities()); + } else { + get.getFullEntities().forEach(e -> { + if (region.contains(e.getLocation().toBlockPoint())) { + entities.add(e); + } + }); + } + } + ); + ExtentBatchProcessorHolder batchExtent = + new ExtentTraverser<>(source).findAndGet(ExtentBatchProcessorHolder.class); + if (batchExtent != null) { + batchExtent.getProcessor().join(oncePer); + } else { + new ExtentTraverser(source).setNext(oncePer); + } + } else { + if (Settings.settings().EXPERIMENTAL.IMPROVED_ENTITY_EDITS) { + LOGGER.warn("Could not find IQueueExtent instance for entity retrieval, falling back to default method."); + } + // filter players since they can't be copied + entities = new HashSet<>(source.getEntities(region)); + entities.removeIf(entity -> { + EntityProperties properties = entity.getFacet(EntityProperties.class); + return properties != null && !properties.isPasteable(); + }); + } } else { - entities = Collections.emptyList(); + entities = Collections.emptySet(); } for (int i = 0; i < repetitions; i++) { @@ -476,4 +530,40 @@ public Iterable getStatusMessages() { ); } + private static final class EntityHolder implements Entity { + + @Nullable + @Override + public BaseEntity getState() { + return null; + } + + @Override + public boolean remove() { + return false; + } + + @Override + public Location getLocation() { + return null; + } + + @Override + public boolean setLocation(final Location location) { + return false; + } + + @Override + public Extent getExtent() { + return null; + } + + @Nullable + @Override + public T getFacet(final Class cls) { + return null; + } + + } + } From 63b2d9bd9df05d757dca434e72d94be69916f363 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sat, 15 Jul 2023 14:41:05 +0100 Subject: [PATCH 2/8] minor changes --- .../core/extent/OncePerChunkExtent.java | 7 +++- .../core/math/LocalBlockVector2Set.java | 35 +++++++++++-------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java index cd2d68f9a6..9869fede15 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java @@ -20,7 +20,9 @@ import java.util.function.Consumer; /** - * Extent/processor that runs a t + * Extent/processor that runs a task the first time a chunk GET is loaded + * + * @since TODO */ public class OncePerChunkExtent extends AbstractDelegateExtent implements IBatchProcessor { @@ -34,6 +36,9 @@ public class OncePerChunkExtent extends AbstractDelegateExtent implements IBatch * Create a new instance. * * @param extent the extent + * @param queue Queue to load chunk GET from if acting as extent not processor + * @param task Consumer task for the chunk GET + * @since TODO */ public OncePerChunkExtent(Extent extent, IQueueExtent queue, Consumer task) { super(extent); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java index 7937f78136..bfc80ee916 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java @@ -11,9 +11,11 @@ import java.util.Set; /** - * The LocalBlockVectorSet is a Memory and CPU optimized Set for storing BlockVectors which are all in a local region - * - All vectors must be in a 2048 * 512 * 2048 area centered around the first entry - * - This will use 8 bytes for every 64 BlockVectors (about 800x less than a HashSet) + * The LocalBlockVector2Set is a Memory and CPU optimized Set for storing BlockVector2s which are all in a local region + * - All vectors must be in a 65534 * 65534 area centered around the first entry + * - This will use 8 bytes for every 64 BlockVector2s (about 600x less than a HashSet) + * + * @since TODO */ public class LocalBlockVector2Set implements Set { @@ -22,8 +24,9 @@ public class LocalBlockVector2Set implements Set { private int offsetZ; /** - * New LocalBlockVectorSet that will set the offset x and z to the first value given. The y offset will default to 128 to - * allow -64 -> 320 world height. + * New LocalBlockVectorSet that will set the offset x and z to the first value given. + * + * @since TODO */ public LocalBlockVector2Set() { offsetX = offsetZ = Integer.MAX_VALUE; @@ -31,7 +34,7 @@ public LocalBlockVector2Set() { } /** - * New LocalBlockVectorSet with a given offset. Defaults y offset to 128. + * New LocalBlockVectorSet with a given offset. * * @param x x offset * @param z z offset @@ -71,10 +74,15 @@ public boolean contains(int x, int z) { } short sx = (short) (x - offsetX); short sz = (short) (z - offsetZ); - if (sx > 32767 || sx < -32768 || sz > 32767 || sz < -32768) { + if (sx > 32767 || sx < -32767 || sz > 32767 || sz < -32767) { return false; } - return set.get(MathMan.pairSearchCoords(sx, sz)); + try { + return set.get(MathMan.pairSearchCoords(sx, sz)); + } catch (IndexOutOfBoundsException e) { + System.out.println(x + " " + z + " " + sx + " " + sz); + throw e; + } } @Override @@ -125,8 +133,7 @@ public boolean containsRadius(int x, int z, int radius) { } /** - * Set the offset applied to values when storing and reading to keep the values within -1024 to 1023. Uses default y offset - * of 128 to allow -64 -> 320 world height use. + * Set the offset applied to values when storing and reading to keep the values within -32767 to 32767. * * @param x x offset * @param z z offset @@ -226,7 +233,7 @@ public boolean canAdd(int x, int z) { } int relX = x - offsetX; int relZ = z - offsetZ; - return relX <= 32767 && relX >= -32768 && relZ <= 32727 && relZ >= -32768; + return relX <= 32767 && relX >= -32767 && relZ <= 32727 && relZ >= -32767; } /** @@ -243,9 +250,9 @@ public boolean add(int x, int z) { } int relX = x - offsetX; int relZ = z - offsetZ; - if (relX > 32767 || relX < -32768 || relZ > 32767 || relZ < -32768) { + if (relX > 32767 || relX < -32767 || relZ > 32767 || relZ < -32767) { throw new UnsupportedOperationException( - "LocalBlockVector2Set can only contain vectors within 32768 blocks (cuboid) of the first entry. Attempted " + "to set block at " + x + ", " + z + ". With origin " + offsetX + " " + offsetZ); + "LocalBlockVector2Set can only contain vectors within 32767 blocks (cuboid) of the first entry. Attempted " + "to set block at " + x + ", " + z + ". With origin " + offsetX + " " + offsetZ); } int index = getIndex(x, z); if (set.get(index)) { @@ -285,7 +292,7 @@ private int getIndex(int x, int z) { public boolean remove(int x, int z) { int relX = x - offsetX; int relZ = z - offsetZ; - if (relX > 1023 || relX < -1024 || relZ > 1023 || relZ < -1024) { + if (relX > 32767 || relX < -32767 || relZ > 32767 || relZ < -32767) { return false; } int index = MathMan.pairSearchCoords((short) (x - offsetX), (short) (z - offsetZ)); From 6afc7501db49e87cf44c7d545e5502523b1b60d6 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Fri, 21 Jul 2023 12:37:04 +0100 Subject: [PATCH 3/8] add getter and setter for task --- .../core/extent/OncePerChunkExtent.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java index 9869fede15..5125ad9bc5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java @@ -17,6 +17,8 @@ import com.sk89q.worldedit.world.block.BlockState; import org.jetbrains.annotations.Nullable; +import javax.annotation.Nonnull; +import java.util.Objects; import java.util.function.Consumer; /** @@ -28,7 +30,7 @@ public class OncePerChunkExtent extends AbstractDelegateExtent implements IBatch private final LocalBlockVector2Set set = new LocalBlockVector2Set(); private final IQueueExtent queue; - private final Consumer task; + private Consumer task; private volatile long lastPair = Long.MAX_VALUE; private volatile boolean isProcessing; @@ -40,10 +42,14 @@ public class OncePerChunkExtent extends AbstractDelegateExtent implements IBatch * @param task Consumer task for the chunk GET * @since TODO */ - public OncePerChunkExtent(Extent extent, IQueueExtent queue, Consumer task) { + public OncePerChunkExtent( + @Nonnull Extent extent, + @Nonnull IQueueExtent queue, + @Nonnull Consumer task + ) { super(extent); - this.queue = queue; - this.task = task; + this.queue = Objects.requireNonNull(queue); + this.task = Objects.requireNonNull(task); } private boolean shouldRun(int chunkX, int chunkZ) { @@ -67,6 +73,14 @@ private void checkAndRun(int chunkX, int chunkZ) { } } + public Consumer getTask() { + return task; + } + + public void setTask(Consumer task) { + this.task = task; + } + @Override public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { return set; From 8acb7ffcaef685d200b70c84574aafa1e72dd552 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sat, 16 Mar 2024 15:44:12 +0000 Subject: [PATCH 4/8] Address comments --- .../fawe/v1_20_R2/PaperweightGetBlocks.java | 82 ++---------------- .../fawe/v1_20_R3/PaperweightGetBlocks.java | 82 ++---------------- .../fawe/v1_20_R4/PaperweightGetBlocks.java | 82 ++---------------- .../fawe/v1_21_R1/PaperweightGetBlocks.java | 83 ++----------------- .../adapter/NativeEntityFunctionSet.java | 60 ++++++++++++++ .../core/extent/OncePerChunkExtent.java | 10 +-- .../core/math/LocalBlockVector2Set.java | 6 +- 7 files changed, 93 insertions(+), 312 deletions(-) create mode 100644 worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NativeEntityFunctionSet.java diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java index 8251ee0e37..0d716e9b72 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java @@ -2,6 +2,7 @@ import com.fastasyncworldedit.bukkit.adapter.BukkitGetBlocks; import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; +import com.fastasyncworldedit.bukkit.adapter.NativeEntityFunctionSet; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Settings; @@ -377,44 +378,11 @@ public Collection entities() { if (entities.isEmpty()) { return Collections.emptyList(); } - int size = entities.size(); - return new AbstractCollection<>() { - @Override - public int size() { - return size; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean contains(Object get) { - if (!(get instanceof FaweCompoundTag getTag)) { - return false; - } - UUID getUUID = NbtUtils.uuid(getTag); - for (Entity entity : entities) { - UUID uuid = entity.getUUID(); - if (uuid.equals(getUUID)) { - return true; - } - } - return false; - } - - @Nonnull - @Override - public Iterator iterator() { - Iterable result = entities.stream().map(input -> { - CompoundTag tag = new CompoundTag(); - input.save(tag); - return FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(tag)); - })::iterator; - return result.iterator(); - } - }; + return new NativeEntityFunctionSet<>(entities, Entity::getUUID, e -> { + net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + e.save(tag); + return FaweCompoundTag.of(() -> (LinCompoundTag) adapter.toNativeLin(tag)); + }); } @Override @@ -423,43 +391,7 @@ public Set getFullEntities() { if (entities.isEmpty()) { return Collections.emptySet(); } - int size = entities.size(); - return new AbstractSet<>() { - @Override - public int size() { - return size; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean contains(Object get) { - if (!(get instanceof com.sk89q.worldedit.entity.Entity e)) { - return false; - } - UUID getUUID = e.getState().getNbtData().getUUID(); - for (Entity entity : entities) { - UUID uuid = entity.getUUID(); - if (uuid.equals(getUUID)) { - return true; - } - } - return false; - } - - @Nonnull - @Override - public Iterator iterator() { - Iterable result = entities - .stream() - .map(input -> new BukkitEntity(input.getBukkitEntity())) - .collect(Collectors.toList()); - return result.iterator(); - } - }; + return new NativeEntityFunctionSet<>(entities, Entity::getUUID, e -> new BukkitEntity(e.getBukkitEntity())); } private void removeEntity(Entity entity) { diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java index a04f7d2d49..399eacefb7 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java @@ -2,6 +2,7 @@ import com.fastasyncworldedit.bukkit.adapter.BukkitGetBlocks; import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; +import com.fastasyncworldedit.bukkit.adapter.NativeEntityFunctionSet; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Settings; @@ -377,44 +378,11 @@ public Collection entities() { if (entities.isEmpty()) { return Collections.emptyList(); } - int size = entities.size(); - return new AbstractCollection<>() { - @Override - public int size() { - return size; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean contains(Object get) { - if (!(get instanceof FaweCompoundTag getTag)) { - return false; - } - UUID getUUID = NbtUtils.uuid(getTag); - for (Entity entity : entities) { - UUID uuid = entity.getUUID(); - if (uuid.equals(getUUID)) { - return true; - } - } - return false; - } - - @Nonnull - @Override - public Iterator iterator() { - Iterable result = entities.stream().map(input -> { - net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); - input.save(tag); - return FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(tag)); - })::iterator; - return result.iterator(); - } - }; + return new NativeEntityFunctionSet<>(entities, Entity::getUUID, e -> { + net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + e.save(tag); + return FaweCompoundTag.of(() -> (LinCompoundTag) adapter.toNativeLin(tag)); + }); } @Override @@ -423,43 +391,7 @@ public Set getFullEntities() { if (entities.isEmpty()) { return Collections.emptySet(); } - int size = entities.size(); - return new AbstractSet<>() { - @Override - public int size() { - return size; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean contains(Object get) { - if (!(get instanceof com.sk89q.worldedit.entity.Entity e)) { - return false; - } - UUID getUUID = e.getState().getNbtData().getUUID(); - for (Entity entity : entities) { - UUID uuid = entity.getUUID(); - if (uuid.equals(getUUID)) { - return true; - } - } - return false; - } - - @Nonnull - @Override - public Iterator iterator() { - Iterable result = entities - .stream() - .map(input -> new BukkitEntity(input.getBukkitEntity())) - .collect(Collectors.toList()); - return result.iterator(); - } - }; + return new NativeEntityFunctionSet<>(entities, Entity::getUUID, e -> new BukkitEntity(e.getBukkitEntity())); } private void removeEntity(Entity entity) { diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java index 453801190c..42fec53a66 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java @@ -2,6 +2,7 @@ import com.fastasyncworldedit.bukkit.adapter.BukkitGetBlocks; import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; +import com.fastasyncworldedit.bukkit.adapter.NativeEntityFunctionSet; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Settings; @@ -378,44 +379,11 @@ public Collection entities() { if (entities.isEmpty()) { return Collections.emptyList(); } - int size = entities.size(); - return new AbstractCollection<>() { - @Override - public int size() { - return size; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean contains(Object get) { - if (!(get instanceof FaweCompoundTag getTag)) { - return false; - } - UUID getUUID = NbtUtils.uuid(getTag); - for (Entity entity : entities) { - UUID uuid = entity.getUUID(); - if (uuid.equals(getUUID)) { - return true; - } - } - return false; - } - - @Nonnull - @Override - public Iterator iterator() { - Iterable result = entities.stream().map(input -> { - net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); - input.save(tag); - return FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(tag)); - })::iterator; - return result.iterator(); - } - }; + return new NativeEntityFunctionSet<>(entities, Entity::getUUID, e -> { + net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + e.save(tag); + return FaweCompoundTag.of(() -> (LinCompoundTag) adapter.toNativeLin(tag)); + }); } @Override @@ -424,43 +392,7 @@ public Set getFullEntities() { if (entities.isEmpty()) { return Collections.emptySet(); } - int size = entities.size(); - return new AbstractSet<>() { - @Override - public int size() { - return size; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean contains(Object get) { - if (!(get instanceof com.sk89q.worldedit.entity.Entity e)) { - return false; - } - UUID getUUID = e.getState().getNbtData().getUUID(); - for (Entity entity : entities) { - UUID uuid = entity.getUUID(); - if (uuid.equals(getUUID)) { - return true; - } - } - return false; - } - - @Nonnull - @Override - public Iterator iterator() { - Iterable result = entities - .stream() - .map(input -> new BukkitEntity(input.getBukkitEntity())) - .collect(Collectors.toList()); - return result.iterator(); - } - }; + return new NativeEntityFunctionSet<>(entities, Entity::getUUID, e -> new BukkitEntity(e.getBukkitEntity())); } private void removeEntity(Entity entity) { diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java index 095b17b63e..4bc91ea86b 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java @@ -2,6 +2,7 @@ import com.fastasyncworldedit.bukkit.adapter.BukkitGetBlocks; import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; +import com.fastasyncworldedit.bukkit.adapter.NativeEntityFunctionSet; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Settings; @@ -378,45 +379,11 @@ public Collection entities() { if (entities.isEmpty()) { return Collections.emptyList(); } - int size = entities.size(); - return new AbstractCollection<>() { - @Override - public int size() { - return size; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean contains(Object get) { - if (!(get instanceof FaweCompoundTag getTag)) { - return false; - } - UUID getUUID = NbtUtils.uuid(getTag); - for (Entity entity : entities) { - UUID uuid = entity.getUUID(); - if (uuid.equals(getUUID)) { - return true; - } - } - return false; - } - - @Nonnull - @Override - public Iterator iterator() { - Iterable result = entities.stream().map(input -> { - CompoundTag tag = new CompoundTag(); - input.save(tag); - return FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(tag)); - })::iterator; - return result.iterator(); - } - }; - + return new NativeEntityFunctionSet<>(entities, Entity::getUUID, e -> { + net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + e.save(tag); + return FaweCompoundTag.of(() -> (LinCompoundTag) adapter.toNativeLin(tag)); + }); } @Override @@ -427,43 +394,7 @@ public Set getFullEntities() { if (entities.isEmpty()) { return Collections.emptySet(); } - int size = entities.size(); - return new AbstractSet<>() { - @Override - public int size() { - return size; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean contains(Object get) { - if (!(get instanceof com.sk89q.worldedit.entity.Entity e)) { - return false; - } - UUID getUUID = e.getState().getNbtData().getUUID(); - for (Entity entity : entities) { - UUID uuid = entity.getUUID(); - if (uuid.equals(getUUID)) { - return true; - } - } - return false; - } - - @Nonnull - @Override - public Iterator iterator() { - Iterable result = entities - .stream() - .map(input -> new BukkitEntity(input.getBukkitEntity())) - .collect(Collectors.toList()); - return result.iterator(); - } - }; + return new NativeEntityFunctionSet<>(entities, Entity::getUUID, e -> new BukkitEntity(e.getBukkitEntity())); } private void removeEntity(Entity entity) { diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NativeEntityFunctionSet.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NativeEntityFunctionSet.java new file mode 100644 index 0000000000..67196ccbb2 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NativeEntityFunctionSet.java @@ -0,0 +1,60 @@ +package com.fastasyncworldedit.bukkit.adapter; + +import javax.annotation.Nonnull; +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; + +public class NativeEntityFunctionSet extends AbstractSet { + + private final List nativeEntities; + private final Function uuidGetter; + private final Function resultFunction; + + public NativeEntityFunctionSet( + List nativeEntities, + Function uuidGetter, + Function resultFunction + ) { + this.nativeEntities = nativeEntities; + this.uuidGetter = uuidGetter; + this.resultFunction = resultFunction; + } + + @Override + public int size() { + return nativeEntities.size(); + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object get) { + if (!(get instanceof com.sk89q.worldedit.entity.Entity e)) { + return false; + } + UUID getUUID = e.getState().getNbtData().getUUID(); + for (NativeEntity entity : nativeEntities) { + UUID uuid = uuidGetter.apply(entity); + if (uuid.equals(getUUID)) { + return true; + } + } + return false; + } + + @Nonnull + @Override + public Iterator iterator() { + return nativeEntities + .stream() + .map(resultFunction) + .iterator(); + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java index 5125ad9bc5..4335d59481 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java @@ -32,7 +32,6 @@ public class OncePerChunkExtent extends AbstractDelegateExtent implements IBatch private final IQueueExtent queue; private Consumer task; private volatile long lastPair = Long.MAX_VALUE; - private volatile boolean isProcessing; /** * Create a new instance. @@ -59,16 +58,12 @@ private boolean shouldRun(int chunkX, int chunkZ) { } lastPair = pair; synchronized (set) { - if (!set.contains(chunkX, chunkZ)) { - set.add(chunkX, chunkZ); - return true; - } + return set.add(chunkX, chunkZ); } - return false; } private void checkAndRun(int chunkX, int chunkZ) { - if (!isProcessing && shouldRun(chunkX, chunkZ)) { + if (shouldRun(chunkX, chunkZ)) { task.accept(queue.getCachedGet(chunkX, chunkZ)); } } @@ -88,7 +83,6 @@ public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChun @Override public IChunkGet processGet(final IChunkGet get) { - isProcessing = true; if (shouldRun(get.getX(), get.getZ())) { task.accept(get); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java index bfc80ee916..9ad9cf2513 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java @@ -72,11 +72,11 @@ public boolean contains(int x, int z) { if (offsetX == Integer.MAX_VALUE) { return false; } - short sx = (short) (x - offsetX); - short sz = (short) (z - offsetZ); - if (sx > 32767 || sx < -32767 || sz > 32767 || sz < -32767) { + if (x > 32767 || x < -32769 || z > 32769 || z < -32767) { return false; } + short sx = (short) (x - offsetX); + short sz = (short) (z - offsetZ); try { return set.get(MathMan.pairSearchCoords(sx, sz)); } catch (IndexOutOfBoundsException e) { From 28764075be03196930b56407aac4e5471ba87edd Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sat, 6 Apr 2024 15:44:59 +0100 Subject: [PATCH 5/8] extend to lazy clipboard --- .../core/extent/OncePerChunkExtent.java | 24 +++- .../extent/clipboard/ReadOnlyClipboard.java | 12 +- .../extent/clipboard/WorldCopyClipboard.java | 111 +++++++++++++-- .../implementation/ParallelQueueExtent.java | 2 +- .../core/util/ExtentTraverser.java | 28 +++- .../java/com/sk89q/worldedit/EditSession.java | 2 +- .../worldedit/command/ClipboardCommands.java | 3 +- .../com/sk89q/worldedit/extent/Extent.java | 2 +- .../worldedit/extent/clipboard/Clipboard.java | 5 + .../function/operation/ForwardExtentCopy.java | 127 +++++++++++------- .../sk89q/worldedit/regions/CuboidRegion.java | 20 +++ 11 files changed, 262 insertions(+), 74 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java index 4335d59481..532bc6af9f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java @@ -52,12 +52,12 @@ public OncePerChunkExtent( } private boolean shouldRun(int chunkX, int chunkZ) { - final long pair = (long) chunkX << 32 | chunkZ & 0xffffffffL; - if (pair == lastPair) { - return false; - } - lastPair = pair; synchronized (set) { + final long pair = (long) chunkX << 32 | chunkZ & 0xffffffffL; + if (pair == lastPair) { + return false; + } + lastPair = pair; return set.add(chunkX, chunkZ); } } @@ -68,10 +68,16 @@ private void checkAndRun(int chunkX, int chunkZ) { } } + /** + * Get the task run once per chunk. + */ public Consumer getTask() { return task; } + /** + * Set the task to be run once per chunk + */ public void setTask(Consumer task) { this.task = task; } @@ -181,4 +187,12 @@ public void setSkyLight(final int x, final int y, final int z, final int value) super.setSkyLight(x, y, z, value); } + /** + * Reset the chunks visited + */ + public void reset() { + lastPair = Long.MAX_VALUE; + set.clear(); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ReadOnlyClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ReadOnlyClipboard.java index aa608be052..0f777196e1 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ReadOnlyClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ReadOnlyClipboard.java @@ -45,17 +45,17 @@ public static ReadOnlyClipboard of(Extent extent, final Region region) { return of(() -> extent, region); } - public static ReadOnlyClipboard of(Extent extent, final Region region, boolean copyEntities, boolean copyBiomes) { - Fawe.instance().getQueueHandler().unCache(); - return of(() -> extent, region, copyEntities, copyBiomes); - } - public static ReadOnlyClipboard of(Supplier supplier, final Region region) { return of(supplier, region, true, false); } public static ReadOnlyClipboard of(Supplier supplier, final Region region, boolean copyEntities, boolean copyBiomes) { - return new WorldCopyClipboard(supplier, region, copyEntities, copyBiomes); + return of(supplier.get(), region, copyEntities, copyBiomes); + } + + public static ReadOnlyClipboard of(Extent extent, final Region region, boolean copyEntities, boolean copyBiomes) { + Fawe.instance().getQueueHandler().unCache(); + return WorldCopyClipboard.of(extent, region, copyEntities, copyBiomes); } private static Supplier supply() { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java index 4bb50ca734..55a0176041 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java @@ -1,14 +1,22 @@ package com.fastasyncworldedit.core.extent.clipboard; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.EditSessionBuilder; +import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.operation.ForwardExtentCopy; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.function.Supplier; @@ -16,26 +24,47 @@ public class WorldCopyClipboard extends ReadOnlyClipboard { private final boolean hasBiomes; private final boolean hasEntities; - private Extent extent; - private Supplier supplier; + private final Extent extent; + /** + * @deprecated use {@link WorldCopyClipboard#of(Extent, Region)} + */ + @Deprecated(forRemoval = true, since = "TODO") public WorldCopyClipboard(Supplier supplier, Region region) { this(supplier, region, true, false); } + /** + * @deprecated use {@link WorldCopyClipboard#of(Extent, Region, boolean, boolean)} + */ + @Deprecated(forRemoval = true, since = "TODO") public WorldCopyClipboard(Supplier supplier, Region region, boolean hasEntities, boolean hasBiomes) { super(region); this.hasBiomes = hasBiomes; this.hasEntities = hasEntities; - this.supplier = supplier; + this.extent = supplier.get(); + } + + private WorldCopyClipboard(Extent extent, Region region, boolean hasEntities, boolean hasBiomes) { + super(region); + this.hasBiomes = hasBiomes; + this.hasEntities = hasEntities; + this.extent = extent; + } + + public static WorldCopyClipboard of(Extent extent, Region region) { + return of(extent, region, true); + } + + public static WorldCopyClipboard of(Extent extent, Region region, boolean hasEntities) { + return of(extent, region, hasEntities, false); + } + + public static WorldCopyClipboard of(Extent extent, Region region, boolean hasEntities, boolean hasBiomes) { + return new WorldCopyClipboard(extent, region, hasEntities, hasBiomes); } public Extent getExtent() { - if (extent != null) { - return extent; - } - extent = supplier.get(); - supplier = null; return extent; } @@ -60,6 +89,7 @@ public BiomeType getBiome(BlockVector3 position) { } @Override + @Deprecated public List getEntities() { if (!hasEntities) { return new ArrayList<>(); @@ -72,4 +102,69 @@ public boolean hasBiomes() { return hasBiomes; } + @Override + public void paste(Extent toExtent, BlockVector3 to, boolean pasteAir, boolean pasteEntities, boolean pasteBiomes) { + boolean close = false; + if (toExtent instanceof World) { + close = true; + EditSessionBuilder builder = WorldEdit + .getInstance() + .newEditSessionBuilder() + .world((World) toExtent) + .checkMemory(false) + .allowedRegionsEverywhere() + .limitUnlimited() + .changeSetNull(); + toExtent = builder.build(); + } + + Extent source = getExtent(); + + Collection entities = pasteEntities ? ForwardExtentCopy.getEntities(source, region) : Collections.emptySet(); + + final BlockVector3 origin = this.getOrigin(); + + // To must be relative to the clipboard origin ( player location - clipboard origin ) (as the locations supplied are relative to the world origin) + final int relx = to.getBlockX() - origin.getBlockX(); + final int rely = to.getBlockY() - origin.getBlockY(); + final int relz = to.getBlockZ() - origin.getBlockZ(); + + pasteBiomes &= this.hasBiomes(); + + for (BlockVector3 pos : this) { + BaseBlock block = pos.getFullBlock(this); + int xx = pos.getX() + relx; + int yy = pos.getY() + rely; + int zz = pos.getZ() + relz; + if (pasteBiomes) { + toExtent.setBiome(xx, yy, zz, pos.getBiome(this)); + } + if (!pasteAir && block.getBlockType().getMaterial().isAir()) { + continue; + } + toExtent.setBlock(xx, yy, zz, block); + } + // Entity offset is the paste location subtract the clipboard origin (entity's location is already relative to the world origin) + final int entityOffsetX = to.getBlockX() - origin.getBlockX(); + final int entityOffsetY = to.getBlockY() - origin.getBlockY(); + final int entityOffsetZ = to.getBlockZ() - origin.getBlockZ(); + // entities + for (Entity entity : entities) { + // skip players on pasting schematic + if (entity.getState() != null && entity.getState().getType().getId() + .equals("minecraft:player")) { + continue; + } + Location pos = entity.getLocation(); + Location newPos = new Location(pos.getExtent(), pos.getX() + entityOffsetX, + pos.getY() + entityOffsetY, pos.getZ() + entityOffsetZ, pos.getYaw(), + pos.getPitch() + ); + toExtent.createEntity(newPos, entity.getState()); + } + if (close) { + ((EditSession) toExtent).close(); + } + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java index 56a2381666..da43ff2052 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java @@ -266,7 +266,7 @@ public List> getBlockDistribution(Region region) { */ @Override public Clipboard lazyCopy(Region region) { - Clipboard clipboard = new WorldCopyClipboard(() -> this, region); + Clipboard clipboard = WorldCopyClipboard.of(this, region); clipboard.setOrigin(region.getMinimumPoint()); return clipboard; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java index 6ae147765c..dc103dc799 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java @@ -2,6 +2,8 @@ import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.internal.util.LogManagerCompat; +import org.apache.logging.log4j.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -9,6 +11,8 @@ public class ExtentTraverser { + private static final Logger LOGGER = LogManagerCompat.getLogger(); + private final T root; private final ExtentTraverser parent; @@ -98,9 +102,8 @@ public ExtentTraverser previous() { @SuppressWarnings("unchecked") public ExtentTraverser next() { try { - if (root instanceof AbstractDelegateExtent) { - AbstractDelegateExtent root = (AbstractDelegateExtent) this.root; - T value = (T) root.getExtent(); + if (root instanceof AbstractDelegateExtent abstractDelegateExtent) { + T value = (T) abstractDelegateExtent.getExtent(); if (value == null) { return null; } @@ -113,4 +116,23 @@ public ExtentTraverser next() { } } + public static void printNestedExtents(Extent extent) { + String nested = printNestedExtent(new StringBuilder("Extent tree:"), new ExtentTraverser<>(extent), 0).toString(); + LOGGER.info(nested); + } + + private static StringBuilder printNestedExtent(StringBuilder builder, ExtentTraverser traverser, int depth) { + if (traverser == null || !traverser.exists()) { + return builder; + } + String indent = " ".repeat(Math.max(0, depth)); // Adjust the indentation as needed + + Extent extent = traverser.get(); + builder.append("\n").append(indent).append("- ").append(extent.getClass().getSimpleName()); + + // Recursively print nested extents + printNestedExtent(builder, traverser.next(), depth + 1); + return builder; + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 85fbde830b..75e837f22e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -3885,7 +3885,7 @@ public void addOre(Region region, Mask mask, Pattern material, int size, int fre @Override public Clipboard lazyCopy(Region region) { - WorldCopyClipboard faweClipboard = new WorldCopyClipboard(() -> this, region); + WorldCopyClipboard faweClipboard = WorldCopyClipboard.of(this, region); faweClipboard.setOrigin(region.getMinimumPoint()); return faweClipboard; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index bb42e70955..9bda82380b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -29,6 +29,7 @@ import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder; import com.fastasyncworldedit.core.extent.clipboard.ReadOnlyClipboard; import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder; +import com.fastasyncworldedit.core.extent.clipboard.WorldCopyClipboard; import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream; import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.util.ImgurUtility; @@ -192,7 +193,7 @@ public void lazyCopy( throw FaweCache.MAX_CHECKS; } session.setClipboard(null); - ReadOnlyClipboard lazyClipboard = ReadOnlyClipboard.of(region, !skipEntities, copyBiomes); + ReadOnlyClipboard lazyClipboard = WorldCopyClipboard.of(editSession, region, !skipEntities, copyBiomes); lazyClipboard.setOrigin(session.getPlacementPosition(actor)); session.setClipboard(new ClipboardHolder(lazyClipboard)); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java index 45f604f715..47ab909dcb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java @@ -692,7 +692,7 @@ default int getMaxY() { * @return */ default Clipboard lazyCopy(Region region) { - WorldCopyClipboard faweClipboard = new WorldCopyClipboard(() -> this, region); + WorldCopyClipboard faweClipboard = WorldCopyClipboard.of(this, region); faweClipboard.setOrigin(region.getMinimumPoint()); return faweClipboard; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java index cac9f4a7b2..be702ae4d4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java @@ -381,7 +381,9 @@ default void paste(Extent extent, BlockVector3 to, boolean pasteAir) { } default void paste(Extent extent, BlockVector3 to, boolean pasteAir, boolean pasteEntities, boolean pasteBiomes) { + boolean close = false; if (extent instanceof World) { + close = true; EditSessionBuilder builder = WorldEdit .getInstance() .newEditSessionBuilder() @@ -435,6 +437,9 @@ default void paste(Extent extent, BlockVector3 to, boolean pasteAir, boolean pas extent.createEntity(newPos, entity.getState()); } } + if (close) { + ((EditSession) extent).close(); + } } //FAWE end } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java index 5403b38074..f57a536bb1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java @@ -24,18 +24,21 @@ import com.fastasyncworldedit.core.extent.BlockTranslateExtent; import com.fastasyncworldedit.core.extent.OncePerChunkExtent; import com.fastasyncworldedit.core.extent.PositionTransformExtent; +import com.fastasyncworldedit.core.extent.clipboard.WorldCopyClipboard; import com.fastasyncworldedit.core.extent.processor.ExtentBatchProcessorHolder; import com.fastasyncworldedit.core.function.RegionMaskTestFunction; import com.fastasyncworldedit.core.function.block.BiomeCopy; import com.fastasyncworldedit.core.function.block.CombinedBlockCopy; import com.fastasyncworldedit.core.function.block.SimpleBlockCopy; import com.fastasyncworldedit.core.function.visitor.IntersectRegionFunction; +import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent; import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent; import com.fastasyncworldedit.core.util.ExtentTraverser; import com.fastasyncworldedit.core.util.MaskTraverser; +import com.fastasyncworldedit.core.util.ProcessorTraverser; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.sk89q.worldedit.WorldEditException; @@ -57,6 +60,7 @@ import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.math.transform.Identity; import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.FlatRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; @@ -69,7 +73,9 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; +import java.util.function.Consumer; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -422,54 +428,7 @@ public Operation resume(RunContext run) throws WorldEditException { blockCopy = new RegionVisitor(region, copy, preloader); } - Collection entities; - if (copyingEntities) { - IQueueExtent queue; - Extent ext = source instanceof AbstractDelegateExtent ex ? ex.getExtent() : source; - ParallelQueueExtent parallel = new ExtentTraverser<>(source).findAndGet(ParallelQueueExtent.class); - if (parallel != null) { - queue = parallel.getExtent(); - } else { - queue = new ExtentTraverser<>(source).findAndGet(SingleThreadQueueExtent.class); - } - if (Settings.settings().EXPERIMENTAL.IMPROVED_ENTITY_EDITS && queue != null) { - entities = new LinkedBlockingQueue<>(); - OncePerChunkExtent oncePer = new OncePerChunkExtent( - ext, - queue, - (get) -> { - if (region.containsChunk(get.getX(), get.getZ())) { - entities.addAll(get.getFullEntities()); - } else { - get.getFullEntities().forEach(e -> { - if (region.contains(e.getLocation().toBlockPoint())) { - entities.add(e); - } - }); - } - } - ); - ExtentBatchProcessorHolder batchExtent = - new ExtentTraverser<>(source).findAndGet(ExtentBatchProcessorHolder.class); - if (batchExtent != null) { - batchExtent.getProcessor().join(oncePer); - } else { - new ExtentTraverser(source).setNext(oncePer); - } - } else { - if (Settings.settings().EXPERIMENTAL.IMPROVED_ENTITY_EDITS) { - LOGGER.warn("Could not find IQueueExtent instance for entity retrieval, falling back to default method."); - } - // filter players since they can't be copied - entities = new HashSet<>(source.getEntities(region)); - entities.removeIf(entity -> { - EntityProperties properties = entity.getFacet(EntityProperties.class); - return properties != null && !properties.isPasteable(); - }); - } - } else { - entities = Collections.emptySet(); - } + Collection entities = copyingEntities ? getEntities(source, region) : Collections.emptySet(); for (int i = 0; i < repetitions; i++) { Operations.completeBlindly(blockCopy); @@ -508,6 +467,78 @@ public Operation resume(RunContext run) throws WorldEditException { return null; } + /** + * If setting enabled, Creates a new OncePerChunkExtent instance to retain a list of entities for the given source extent, + * then add it to the source extent. If setting is not set simply returns the entities from {@link Extent#getEntities()} Accepts an + * optional region for entities to be within. + * + * @param source Source extent + * @param region Optional regions for entities to be within + * @return Collection of entities (may not be filled until an operation completes on the chunks) + * @since TODO + */ + public static Collection getEntities(Extent source, Region region) { + Extent extent = source; + if (source instanceof WorldCopyClipboard clip) { + extent = clip.getExtent(); + } + IQueueExtent queue = null; + if (Settings.settings().EXPERIMENTAL.IMPROVED_ENTITY_EDITS) { + ParallelQueueExtent parallel = new ExtentTraverser<>(extent).findAndGet(ParallelQueueExtent.class); + if (parallel != null) { + queue = parallel.getExtent(); + } else { + queue = new ExtentTraverser<>(extent).findAndGet(SingleThreadQueueExtent.class); + } + if (queue == null) { + LOGGER.warn("Could not find IQueueExtent instance for entity retrieval, OncePerChunkExtent will not work."); + } + } + if (queue == null) { + Set entities = new HashSet<>(region != null ? source.getEntities(region) : source.getEntities()); + entities.removeIf(entity -> { + EntityProperties properties = entity.getFacet(EntityProperties.class); + return properties != null && !properties.isPasteable(); + }); + return entities; + } + LinkedBlockingQueue entities = new LinkedBlockingQueue<>(); + Consumer task = (get) -> { + if (region == null || region instanceof CuboidRegion cuboid && cuboid.chunkContainedBy( + get.getX(), + get.getZ(), + get.getMinY(), + get.getMaxY() + )) { + entities.addAll(get.getFullEntities()); + } else { + get.getFullEntities().forEach(e -> { + if (region.contains(e.getLocation().toBlockPoint())) { + entities.add(e); + } + }); + } + }; + Extent ext = extent instanceof AbstractDelegateExtent ex ? ex.getExtent() : extent; + ExtentBatchProcessorHolder batchExtent = new ExtentTraverser<>(extent).findAndGet(ExtentBatchProcessorHolder.class); + OncePerChunkExtent oncePer = new ExtentTraverser<>(extent).findAndGet(OncePerChunkExtent.class); + if (batchExtent != null && oncePer == null) { + oncePer = new ProcessorTraverser<>(batchExtent).find(OncePerChunkExtent.class); + } + if (oncePer != null) { + oncePer.reset(); + oncePer.setTask(task); + } else { + oncePer = new OncePerChunkExtent(ext, queue, task); + if (false && batchExtent != null) { + batchExtent.getProcessor().join(oncePer); + } else { + new ExtentTraverser(extent).setNext(oncePer); + } + } + return entities; + } + @Override public void cancel() { } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java index 4b0894d918..3c0879a805 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java @@ -744,6 +744,26 @@ public int getMaximumZ() { return maxZ; } + /** + * Check if an entire chunk is contained by this region + * + * @param chunkX chunk x coord + * @param chunkZ chunk z coord + * @param chunkMinY The minimum Y level of the chunk + * @param chunkMaxY The maximum Y level of the chunk + * @return If the entire chunk is contained by this region + * @since TODO + */ + public boolean chunkContainedBy(int chunkX, int chunkZ, int chunkMinY, int chunkMaxY) { + int bx = chunkX << 4; + int bz = chunkZ << 4; + int tx = bx + 15; + int tz = bz + 15; + BlockVector3 min = getMinimumPoint(); + BlockVector3 max = getMaximumPoint(); + return min.getY() <= chunkMinY && max.getY() >= chunkMaxY && min.getX() <= bx && max.getX() >= tx && min.getZ() <= bz && max.getZ() >= tz; + } + @Override public void filter( final IChunk chunk, From defebd4617667d00a136b11d6cbc004bf4ee884d Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sat, 6 Apr 2024 17:46:14 +0100 Subject: [PATCH 6/8] remove EntityHolder --- .../function/operation/ForwardExtentCopy.java | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java index f57a536bb1..0c6508cd34 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java @@ -561,40 +561,4 @@ public Iterable getStatusMessages() { ); } - private static final class EntityHolder implements Entity { - - @Nullable - @Override - public BaseEntity getState() { - return null; - } - - @Override - public boolean remove() { - return false; - } - - @Override - public Location getLocation() { - return null; - } - - @Override - public boolean setLocation(final Location location) { - return false; - } - - @Override - public Extent getExtent() { - return null; - } - - @Nullable - @Override - public T getFacet(final Class cls) { - return null; - } - - } - } From 0cf41b146242a88247f9f49c980a0dcc5a5c6c7e Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Fri, 31 May 2024 18:21:21 +0100 Subject: [PATCH 7/8] Address deprecations in vector classes --- .../core/extent/OncePerChunkExtent.java | 8 +++---- .../extent/clipboard/WorldCopyClipboard.java | 22 +++++++++---------- .../core/math/LocalBlockVector2Set.java | 8 +++---- .../sk89q/worldedit/regions/CuboidRegion.java | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java index 532bc6af9f..fbbd92ad23 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java @@ -111,7 +111,7 @@ public ProcessorScope getScope() { @Override public BlockState getBlock(final BlockVector3 position) { - checkAndRun(position.getBlockX() >> 4, position.getBlockZ() >> 4); + checkAndRun(position.x() >> 4, position.z() >> 4); return super.getBlock(position); } @@ -123,7 +123,7 @@ public BlockState getBlock(final int x, final int y, final int z) { @Override public BaseBlock getFullBlock(final BlockVector3 position) { - checkAndRun(position.getBlockX() >> 4, position.getBlockZ() >> 4); + checkAndRun(position.x() >> 4, position.z() >> 4); return super.getFullBlock(position); } @@ -141,7 +141,7 @@ public BiomeType getBiomeType(final int x, final int y, final int z) { @Override public BiomeType getBiome(final BlockVector3 position) { - checkAndRun(position.getBlockX() >> 4, position.getBlockZ() >> 4); + checkAndRun(position.x() >> 4, position.z() >> 4); return super.getBiome(position); } @@ -171,7 +171,7 @@ public boolean setBiome(final int x, final int y, final int z, final BiomeType b @Override public boolean setBiome(final BlockVector3 position, final BiomeType biome) { - checkAndRun(position.getBlockX() >> 4, position.getBlockZ() >> 4); + checkAndRun(position.x() >> 4, position.z() >> 4); return super.setBiome(position, biome); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java index 55a0176041..5359b4240b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java @@ -125,17 +125,17 @@ public void paste(Extent toExtent, BlockVector3 to, boolean pasteAir, boolean pa final BlockVector3 origin = this.getOrigin(); // To must be relative to the clipboard origin ( player location - clipboard origin ) (as the locations supplied are relative to the world origin) - final int relx = to.getBlockX() - origin.getBlockX(); - final int rely = to.getBlockY() - origin.getBlockY(); - final int relz = to.getBlockZ() - origin.getBlockZ(); + final int relx = to.x() - origin.x(); + final int rely = to.y() - origin.y(); + final int relz = to.z() - origin.z(); pasteBiomes &= this.hasBiomes(); for (BlockVector3 pos : this) { BaseBlock block = pos.getFullBlock(this); - int xx = pos.getX() + relx; - int yy = pos.getY() + rely; - int zz = pos.getZ() + relz; + int xx = pos.x() + relx; + int yy = pos.y() + rely; + int zz = pos.z() + relz; if (pasteBiomes) { toExtent.setBiome(xx, yy, zz, pos.getBiome(this)); } @@ -145,9 +145,9 @@ public void paste(Extent toExtent, BlockVector3 to, boolean pasteAir, boolean pa toExtent.setBlock(xx, yy, zz, block); } // Entity offset is the paste location subtract the clipboard origin (entity's location is already relative to the world origin) - final int entityOffsetX = to.getBlockX() - origin.getBlockX(); - final int entityOffsetY = to.getBlockY() - origin.getBlockY(); - final int entityOffsetZ = to.getBlockZ() - origin.getBlockZ(); + final int entityOffsetX = to.x() - origin.x(); + final int entityOffsetY = to.y() - origin.y(); + final int entityOffsetZ = to.z() - origin.z(); // entities for (Entity entity : entities) { // skip players on pasting schematic @@ -156,8 +156,8 @@ public void paste(Extent toExtent, BlockVector3 to, boolean pasteAir, boolean pa continue; } Location pos = entity.getLocation(); - Location newPos = new Location(pos.getExtent(), pos.getX() + entityOffsetX, - pos.getY() + entityOffsetY, pos.getZ() + entityOffsetZ, pos.getYaw(), + Location newPos = new Location(pos.getExtent(), pos.x() + entityOffsetX, + pos.y() + entityOffsetY, pos.z() + entityOffsetZ, pos.getYaw(), pos.getPitch() ); toExtent.createEntity(newPos, entity.getState()); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java index 9ad9cf2513..5d50114c2e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVector2Set.java @@ -88,7 +88,7 @@ public boolean contains(int x, int z) { @Override public boolean contains(Object o) { if (o instanceof BlockVector2 v) { - return contains(v.getBlockX(), v.getBlockZ()); + return contains(v.x(), v.z()); } return false; } @@ -271,11 +271,11 @@ public boolean add(int x, int z) { */ @Override public boolean add(BlockVector2 vector) { - return add(vector.getBlockX(), vector.getBlockZ()); + return add(vector.x(), vector.z()); } private int getIndex(BlockVector2 vector) { - return getIndex(vector.getX(), vector.getZ()); + return getIndex(vector.x(), vector.z()); } private int getIndex(int x, int z) { @@ -304,7 +304,7 @@ public boolean remove(int x, int z) { @Override public boolean remove(Object o) { if (o instanceof BlockVector2 v) { - return remove(v.getBlockX(), v.getBlockZ()); + return remove(v.x(), v.z()); } return false; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java index 3c0879a805..7d39bf129a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java @@ -761,7 +761,7 @@ public boolean chunkContainedBy(int chunkX, int chunkZ, int chunkMinY, int chunk int tz = bz + 15; BlockVector3 min = getMinimumPoint(); BlockVector3 max = getMaximumPoint(); - return min.getY() <= chunkMinY && max.getY() >= chunkMaxY && min.getX() <= bx && max.getX() >= tx && min.getZ() <= bz && max.getZ() >= tz; + return min.y() <= chunkMinY && max.y() >= chunkMaxY && min.x() <= bx && max.x() >= tx && min.z() <= bz && max.z() >= tz; } @Override From 8f7c4bcf852699257db85a7c0e6c939f8ec05349 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sun, 30 Jun 2024 10:56:24 +0100 Subject: [PATCH 8/8] Add for 1.21 --- .../bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java index 4bc91ea86b..de23ed2a3d 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java @@ -388,8 +388,6 @@ public Collection entities() { @Override public Set getFullEntities() { - getSections(true); - getChunk(); List entities = PaperweightPlatformAdapter.getEntities(ensureLoaded(serverLevel, chunkX, chunkZ)); if (entities.isEmpty()) { return Collections.emptySet();