diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java index aab9e5aa7..f10370b8d 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java @@ -19,6 +19,7 @@ import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.BukkitEntity; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.internal.Constants; @@ -57,6 +58,7 @@ import org.apache.logging.log4j.Logger; import org.bukkit.World; import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; import org.bukkit.craftbukkit.v1_17_R1.block.CraftBlock; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity; import org.bukkit.event.entity.CreatureSpawnEvent; import javax.annotation.Nonnull; @@ -124,11 +126,13 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc this.blockLight = new DataLayer[getSectionCount()]; } - public int getChunkX() { + @Override + public int getX() { return chunkX; } - public int getChunkZ() { + @Override + public int getZ() { return chunkZ; } @@ -338,7 +342,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public Set getEntities() { - List entities = PaperweightPlatformAdapter.getEntities(getChunk()); + List entities = PaperweightPlatformAdapter.getEntities(ensureLoaded(serverLevel, chunkX, chunkZ)); if (entities.isEmpty()) { return Collections.emptySet(); } @@ -382,6 +386,53 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc }; } + @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_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks_Copy.java index de431c718..7825dbc4d 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks_Copy.java @@ -36,6 +36,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 ChunkBiomeContainer chunkBiomeContainer; @@ -46,6 +48,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { 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) { @@ -83,6 +87,11 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { return this.entities; } + @Override + public Set getFullEntities() { + throw new UnsupportedOperationException("Cannot get full entities from GET copy."); + } + @Override public CompoundTag getEntity(UUID uuid) { for (CompoundTag tag : entities) { @@ -134,6 +143,16 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { return minHeight >> 4; } + @Override + public int getX() { + return chunkX; + } + + @Override + public int getZ() { + return chunkZ; + } + protected void storeBiomes(ChunkBiomeContainer chunkBiomeContainer) { // The to do one line below is pre-paperweight and needs to be revised // TODO revisit last parameter, BiomeStorage[] *would* be more efficient diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPostProcessor.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPostProcessor.java index 0833c6ecb..13f8ddb77 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPostProcessor.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPostProcessor.java @@ -112,7 +112,7 @@ public class PaperweightPostProcessor implements IBatchProcessor { @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_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java index 91a5abede..d74e60f9d 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java @@ -14,12 +14,12 @@ import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.google.common.base.Suppliers; -import com.google.common.collect.Iterables; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.BukkitEntity; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.internal.Constants; @@ -60,13 +60,13 @@ import org.apache.logging.log4j.Logger; import org.bukkit.World; import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; import org.bukkit.craftbukkit.v1_18_R2.block.CraftBlock; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity; import org.bukkit.event.entity.CreatureSpawnEvent; import javax.annotation.Nonnull; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -82,7 +82,6 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; import java.util.stream.Collectors; -import java.util.stream.StreamSupport; public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks { @@ -132,11 +131,13 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); } - public int getChunkX() { + @Override + public int getX() { return chunkX; } - public int getChunkZ() { + @Override + public int getZ() { return chunkZ; } @@ -332,7 +333,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public Set getEntities() { - List entities = PaperweightPlatformAdapter.getEntities(getChunk()); + List entities = PaperweightPlatformAdapter.getEntities(ensureLoaded(serverLevel, chunkX, chunkZ)); if (entities.isEmpty()) { return Collections.emptySet(); } @@ -376,6 +377,51 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc }; } + @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_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks_Copy.java index 991c3d1f9..4457286df 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks_Copy.java @@ -38,6 +38,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 PalettedContainer>[] biomes = null; @@ -48,6 +50,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { 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) { @@ -85,6 +89,11 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { return this.entities; } + @Override + public Set getFullEntities() { + throw new UnsupportedOperationException("Cannot get full entities from GET copy."); + } + @Override public CompoundTag getEntity(UUID uuid) { for (CompoundTag tag : entities) { @@ -136,6 +145,16 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { 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()].get(x >> 2, (y & 15) >> 2, z >> 2); diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPostProcessor.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPostProcessor.java index abc8d6150..628b015b1 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPostProcessor.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPostProcessor.java @@ -112,7 +112,7 @@ public class PaperweightPostProcessor implements IBatchProcessor { @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_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java index c715e5fc2..d6feb4485 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java @@ -19,6 +19,7 @@ import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.BukkitEntity; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.internal.Constants; @@ -60,6 +61,7 @@ import org.apache.logging.log4j.Logger; import org.bukkit.World; import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlock; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftEntity; import org.bukkit.event.entity.CreatureSpawnEvent; import javax.annotation.Nonnull; @@ -132,11 +134,13 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); } - public int getChunkX() { + @Override + public int getX() { return chunkX; } - public int getChunkZ() { + @Override + public int getZ() { return chunkZ; } @@ -332,8 +336,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public Set getEntities() { - ensureLoaded(serverLevel, chunkX, chunkZ); - List entities = PaperweightPlatformAdapter.getEntities(getChunk()); + List entities = PaperweightPlatformAdapter.getEntities(ensureLoaded(serverLevel, chunkX, chunkZ)); if (entities.isEmpty()) { return Collections.emptySet(); } @@ -377,6 +380,51 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc }; } + @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_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks_Copy.java index b6557ef0b..9cd285fe1 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks_Copy.java @@ -42,6 +42,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 PalettedContainer>[] biomes = null; @@ -52,6 +54,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { 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) { @@ -89,6 +93,11 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { return this.entities; } + @Override + public Set getFullEntities() { + throw new UnsupportedOperationException("Cannot get full entities from GET copy."); + } + @Override public CompoundTag getEntity(UUID uuid) { for (CompoundTag tag : entities) { @@ -140,6 +149,16 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { 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()].get(x >> 2, (y & 15) >> 2, z >> 2); diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPostProcessor.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPostProcessor.java index ac10f2c0a..987bcac36 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPostProcessor.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPostProcessor.java @@ -112,7 +112,7 @@ public class PaperweightPostProcessor implements IBatchProcessor { @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/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java index 08d2f1069..bc1e0788e 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java @@ -19,6 +19,7 @@ import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.BukkitEntity; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.internal.Constants; @@ -49,6 +50,7 @@ import org.apache.logging.log4j.Logger; import org.bukkit.World; import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlock; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity; import org.bukkit.event.entity.CreatureSpawnEvent; import javax.annotation.Nonnull; @@ -111,11 +113,13 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); } - public int getChunkX() { + @Override + public int getX() { return chunkX; } - public int getChunkZ() { + @Override + public int getZ() { return chunkZ; } @@ -310,8 +314,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public Set getEntities() { - ensureLoaded(serverLevel, chunkX, chunkZ); - List entities = PaperweightPlatformAdapter.getEntities(getChunk()); + List entities = PaperweightPlatformAdapter.getEntities(ensureLoaded(serverLevel, chunkX, chunkZ)); if (entities.isEmpty()) { return Collections.emptySet(); } @@ -355,6 +358,51 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc }; } + @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/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks_Copy.java index 7a387a887..cec07bb38 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks_Copy.java @@ -42,6 +42,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 PalettedContainer>[] biomes = null; @@ -52,6 +54,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { 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) { @@ -89,6 +93,11 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { return this.entities; } + @Override + public Set getFullEntities() { + throw new UnsupportedOperationException("Cannot get full entities from GET copy."); + } + @Override public CompoundTag getEntity(UUID uuid) { for (CompoundTag tag : entities) { @@ -140,6 +149,16 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { 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()].get(x >> 2, (y & 15) >> 2, z >> 2); diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPostProcessor.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPostProcessor.java index 0c4bcff85..1cbdd9584 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPostProcessor.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPostProcessor.java @@ -112,7 +112,7 @@ public class PaperweightPostProcessor implements IBatchProcessor { @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 174d59f37..a4ad614db 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 @@ -608,6 +608,13 @@ public class Settings extends Config { }) 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 000000000..cd2d68f9a --- /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 540e933b0..221117260 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 class BatchProcessorHolder implements IBatchProcessorHolder { 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 e57ccb490..ef4fc9ca2 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 @@ -134,7 +134,7 @@ public class MultiBatchProcessor implements IBatchProcessor { 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)); @@ -165,7 +165,7 @@ public class MultiBatchProcessor implements IBatchProcessor { 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); @@ -200,6 +200,14 @@ public class MultiBatchProcessor implements IBatchProcessor { 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 503351fb7..6e97800aa 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 @@ package com.fastasyncworldedit.core.extent.processor; * - CHANGING_BLOCKS (processors that may ADD or CHANGE blocks being set) * - REMOVING_BLOCKS (processors that may ADD, CHANGE or REMOVE blocks being set) * - CUSTOM (processors that do not specify a SCOPE) - * - READING_SET_BLOCKS (processors that do not alter blocks at all, and read the blocks that are actually going to set, e.g. + * - 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 enum ProcessorScope { } 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 3cdfeafb9..16cc173f8 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 @@ -144,7 +144,7 @@ public class HeightmapProcessor implements IBatchProcessor { @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 6a9f16dee..6951ab4e0 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.fastasyncworldedit.core.queue.IChunkSet; 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 @@ public class RelightProcessor implements IBatchProcessor { @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 b3493c896..56d2e0882 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 @@ -233,7 +233,7 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { @Override public ProcessorScope getScope() { - return ProcessorScope.READING_SET_BLOCKS; + return ProcessorScope.READING_BLOCKS; } public abstract void addTileCreate(CompoundTag tag); 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 000000000..7937f7813 --- /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 8e6cdabe8..1536017b3 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 @@ -232,8 +232,8 @@ public class LocalBlockVectorSet implements BlockVector3Set { @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 0d755eec6..c6decdaff 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 class MutableBlockVector2 extends BlockVector2 { 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 19fa1293a..79e3868e7 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( - @Range(from = 0, to = 15) int chunkX, - @Range(from = 0, to = 15) 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 807a90fb4..0c8c967a4 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 @@ -49,6 +49,17 @@ public interface IBatchProcessor { return true; } + /** + * 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. */ 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 ff6842c0a..94e5b39c2 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 @@ -19,7 +19,7 @@ import java.util.Set; 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 { @@ -92,6 +92,16 @@ public interface IBlocks extends Trimable { */ 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/IChunk.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java index 126a5cd13..7add91c66 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java @@ -2,7 +2,6 @@ package com.fastasyncworldedit.core.queue; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.sk89q.worldedit.regions.Region; -import org.jetbrains.annotations.Range; import javax.annotation.Nullable; @@ -20,22 +19,6 @@ public interface IChunk extends Trimable, IChunkGet, IChunkSet { default void init(IQueueExtent extent, int x, int z) { } - /** - * Get chunkX - * - * @return the x coordinate of the chunk - */ - @Range(from = 0, to = 15) - int getX(); - - /** - * Get chunkZ - * - * @return the z coordinate of the chunk - */ - @Range(from = 0, to = 15) - int getZ(); - /** * If the chunk is a delegate, returns its parent's root * diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkCache.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkCache.java index ce2761390..63d07f2b7 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkCache.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkCache.java @@ -8,7 +8,7 @@ import org.jetbrains.annotations.Range; */ public interface IChunkCache extends Trimable { - T get(@Range(from = 0, to = 15) int chunkX, @Range(from = 0, to = 15) int chunkZ); + T get(int chunkX, int chunkZ); @Override default boolean trim(boolean aggressive) { 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 8004f7098..9222abc52 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 @@ -2,6 +2,7 @@ package com.fastasyncworldedit.core.queue; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; 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; @@ -9,6 +10,7 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import javax.annotation.Nullable; +import java.util.Set; import java.util.UUID; import java.util.concurrent.Future; @@ -48,6 +50,13 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput { CompoundTag getEntity(UUID uuid); + /** + * Get the entities in the chunk as "full" entities. + * + * @since TODO; + */ + Set getFullEntities(); + boolean isCreateCopy(); void setCreateCopy(boolean createCopy); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java index a5a29f170..cb09aef7e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java @@ -51,12 +51,12 @@ public interface IQueueExtent extends Flushable, Trimable, ICh * Get the cached get object. This is faster than getting the object using NMS and allows for * wrapping. */ - IChunkGet getCachedGet(@Range(from = 0, to = 15) int chunkX, @Range(from = 0, to = 15) int chunkZ); + IChunkGet getCachedGet(int chunkX, int chunkZ); /** * Get the cached chunk set object. */ - IChunkSet getCachedSet(@Range(from = 0, to = 15) int chunkX, @Range(from = 0, to = 15) int chunkZ); + IChunkSet getCachedSet(int chunkX, int chunkZ); /** * Submit the chunk so that it's changes are applied to the world 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 59427c8ef..3f475bfeb 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 @@ -106,7 +106,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen @Override public IChunkGet getCachedGet(int chunkX, int chunkZ) { - return cacheGet.get(chunkX, chunkZ); + return processGet(cacheGet.get(chunkX, chunkZ)); } @Override @@ -186,7 +186,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen }; } 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 ae976777c..eb707aab8 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 @@ -25,13 +25,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 @@ -226,6 +238,16 @@ public class BitSetBlocks implements IChunkSet { 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 85cadd9ef..3487aa750 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 @@ -74,6 +74,8 @@ public abstract class CharBlocks implements IBlocks { 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. @@ -91,6 +93,11 @@ public abstract class CharBlocks implements IBlocks { } } + public void init(int chunkX, int chunkZ) { + this.chunkX = chunkX; + this.chunkZ = chunkZ; + } + @Override public synchronized boolean trim(boolean aggressive) { boolean result = true; @@ -196,6 +203,16 @@ public abstract class CharBlocks implements IBlocks { 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 9ab8734ea..d392d280c 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 @@ -25,14 +25,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; @@ -372,7 +388,9 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { 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 8f7b8007c..ca7c167ab 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.jnbt.CompoundTag; +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; @@ -68,6 +69,11 @@ public final class NullChunkGet implements IChunkGet { return null; } + @Nullable + public Set getFullEntities() { + return null; + } + @Override public void setCreateCopy(boolean createCopy) { } @@ -109,6 +115,16 @@ public final class NullChunkGet implements IChunkGet { 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 ca0dd3442..8f6ab06b8 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 @@ -37,6 +37,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; @@ -70,7 +72,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; @@ -86,6 +90,8 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { this.defaultOrdinal = defaultOrdinal; this.fastMode = fastMode; this.bitMask = bitMask; + this.chunkX = chunkX; + this.chunkZ = chunkZ; } @Override @@ -177,6 +183,16 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { 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)) { @@ -479,7 +495,9 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { 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 9172d8b3e..9b2c2ab1f 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 @@ -14,6 +14,7 @@ import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.Pool; import com.sk89q.jnbt.CompoundTag; +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; @@ -923,6 +924,12 @@ public class ChunkHolder> implements IQueueChunk { return delegate.get(this).getEntities(); } + @Override + public Set getFullEntities() { + checkAndWaitOnCalledLock(); + return delegate.get(this).getFullEntities(); + } + @Override public boolean hasSection(int layer) { checkAndWaitOnCalledLock(); 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 99757a513..b636cebdf 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.jnbt.CompoundTag; +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; @@ -183,6 +184,11 @@ public final class NullChunk implements IQueueChunk { return null; } + @Override + public Set getFullEntities() { + return Collections.emptySet(); + } + @Override public void setCreateCopy(boolean createCopy) { } 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 b4822863c..abac68084 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 @@ -49,10 +49,9 @@ public class ExtentTraverser { return last; } - @SuppressWarnings("unchecked") - public U findAndGet(Class clazz) { - ExtentTraverser traverser = find(clazz); - return (traverser != null) ? (U) traverser.get() : null; + public U findAndGet(Class clazz) { + ExtentTraverser traverser = find(clazz); + return (traverser != null) ? traverser.get() : null; } @SuppressWarnings("unchecked") 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 3d2a45642..8ea2fb548 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 @@ -877,7 +877,7 @@ public interface Extent extends InputExtent, OutputExtent { } 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 9959c9d67..6d11f3b0d 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.Mask; 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 @@ import static com.google.common.base.Preconditions.checkNotNull; */ 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 class ForwardExtentCopy implements Operation { 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++) { @@ -472,4 +526,40 @@ public class ForwardExtentCopy implements Operation { ); } + 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; + } + + } + }