diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightAdapter.java index 9aaeb7bc23..e7523973fb 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightAdapter.java @@ -28,6 +28,7 @@ import com.google.common.util.concurrent.Futures; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Lifecycle; +import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.NBTConstants; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItem; @@ -145,7 +146,6 @@ import org.spigotmc.SpigotConfig; import org.spigotmc.WatchdogThread; -import javax.annotation.Nullable; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -168,6 +168,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import javax.annotation.Nullable; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightDataConverters.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightDataConverters.java index be25d079a9..80bc50a06f 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightDataConverters.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightDataConverters.java @@ -49,7 +49,6 @@ import org.apache.logging.log4j.Logger; import org.enginehub.linbus.tree.LinCompoundTag; -import javax.annotation.Nullable; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.EnumMap; @@ -63,6 +62,7 @@ import java.util.UUID; import java.util.concurrent.Executor; import java.util.stream.Collectors; +import javax.annotation.Nullable; /** * Handles converting all Pre 1.13.2 data using the Legacy DataFix System (ported to 1.13.2) diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightWorldNativeAccess.java index a27f01b704..2386e69690 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightWorldNativeAccess.java @@ -37,9 +37,9 @@ import org.bukkit.event.block.BlockPhysicsEvent; import org.enginehub.linbus.tree.LinCompoundTag; -import javax.annotation.Nullable; import java.lang.ref.WeakReference; import java.util.Objects; +import javax.annotation.Nullable; public class PaperweightWorldNativeAccess implements WorldNativeAccess { private static final int UPDATE = 1; 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..3246556432 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 @@ -12,7 +12,8 @@ import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.implementation.QueueHandler; -import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArrayGetBlocks; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; @@ -91,7 +92,7 @@ import static net.minecraft.core.registries.Registries.BIOME; -public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks { +public class PaperweightGetBlocks extends DataArrayGetBlocks implements BukkitGetBlocks { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -502,7 +503,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz ); LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, - new char[4096], + DataArray.createEmpty(), adapter, biomeRegistry, biomeData @@ -515,7 +516,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz newSection, getSectionIndex )) { - updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex); + updateGet(nmsChunk, levelChunkSections, newSection, DataArray.createEmpty(), getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; @@ -545,9 +546,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to // this chunk GET when #updateGet is called. Future dords, please listen this time. - char[] tmp = set.load(layerNo); - char[] setArr = new char[tmp.length]; - System.arraycopy(tmp, 0, setArr, 0, tmp.length); + DataArray setArr = DataArray.createCopy(set.load(layerNo)); // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was // submitted to keep loaded internal chunks to queue target size. @@ -564,10 +563,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz } if (createCopy) { - char[] tmpLoad = loadPrivately(layerNo); - char[] copyArr = new char[4096]; - System.arraycopy(tmpLoad, 0, copyArr, 0, 4096); - copy.storeSection(getSectionIndex, copyArr); + copy.storeSection(getSectionIndex, DataArray.createCopy(loadPrivately(layerNo))); if (biomes != null && existingSection != null) { copy.storeBiomes(getSectionIndex, existingSection.getBiomes()); } @@ -627,10 +623,8 @@ public synchronized > T call(IChunkSet set, Runnable finaliz } else if (existingSection != getSections(false)[getSectionIndex]) { this.sections[getSectionIndex] = existingSection; this.reset(); - } else if (!Arrays.equals( - update(getSectionIndex, new char[4096], true), - loadPrivately(layerNo) - )) { + } else if (!update(getSectionIndex, DataArray.createEmpty(), true) + .equals(loadPrivately(layerNo))) { this.reset(layerNo); /*} else if (lock.isModified()) { this.reset(layerNo);*/ @@ -899,7 +893,7 @@ private void updateGet( LevelChunk nmsChunk, LevelChunkSection[] chunkSections, LevelChunkSection section, - char[] arr, + DataArray arr, int layer ) { try { @@ -924,7 +918,7 @@ private void updateGet( this.blocks[layer] = arr; } - private char[] loadPrivately(int layer) { + private DataArray loadPrivately(int layer) { layer -= getMinSectionPosition(); if (super.sections[layer] != null) { synchronized (super.sectionLocks[layer]) { @@ -955,21 +949,14 @@ public void send() { */ @Override @SuppressWarnings("unchecked") - public char[] update(int layer, char[] data, boolean aggressive) { + public DataArray update(int layer, DataArray data, boolean aggressive) { LevelChunkSection section = getSections(aggressive)[layer]; // Section is null, return empty array if (section == null) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); - return data; + return DataArray.createFilled(BlockTypesCache.ReservedIDs.AIR); } - if (data != null && data.length != 4096) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); - } - if (data == null || data == FaweCache.INSTANCE.EMPTY_CHAR_4096) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); + if (data == null || data == FaweCache.INSTANCE.EMPTY_DATA) { + data = DataArray.createFilled(BlockTypesCache.ReservedIDs.AIR); } Semaphore lock = PaperweightPlatformAdapter.applyLock(section); synchronized (lock) { @@ -982,7 +969,7 @@ public char[] update(int layer, char[] data, boolean aggressive) { final BitStorage bits = (BitStorage) PaperweightPlatformAdapter.fieldStorage.get(dataObject); if (bits instanceof ZeroBitStorage) { - Arrays.fill(data, adapter.adaptToChar(blocks.get(0, 0, 0))); // get(int) is only public on paper + data.setAll(adapter.adaptToChar(blocks.get(0, 0, 0))); // get(int) is only public on paper return data; } @@ -999,9 +986,9 @@ public char[] update(int layer, char[] data, boolean aggressive) { } else { // The section's palette is the global block palette. for (int i = 0; i < 4096; i++) { - char paletteVal = data[i]; + char paletteVal = (char) data.getAt(i); char ordinal = adapter.ibdIDToOrdinal(paletteVal); - data[i] = ordinal; + data.setAt(i, ordinal); } return data; } @@ -1014,17 +1001,17 @@ public char[] update(int layer, char[] data, boolean aggressive) { paletteToOrdinal[i] = ordinal; } for (int i = 0; i < 4096; i++) { - char paletteVal = data[i]; + char paletteVal = (char) data.getAt(i); char val = paletteToOrdinal[paletteVal]; if (val == Character.MAX_VALUE) { val = ordinal(palette.valueFor(i), adapter); paletteToOrdinal[i] = val; } - data[i] = val; + data.setAt(i, val); } } else { char ordinal = ordinal(palette.valueFor(0), adapter); - Arrays.fill(data, ordinal); + data.setAll(ordinal); } } finally { for (int i = 0; i < num_palette; i++) { 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..774791d661 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 @@ -5,6 +5,7 @@ import com.fastasyncworldedit.core.queue.IBlocks; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -28,7 +29,6 @@ import org.enginehub.linbus.tree.LinCompoundTag; import javax.annotation.Nullable; -import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -43,7 +43,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { private final Map tiles = new HashMap<>(); private final Set entities = new HashSet<>(); - private final char[][] blocks; + private final DataArray[] blocks; private final int minHeight; private final int maxHeight; final ServerLevel serverLevel; @@ -55,7 +55,7 @@ protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) { this.serverLevel = levelChunk.level; this.minHeight = serverLevel.getMinBuildHeight(); this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. - this.blocks = new char[getSectionCount()][]; + this.blocks = new DataArray[getSectionCount()]; } protected void storeTile(BlockEntity blockEntity) { @@ -161,7 +161,7 @@ public int getSectionCount() { return serverLevel.getSectionsCount(); } - protected void storeSection(int layer, char[] data) { + protected void storeSection(int layer, DataArray data) { blocks[layer] = data; } @@ -208,17 +208,16 @@ public boolean hasSection(int layer) { } @Override - public char[] load(int layer) { + public DataArray load(int layer) { layer -= getMinSectionPosition(); if (blocks[layer] == null) { - blocks[layer] = new char[4096]; - Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR); + blocks[layer] = DataArray.createFilled(BlockTypesCache.ReservedIDs.AIR); } return blocks[layer]; } @Override - public char[] loadIfPresent(int layer) { + public DataArray loadIfPresent(int layer) { layer -= getMinSectionPosition(); return blocks[layer]; } @@ -261,7 +260,7 @@ public > T call(IChunkSet set, Runnable finalize) { public char get(int x, int y, int z) { final int layer = (y >> 4) - getMinSectionPosition(); final int index = (y & 15) << 8 | z << 4 | x; - return blocks[layer][index]; + return (char) blocks[layer].getAt(index); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java index d7fab2dd64..90a41f664c 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java @@ -8,6 +8,7 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; @@ -410,7 +411,7 @@ private static List nearbyPlayers(ServerLevel serverLevel, ChunkPo */ public static LevelChunkSection newChunkSection( final int layer, - final char[] blocks, + final DataArray blocks, CachedBukkitAdapter adapter, Registry biomeRegistry, @Nullable PalettedContainer> biomes @@ -420,8 +421,8 @@ public static LevelChunkSection newChunkSection( public static LevelChunkSection newChunkSection( final int layer, - final Function get, - char[] set, + final Function get, + DataArray set, CachedBukkitAdapter adapter, Registry biomeRegistry, @Nullable PalettedContainer> biomes 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..7c01e7d586 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 @@ -1,11 +1,12 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2; +import com.fastasyncworldedit.bukkit.adapter.PostProcessor; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.processor.ProcessorScope; -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.implementation.blocks.DataArray; import com.fastasyncworldedit.core.registry.state.PropertyKey; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.world.block.BlockState; @@ -18,12 +19,7 @@ import javax.annotation.Nullable; -public class PaperweightPostProcessor implements IBatchProcessor { - - @Override - public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { - return set; - } +public class PaperweightPostProcessor extends PostProcessor { @SuppressWarnings("deprecation") @Override @@ -36,14 +32,14 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet; layer: for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) { - char[] set = iChunkSet.loadIfPresent(layer); + DataArray set = iChunkSet.loadIfPresent(layer); if (set == null) { // No edit means no need to process continue; } - char[] get = null; + DataArray get = null; for (int i = 0; i < 4096; i++) { - char ordinal = set[i]; + char ordinal = (char) set.getAt(i); char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__; boolean fromGet = false; // Used for liquids if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { @@ -56,7 +52,7 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh continue layer; } fromGet = true; - ordinal = replacedOrdinal = get[i]; + ordinal = replacedOrdinal = (char) get.getAt(i); } if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { continue; @@ -64,7 +60,7 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh if (get == null) { get = getBlocks.load(layer); } - replacedOrdinal = get[i]; + replacedOrdinal = (char) get.getAt(i); } boolean ticking = BlockTypesCache.ticking[ordinal]; boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal]; @@ -115,48 +111,6 @@ public ProcessorScope getScope() { return ProcessorScope.READING_SET_BLOCKS; } - private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) { - if (set == null || get == null) { - return false; - } - char ordinal; - char reserved = BlockTypesCache.ReservedIDs.__RESERVED__; - if (x > 0 && set[i - 1] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) { - return true; - } - } - if (x < 15 && set[i + 1] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) { - return true; - } - } - if (z > 0 && set[i - 16] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) { - return true; - } - } - if (z < 15 && set[i + 16] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) { - return true; - } - } - if (y > 0 && set[i - 256] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) { - return true; - } - } - if (y < 15 && set[i + 256] != reserved) { - return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal); - } - return false; - } - - @SuppressWarnings("deprecation") - private boolean isFluid(char ordinal) { - return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid(); - } - @SuppressWarnings("deprecation") private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) { Fluid type; diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightAdapter.java index ded4bcfdd7..a9eb86ee65 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightAdapter.java @@ -145,7 +145,6 @@ import org.spigotmc.SpigotConfig; import org.spigotmc.WatchdogThread; -import javax.annotation.Nullable; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -168,6 +167,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import javax.annotation.Nullable; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightDataConverters.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightDataConverters.java index 3a80b6a3a1..177d3158ae 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightDataConverters.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightDataConverters.java @@ -49,7 +49,6 @@ import org.apache.logging.log4j.Logger; import org.enginehub.linbus.tree.LinCompoundTag; -import javax.annotation.Nullable; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.EnumMap; @@ -63,6 +62,7 @@ import java.util.UUID; import java.util.concurrent.Executor; import java.util.stream.Collectors; +import javax.annotation.Nullable; /** * Handles converting all Pre 1.13.2 data using the Legacy DataFix System (ported to 1.13.2) diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightWorldNativeAccess.java index 1f51c66ca9..1caf8d7843 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightWorldNativeAccess.java @@ -38,9 +38,9 @@ import org.bukkit.event.block.BlockPhysicsEvent; import org.enginehub.linbus.tree.LinCompoundTag; -import javax.annotation.Nullable; import java.lang.ref.WeakReference; import java.util.Objects; +import javax.annotation.Nullable; public class PaperweightWorldNativeAccess implements WorldNativeAccess { private static final int UPDATE = 1; 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..fd0e059ef1 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 @@ -12,7 +12,8 @@ import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.implementation.QueueHandler; -import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArrayGetBlocks; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; @@ -91,7 +92,7 @@ import static net.minecraft.core.registries.Registries.BIOME; -public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks { +public class PaperweightGetBlocks extends DataArrayGetBlocks implements BukkitGetBlocks { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -502,7 +503,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz ); LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, - new char[4096], + DataArray.createEmpty(), adapter, biomeRegistry, biomeData @@ -515,7 +516,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz newSection, getSectionIndex )) { - updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex); + updateGet(nmsChunk, levelChunkSections, newSection, DataArray.createEmpty(), getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; @@ -545,9 +546,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to // this chunk GET when #updateGet is called. Future dords, please listen this time. - char[] tmp = set.load(layerNo); - char[] setArr = new char[tmp.length]; - System.arraycopy(tmp, 0, setArr, 0, tmp.length); + DataArray setArr = DataArray.createCopy(set.load(layerNo)); // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was // submitted to keep loaded internal chunks to queue target size. @@ -564,10 +563,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz } if (createCopy) { - char[] tmpLoad = loadPrivately(layerNo); - char[] copyArr = new char[4096]; - System.arraycopy(tmpLoad, 0, copyArr, 0, 4096); - copy.storeSection(getSectionIndex, copyArr); + copy.storeSection(getSectionIndex, DataArray.createCopy(loadPrivately(layerNo))); if (biomes != null && existingSection != null) { copy.storeBiomes(getSectionIndex, existingSection.getBiomes()); } @@ -627,10 +623,8 @@ public synchronized > T call(IChunkSet set, Runnable finaliz } else if (existingSection != getSections(false)[getSectionIndex]) { this.sections[getSectionIndex] = existingSection; this.reset(); - } else if (!Arrays.equals( - update(getSectionIndex, new char[4096], true), - loadPrivately(layerNo) - )) { + } else if (!update(getSectionIndex, DataArray.createEmpty(), true) + .equals(loadPrivately(layerNo))) { this.reset(layerNo); /*} else if (lock.isModified()) { this.reset(layerNo);*/ @@ -899,7 +893,7 @@ private void updateGet( LevelChunk nmsChunk, LevelChunkSection[] chunkSections, LevelChunkSection section, - char[] arr, + DataArray arr, int layer ) { try { @@ -924,7 +918,7 @@ private void updateGet( this.blocks[layer] = arr; } - private char[] loadPrivately(int layer) { + private DataArray loadPrivately(int layer) { layer -= getMinSectionPosition(); if (super.sections[layer] != null) { synchronized (super.sectionLocks[layer]) { @@ -953,21 +947,14 @@ public void send() { */ @Override @SuppressWarnings("unchecked") - public char[] update(int layer, char[] data, boolean aggressive) { + public DataArray update(int layer, DataArray data, boolean aggressive) { LevelChunkSection section = getSections(aggressive)[layer]; // Section is null, return empty array if (section == null) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); - return data; + return DataArray.createFilled(BlockTypesCache.ReservedIDs.AIR); } - if (data != null && data.length != 4096) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); - } - if (data == null || data == FaweCache.INSTANCE.EMPTY_CHAR_4096) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); + if (data == null || data == FaweCache.INSTANCE.EMPTY_DATA) { + data = DataArray.createFilled(BlockTypesCache.ReservedIDs.AIR); } Semaphore lock = PaperweightPlatformAdapter.applyLock(section); synchronized (lock) { @@ -980,7 +967,7 @@ public char[] update(int layer, char[] data, boolean aggressive) { final BitStorage bits = (BitStorage) PaperweightPlatformAdapter.fieldStorage.get(dataObject); if (bits instanceof ZeroBitStorage) { - Arrays.fill(data, adapter.adaptToChar(blocks.get(0, 0, 0))); // get(int) is only public on paper + data.setAll(adapter.adaptToChar(blocks.get(0, 0, 0))); // get(int) is only public on paper return data; } @@ -997,9 +984,9 @@ public char[] update(int layer, char[] data, boolean aggressive) { } else { // The section's palette is the global block palette. for (int i = 0; i < 4096; i++) { - char paletteVal = data[i]; + char paletteVal = (char) data.getAt(i); char ordinal = adapter.ibdIDToOrdinal(paletteVal); - data[i] = ordinal; + data.setAt(i, ordinal); } return data; } @@ -1012,17 +999,17 @@ public char[] update(int layer, char[] data, boolean aggressive) { paletteToOrdinal[i] = ordinal; } for (int i = 0; i < 4096; i++) { - char paletteVal = data[i]; + char paletteVal = (char) data.getAt(i); char val = paletteToOrdinal[paletteVal]; if (val == Character.MAX_VALUE) { val = ordinal(palette.valueFor(i), adapter); paletteToOrdinal[i] = val; } - data[i] = val; + data.setAt(i, val); } } else { char ordinal = ordinal(palette.valueFor(0), adapter); - Arrays.fill(data, ordinal); + data.setAll(ordinal); } } finally { for (int i = 0; i < num_palette; i++) { 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..3c4d36c73e 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 @@ -5,6 +5,7 @@ import com.fastasyncworldedit.core.queue.IBlocks; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -28,7 +29,6 @@ import org.enginehub.linbus.tree.LinCompoundTag; import javax.annotation.Nullable; -import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -43,7 +43,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { private final Map tiles = new HashMap<>(); private final Set entities = new HashSet<>(); - private final char[][] blocks; + private final DataArray[] blocks; private final int minHeight; private final int maxHeight; final ServerLevel serverLevel; @@ -55,7 +55,7 @@ protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) { this.serverLevel = levelChunk.level; this.minHeight = serverLevel.getMinBuildHeight(); this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. - this.blocks = new char[getSectionCount()][]; + this.blocks = new DataArray[getSectionCount()]; } protected void storeTile(BlockEntity blockEntity) { @@ -161,7 +161,7 @@ public int getSectionCount() { return serverLevel.getSectionsCount(); } - protected void storeSection(int layer, char[] data) { + protected void storeSection(int layer, DataArray data) { blocks[layer] = data; } @@ -208,17 +208,16 @@ public boolean hasSection(int layer) { } @Override - public char[] load(int layer) { + public DataArray load(int layer) { layer -= getMinSectionPosition(); if (blocks[layer] == null) { - blocks[layer] = new char[4096]; - Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR); + blocks[layer] = DataArray.createFilled(BlockTypesCache.ReservedIDs.AIR); } return blocks[layer]; } @Override - public char[] loadIfPresent(int layer) { + public DataArray loadIfPresent(int layer) { layer -= getMinSectionPosition(); return blocks[layer]; } @@ -261,7 +260,7 @@ public > T call(IChunkSet set, Runnable finalize) { public char get(int x, int y, int z) { final int layer = (y >> 4) - getMinSectionPosition(); final int index = (y & 15) << 8 | z << 4 | x; - return blocks[layer][index]; + return (char) blocks[layer].getAt(index); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java index 2c05d74662..8a867adb9b 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java @@ -8,6 +8,7 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; @@ -410,7 +411,7 @@ private static List nearbyPlayers(ServerLevel serverLevel, ChunkPo */ public static LevelChunkSection newChunkSection( final int layer, - final char[] blocks, + final DataArray blocks, CachedBukkitAdapter adapter, Registry biomeRegistry, @Nullable PalettedContainer> biomes @@ -420,8 +421,8 @@ public static LevelChunkSection newChunkSection( public static LevelChunkSection newChunkSection( final int layer, - final Function get, - char[] set, + final Function get, + DataArray set, CachedBukkitAdapter adapter, Registry biomeRegistry, @Nullable PalettedContainer> biomes 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..8fa2238bd1 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 @@ -1,13 +1,12 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; +import com.fastasyncworldedit.bukkit.adapter.PostProcessor; import com.fastasyncworldedit.core.configuration.Settings; -import com.fastasyncworldedit.core.extent.processor.ProcessorScope; -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.implementation.blocks.DataArray; import com.fastasyncworldedit.core.registry.state.PropertyKey; -import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypesCache; @@ -16,14 +15,7 @@ import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluids; -import javax.annotation.Nullable; - -public class PaperweightPostProcessor implements IBatchProcessor { - - @Override - public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { - return set; - } +public class PaperweightPostProcessor extends PostProcessor { @SuppressWarnings("deprecation") @Override @@ -36,14 +28,14 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet; layer: for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) { - char[] set = iChunkSet.loadIfPresent(layer); + DataArray set = iChunkSet.loadIfPresent(layer); if (set == null) { // No edit means no need to process continue; } - char[] get = null; + DataArray get = null; for (int i = 0; i < 4096; i++) { - char ordinal = set[i]; + char ordinal = (char) set.getAt(i); char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__; boolean fromGet = false; // Used for liquids if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { @@ -56,7 +48,7 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh continue layer; } fromGet = true; - ordinal = replacedOrdinal = get[i]; + ordinal = replacedOrdinal = (char) get.getAt(i); } if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { continue; @@ -64,7 +56,7 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh if (get == null) { get = getBlocks.load(layer); } - replacedOrdinal = get[i]; + replacedOrdinal = (char) get.getAt(i); } boolean ticking = BlockTypesCache.ticking[ordinal]; boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal]; @@ -104,59 +96,6 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh } } - @Nullable - @Override - public Extent construct(final Extent child) { - throw new UnsupportedOperationException("Processing only"); - } - - @Override - public ProcessorScope getScope() { - return ProcessorScope.READING_SET_BLOCKS; - } - - private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) { - if (set == null || get == null) { - return false; - } - char ordinal; - char reserved = BlockTypesCache.ReservedIDs.__RESERVED__; - if (x > 0 && set[i - 1] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) { - return true; - } - } - if (x < 15 && set[i + 1] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) { - return true; - } - } - if (z > 0 && set[i - 16] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) { - return true; - } - } - if (z < 15 && set[i + 16] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) { - return true; - } - } - if (y > 0 && set[i - 256] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) { - return true; - } - } - if (y < 15 && set[i + 256] != reserved) { - return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal); - } - return false; - } - - @SuppressWarnings("deprecation") - private boolean isFluid(char ordinal) { - return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid(); - } - @SuppressWarnings("deprecation") private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) { Fluid type; diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightAdapter.java index 206ade66a5..9383d2caf9 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightAdapter.java @@ -24,6 +24,7 @@ import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.util.concurrent.Futures; import com.mojang.serialization.Codec; @@ -149,7 +150,6 @@ import org.spigotmc.SpigotConfig; import org.spigotmc.WatchdogThread; -import javax.annotation.Nullable; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -172,6 +172,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import javax.annotation.Nullable; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightDataConverters.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightDataConverters.java index c566f0ae88..4403391bf2 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightDataConverters.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightDataConverters.java @@ -51,7 +51,6 @@ import org.apache.logging.log4j.Logger; import org.enginehub.linbus.tree.LinCompoundTag; -import javax.annotation.Nullable; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.EnumMap; @@ -65,6 +64,7 @@ import java.util.UUID; import java.util.concurrent.Executor; import java.util.stream.Collectors; +import javax.annotation.Nullable; /** * Handles converting all Pre 1.13.2 data using the Legacy DataFix System (ported to 1.13.2) diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightWorldNativeAccess.java index c83733bf28..dd2061ebe1 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightWorldNativeAccess.java @@ -37,9 +37,9 @@ import org.bukkit.event.block.BlockPhysicsEvent; import org.enginehub.linbus.tree.LinCompoundTag; -import javax.annotation.Nullable; import java.lang.ref.WeakReference; import java.util.Objects; +import javax.annotation.Nullable; public class PaperweightWorldNativeAccess implements WorldNativeAccess { private static final int UPDATE = 1; @@ -186,6 +186,6 @@ public void onBlockStateChange(BlockPos pos, net.minecraft.world.level.block.sta @Override public void flush() { - + } } 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..12c063674d 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 @@ -12,7 +12,8 @@ import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.implementation.QueueHandler; -import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArrayGetBlocks; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; @@ -92,7 +93,7 @@ import static net.minecraft.core.registries.Registries.BIOME; -public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks { +public class PaperweightGetBlocks extends DataArrayGetBlocks implements BukkitGetBlocks { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -503,7 +504,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz ); LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, - new char[4096], + DataArray.createEmpty(), adapter, biomeRegistry, biomeData @@ -516,7 +517,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz newSection, getSectionIndex )) { - updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex); + updateGet(nmsChunk, levelChunkSections, newSection, DataArray.createEmpty(), getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; @@ -546,9 +547,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to // this chunk GET when #updateGet is called. Future dords, please listen this time. - char[] tmp = set.load(layerNo); - char[] setArr = new char[tmp.length]; - System.arraycopy(tmp, 0, setArr, 0, tmp.length); + DataArray setArr = DataArray.createCopy(set.load(layerNo)); // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was // submitted to keep loaded internal chunks to queue target size. @@ -565,10 +564,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz } if (createCopy) { - char[] tmpLoad = loadPrivately(layerNo); - char[] copyArr = new char[4096]; - System.arraycopy(tmpLoad, 0, copyArr, 0, 4096); - copy.storeSection(getSectionIndex, copyArr); + copy.storeSection(getSectionIndex, DataArray.createCopy(loadPrivately(layerNo))); if (biomes != null && existingSection != null) { copy.storeBiomes(getSectionIndex, existingSection.getBiomes()); } @@ -628,10 +624,8 @@ public synchronized > T call(IChunkSet set, Runnable finaliz } else if (existingSection != getSections(false)[getSectionIndex]) { this.sections[getSectionIndex] = existingSection; this.reset(); - } else if (!Arrays.equals( - update(getSectionIndex, new char[4096], true), - loadPrivately(layerNo) - )) { + } else if (!update(getSectionIndex, DataArray.createEmpty(), true) + .equals(loadPrivately(layerNo))) { this.reset(layerNo); /*} else if (lock.isModified()) { this.reset(layerNo);*/ @@ -900,7 +894,7 @@ private void updateGet( LevelChunk nmsChunk, LevelChunkSection[] chunkSections, LevelChunkSection section, - char[] arr, + DataArray arr, int layer ) { try { @@ -925,7 +919,7 @@ private void updateGet( this.blocks[layer] = arr; } - private char[] loadPrivately(int layer) { + private DataArray loadPrivately(int layer) { layer -= getMinSectionPosition(); if (super.sections[layer] != null) { synchronized (super.sectionLocks[layer]) { @@ -956,21 +950,14 @@ public void send() { */ @Override @SuppressWarnings("unchecked") - public char[] update(int layer, char[] data, boolean aggressive) { + public DataArray update(int layer, DataArray data, boolean aggressive) { LevelChunkSection section = getSections(aggressive)[layer]; // Section is null, return empty array if (section == null) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); - return data; + return DataArray.createFilled(BlockTypesCache.ReservedIDs.AIR); } - if (data != null && data.length != 4096) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); - } - if (data == null || data == FaweCache.INSTANCE.EMPTY_CHAR_4096) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); + if (data == null || data == FaweCache.INSTANCE.EMPTY_DATA) { + data = DataArray.createFilled(BlockTypesCache.ReservedIDs.AIR); } Semaphore lock = PaperweightPlatformAdapter.applyLock(section); synchronized (lock) { @@ -983,7 +970,7 @@ public char[] update(int layer, char[] data, boolean aggressive) { final BitStorage bits = (BitStorage) PaperweightPlatformAdapter.fieldStorage.get(dataObject); if (bits instanceof ZeroBitStorage) { - Arrays.fill(data, adapter.adaptToChar(blocks.get(0, 0, 0))); // get(int) is only public on paper + data.setAll(adapter.adaptToChar(blocks.get(0, 0, 0))); // get(int) is only public on paper return data; } @@ -1000,9 +987,9 @@ public char[] update(int layer, char[] data, boolean aggressive) { } else { // The section's palette is the global block palette. for (int i = 0; i < 4096; i++) { - char paletteVal = data[i]; + char paletteVal = (char) data.getAt(i); char ordinal = adapter.ibdIDToOrdinal(paletteVal); - data[i] = ordinal; + data.setAt(i, ordinal); } return data; } @@ -1015,17 +1002,17 @@ public char[] update(int layer, char[] data, boolean aggressive) { paletteToOrdinal[i] = ordinal; } for (int i = 0; i < 4096; i++) { - char paletteVal = data[i]; + char paletteVal = (char) data.getAt(i); char val = paletteToOrdinal[paletteVal]; if (val == Character.MAX_VALUE) { val = ordinal(palette.valueFor(i), adapter); paletteToOrdinal[i] = val; } - data[i] = val; + data.setAt(i, val); } } else { char ordinal = ordinal(palette.valueFor(0), adapter); - Arrays.fill(data, ordinal); + data.setAll(ordinal); } } finally { for (int i = 0; i < num_palette; i++) { @@ -1199,5 +1186,4 @@ public synchronized boolean trim(boolean aggressive) { return true; } } - } 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..0b7e541b61 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 @@ -5,6 +5,7 @@ import com.fastasyncworldedit.core.queue.IBlocks; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -29,7 +30,6 @@ import org.enginehub.linbus.tree.LinCompoundTag; import javax.annotation.Nullable; -import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -44,7 +44,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { private final Map tiles = new HashMap<>(); private final Set entities = new HashSet<>(); - private final char[][] blocks; + private final DataArray[] blocks; private final int minHeight; private final int maxHeight; final ServerLevel serverLevel; @@ -56,7 +56,7 @@ protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) { this.serverLevel = levelChunk.level; this.minHeight = serverLevel.getMinBuildHeight(); this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. - this.blocks = new char[getSectionCount()][]; + this.blocks = new DataArray[getSectionCount()]; } protected void storeTile(BlockEntity blockEntity) { @@ -164,7 +164,7 @@ public int getSectionCount() { return serverLevel.getSectionsCount(); } - protected void storeSection(int layer, char[] data) { + protected void storeSection(int layer, DataArray data) { blocks[layer] = data; } @@ -211,17 +211,16 @@ public boolean hasSection(int layer) { } @Override - public char[] load(int layer) { + public DataArray load(int layer) { layer -= getMinSectionPosition(); if (blocks[layer] == null) { - blocks[layer] = new char[4096]; - Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR); + blocks[layer] = DataArray.createFilled(BlockTypesCache.ReservedIDs.AIR); } return blocks[layer]; } @Override - public char[] loadIfPresent(int layer) { + public DataArray loadIfPresent(int layer) { layer -= getMinSectionPosition(); return blocks[layer]; } @@ -264,7 +263,7 @@ public > T call(IChunkSet set, Runnable finalize) { public char get(int x, int y, int z) { final int layer = (y >> 4) - getMinSectionPosition(); final int index = (y & 15) << 8 | z << 4 | x; - return blocks[layer][index]; + return (char) blocks[layer].getAt(index); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPlatformAdapter.java index e7672ce81d..30d8316386 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPlatformAdapter.java @@ -8,6 +8,7 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; @@ -405,7 +406,7 @@ private static List nearbyPlayers(ServerLevel serverLevel, ChunkPo */ public static LevelChunkSection newChunkSection( final int layer, - final char[] blocks, + final DataArray blocks, CachedBukkitAdapter adapter, Registry biomeRegistry, @Nullable PalettedContainer> biomes @@ -415,8 +416,8 @@ public static LevelChunkSection newChunkSection( public static LevelChunkSection newChunkSection( final int layer, - final Function get, - char[] set, + final Function get, + DataArray set, CachedBukkitAdapter adapter, Registry biomeRegistry, @Nullable PalettedContainer> biomes 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..0c7a04cd26 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 @@ -1,13 +1,12 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4; +import com.fastasyncworldedit.bukkit.adapter.PostProcessor; import com.fastasyncworldedit.core.configuration.Settings; -import com.fastasyncworldedit.core.extent.processor.ProcessorScope; -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.implementation.blocks.DataArray; import com.fastasyncworldedit.core.registry.state.PropertyKey; -import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypesCache; @@ -16,14 +15,7 @@ import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluids; -import javax.annotation.Nullable; - -public class PaperweightPostProcessor implements IBatchProcessor { - - @Override - public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { - return set; - } +public class PaperweightPostProcessor extends PostProcessor { @SuppressWarnings("deprecation") @Override @@ -36,14 +28,14 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet; layer: for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) { - char[] set = iChunkSet.loadIfPresent(layer); + DataArray set = iChunkSet.loadIfPresent(layer); if (set == null) { // No edit means no need to process continue; } - char[] get = null; + DataArray get = null; for (int i = 0; i < 4096; i++) { - char ordinal = set[i]; + char ordinal = (char) set.getAt(i); char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__; boolean fromGet = false; // Used for liquids if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { @@ -56,7 +48,7 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh continue layer; } fromGet = true; - ordinal = replacedOrdinal = get[i]; + ordinal = replacedOrdinal = (char) get.getAt(i); } if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { continue; @@ -64,7 +56,7 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh if (get == null) { get = getBlocks.load(layer); } - replacedOrdinal = get[i]; + replacedOrdinal = (char) get.getAt(i); } boolean ticking = BlockTypesCache.ticking[ordinal]; boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal]; @@ -104,59 +96,6 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh } } - @Nullable - @Override - public Extent construct(final Extent child) { - throw new UnsupportedOperationException("Processing only"); - } - - @Override - public ProcessorScope getScope() { - return ProcessorScope.READING_SET_BLOCKS; - } - - private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) { - if (set == null || get == null) { - return false; - } - char ordinal; - char reserved = BlockTypesCache.ReservedIDs.__RESERVED__; - if (x > 0 && set[i - 1] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) { - return true; - } - } - if (x < 15 && set[i + 1] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) { - return true; - } - } - if (z > 0 && set[i - 16] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) { - return true; - } - } - if (z < 15 && set[i + 16] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) { - return true; - } - } - if (y > 0 && set[i - 256] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) { - return true; - } - } - if (y < 15 && set[i + 256] != reserved) { - return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal); - } - return false; - } - - @SuppressWarnings("deprecation") - private boolean isFluid(char ordinal) { - return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid(); - } - @SuppressWarnings("deprecation") private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) { Fluid type; diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightAdapter.java index 125d000bf9..8eb956daac 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightAdapter.java @@ -24,6 +24,7 @@ import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.util.concurrent.Futures; import com.mojang.serialization.Codec; 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..d092b46627 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 @@ -12,7 +12,8 @@ import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.implementation.QueueHandler; -import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArrayGetBlocks; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; @@ -92,7 +93,7 @@ import static net.minecraft.core.registries.Registries.BIOME; -public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks { +public class PaperweightGetBlocks extends DataArrayGetBlocks implements BukkitGetBlocks { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -504,7 +505,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz ); LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection( layerNo, - new char[4096], + DataArray.createEmpty(), adapter, biomeRegistry, biomeData @@ -517,7 +518,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz newSection, getSectionIndex )) { - updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex); + updateGet(nmsChunk, levelChunkSections, newSection, DataArray.createEmpty(), getSectionIndex); continue; } else { existingSection = levelChunkSections[getSectionIndex]; @@ -547,9 +548,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to // this chunk GET when #updateGet is called. Future dords, please listen this time. - char[] tmp = set.load(layerNo); - char[] setArr = new char[tmp.length]; - System.arraycopy(tmp, 0, setArr, 0, tmp.length); + DataArray setArr = DataArray.createCopy(set.load(layerNo)); // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was // submitted to keep loaded internal chunks to queue target size. @@ -563,10 +562,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz } if (createCopy) { - char[] tmpLoad = loadPrivately(layerNo); - char[] copyArr = new char[4096]; - System.arraycopy(tmpLoad, 0, copyArr, 0, 4096); - copy.storeSection(getSectionIndex, copyArr); + copy.storeSection(getSectionIndex, DataArray.createCopy(loadPrivately(layerNo))); if (biomes != null && existingSection != null) { copy.storeBiomes(getSectionIndex, existingSection.getBiomes()); } @@ -623,10 +619,8 @@ public synchronized > T call(IChunkSet set, Runnable finaliz } else if (existingSection != getSections(false)[getSectionIndex]) { this.sections[getSectionIndex] = existingSection; this.reset(); - } else if (!Arrays.equals( - update(getSectionIndex, new char[4096], true), - loadPrivately(layerNo) - )) { + } else if (! + update(getSectionIndex, DataArray.createEmpty(), true).equals(loadPrivately(layerNo))) { this.reset(layerNo); /*} else if (lock.isModified()) { this.reset(layerNo);*/ @@ -895,7 +889,7 @@ private void updateGet( LevelChunk nmsChunk, LevelChunkSection[] chunkSections, LevelChunkSection section, - char[] arr, + DataArray arr, int layer ) { try { @@ -920,7 +914,7 @@ private void updateGet( this.blocks[layer] = arr; } - private char[] loadPrivately(int layer) { + private DataArray loadPrivately(int layer) { layer -= getMinSectionPosition(); if (super.sections[layer] != null) { synchronized (super.sectionLocks[layer]) { @@ -951,21 +945,14 @@ public void send() { */ @Override @SuppressWarnings("unchecked") - public char[] update(int layer, char[] data, boolean aggressive) { + public DataArray update(int layer, DataArray data, boolean aggressive) { LevelChunkSection section = getSections(aggressive)[layer]; // Section is null, return empty array if (section == null) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); - return data; + return DataArray.createFilled(BlockTypesCache.ReservedIDs.AIR); } - if (data != null && data.length != 4096) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); - } - if (data == null || data == FaweCache.INSTANCE.EMPTY_CHAR_4096) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); + if (data == null || data == FaweCache.INSTANCE.EMPTY_DATA) { + data = DataArray.createFilled(BlockTypesCache.ReservedIDs.AIR); } Semaphore lock = PaperweightPlatformAdapter.applyLock(section); synchronized (lock) { @@ -978,7 +965,7 @@ public char[] update(int layer, char[] data, boolean aggressive) { final BitStorage bits = (BitStorage) PaperweightPlatformAdapter.fieldStorage.get(dataObject); if (bits instanceof ZeroBitStorage) { - Arrays.fill(data, adapter.adaptToChar(blocks.get(0, 0, 0))); // get(int) is only public on paper + data.setAll(adapter.adaptToChar(blocks.get(0, 0, 0))); // get(int) is only public on paper return data; } @@ -995,9 +982,9 @@ public char[] update(int layer, char[] data, boolean aggressive) { } else { // The section's palette is the global block palette. for (int i = 0; i < 4096; i++) { - char paletteVal = data[i]; + char paletteVal = (char) data.getAt(i); char ordinal = adapter.ibdIDToOrdinal(paletteVal); - data[i] = ordinal; + data.setAt(i, ordinal); } return data; } @@ -1010,17 +997,17 @@ public char[] update(int layer, char[] data, boolean aggressive) { paletteToOrdinal[i] = ordinal; } for (int i = 0; i < 4096; i++) { - char paletteVal = data[i]; + char paletteVal = (char) data.getAt(i); char val = paletteToOrdinal[paletteVal]; if (val == Character.MAX_VALUE) { val = ordinal(palette.valueFor(i), adapter); paletteToOrdinal[i] = val; } - data[i] = val; + data.setAt(i, val); } } else { char ordinal = ordinal(palette.valueFor(0), adapter); - Arrays.fill(data, ordinal); + data.setAll(ordinal); } } finally { for (int i = 0; i < num_palette; i++) { @@ -1194,5 +1181,4 @@ public synchronized boolean trim(boolean aggressive) { return true; } } - } 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..95102f75d1 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 @@ -5,6 +5,7 @@ import com.fastasyncworldedit.core.queue.IBlocks; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -29,7 +30,6 @@ import org.enginehub.linbus.tree.LinCompoundTag; import javax.annotation.Nullable; -import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -44,7 +44,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { private final Map tiles = new HashMap<>(); private final Set entities = new HashSet<>(); - private final char[][] blocks; + private final DataArray[] blocks; private final int minHeight; private final int maxHeight; final ServerLevel serverLevel; @@ -56,7 +56,7 @@ protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) { this.serverLevel = levelChunk.level; this.minHeight = serverLevel.getMinBuildHeight(); this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. - this.blocks = new char[getSectionCount()][]; + this.blocks = new DataArray[getSectionCount()]; } protected void storeTile(BlockEntity blockEntity) { @@ -164,7 +164,7 @@ public int getSectionCount() { return serverLevel.getSectionsCount(); } - protected void storeSection(int layer, char[] data) { + protected void storeSection(int layer, DataArray data) { blocks[layer] = data; } @@ -211,17 +211,16 @@ public boolean hasSection(int layer) { } @Override - public char[] load(int layer) { + public DataArray load(int layer) { layer -= getMinSectionPosition(); if (blocks[layer] == null) { - blocks[layer] = new char[4096]; - Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR); + blocks[layer] = DataArray.createFilled(BlockTypesCache.ReservedIDs.AIR); } return blocks[layer]; } @Override - public char[] loadIfPresent(int layer) { + public DataArray loadIfPresent(int layer) { layer -= getMinSectionPosition(); return blocks[layer]; } @@ -264,7 +263,7 @@ public > T call(IChunkSet set, Runnable finalize) { public char get(int x, int y, int z) { final int layer = (y >> 4) - getMinSectionPosition(); final int index = (y & 15) << 8 | z << 4 | x; - return blocks[layer][index]; + return (char) blocks[layer].getAt(index); } diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java index 662140955e..d8d6c333cc 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java @@ -9,6 +9,7 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; @@ -389,7 +390,7 @@ private static List nearbyPlayers(ServerLevel serverLevel, ChunkPo */ public static LevelChunkSection newChunkSection( final int layer, - final char[] blocks, + final DataArray blocks, CachedBukkitAdapter adapter, Registry biomeRegistry, @Nullable PalettedContainer> biomes @@ -399,8 +400,8 @@ public static LevelChunkSection newChunkSection( public static LevelChunkSection newChunkSection( final int layer, - final Function get, - char[] set, + final Function get, + DataArray set, CachedBukkitAdapter adapter, Registry biomeRegistry, @Nullable PalettedContainer> biomes 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..7f8bf08fc9 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 @@ -1,13 +1,12 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1; +import com.fastasyncworldedit.bukkit.adapter.PostProcessor; import com.fastasyncworldedit.core.configuration.Settings; -import com.fastasyncworldedit.core.extent.processor.ProcessorScope; -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.implementation.blocks.DataArray; import com.fastasyncworldedit.core.registry.state.PropertyKey; -import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypesCache; @@ -16,14 +15,7 @@ import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluids; -import javax.annotation.Nullable; - -public class PaperweightPostProcessor implements IBatchProcessor { - - @Override - public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { - return set; - } +public class PaperweightPostProcessor extends PostProcessor { @SuppressWarnings("deprecation") @Override @@ -36,14 +28,14 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet; layer: for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) { - char[] set = iChunkSet.loadIfPresent(layer); + DataArray set = iChunkSet.loadIfPresent(layer); if (set == null) { // No edit means no need to process continue; } - char[] get = null; + DataArray get = null; for (int i = 0; i < 4096; i++) { - char ordinal = set[i]; + char ordinal = (char) set.getAt(i); char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__; boolean fromGet = false; // Used for liquids if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { @@ -56,7 +48,7 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh continue layer; } fromGet = true; - ordinal = replacedOrdinal = get[i]; + ordinal = replacedOrdinal = (char) get.getAt(i); } if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { continue; @@ -64,7 +56,7 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh if (get == null) { get = getBlocks.load(layer); } - replacedOrdinal = get[i]; + replacedOrdinal = (char) get.getAt(i); } boolean ticking = BlockTypesCache.ticking[ordinal]; boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal]; @@ -104,59 +96,6 @@ public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final ICh } } - @Nullable - @Override - public Extent construct(final Extent child) { - throw new UnsupportedOperationException("Processing only"); - } - - @Override - public ProcessorScope getScope() { - return ProcessorScope.READING_SET_BLOCKS; - } - - private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) { - if (set == null || get == null) { - return false; - } - char ordinal; - char reserved = BlockTypesCache.ReservedIDs.__RESERVED__; - if (x > 0 && set[i - 1] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) { - return true; - } - } - if (x < 15 && set[i + 1] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) { - return true; - } - } - if (z > 0 && set[i - 16] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) { - return true; - } - } - if (z < 15 && set[i + 16] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) { - return true; - } - } - if (y > 0 && set[i - 256] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) { - return true; - } - } - if (y < 15 && set[i + 256] != reserved) { - return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal); - } - return false; - } - - @SuppressWarnings("deprecation") - private boolean isFluid(char ordinal) { - return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid(); - } - @SuppressWarnings("deprecation") private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) { Fluid type; diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NMSAdapter.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NMSAdapter.java index f8b240bb99..b112682e42 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NMSAdapter.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NMSAdapter.java @@ -4,6 +4,7 @@ import com.fastasyncworldedit.core.FAWEPlatformAdapterImpl; import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.queue.IChunkGet; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.ReflectionUtils; import com.sk89q.worldedit.world.block.BlockTypesCache; @@ -22,14 +23,14 @@ public static int createPalette( int[] blockToPalette, int[] paletteToBlock, int[] blocksCopy, - char[] set, + DataArray set, CachedBukkitAdapter adapter, short[] nonEmptyBlockCount ) { short nonAir = 4096; int num_palette = 0; for (int i = 0; i < 4096; i++) { - char ordinal = set[i]; + char ordinal = (char) set.getAt(i); switch (ordinal) { case BlockTypesCache.ReservedIDs.__RESERVED__ -> { ordinal = BlockTypesCache.ReservedIDs.AIR; @@ -54,7 +55,7 @@ public static int createPalette( System.arraycopy(adapter.getOrdinalToIbdID(), 0, blockToPalette, 0, adapter.getOrdinalToIbdID().length); } for (int i = 0; i < 4096; i++) { - char ordinal = set[i]; + char ordinal = (char) set.getAt(i); if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { ordinal = BlockTypesCache.ReservedIDs.AIR; } @@ -73,16 +74,16 @@ public static int createPalette( int[] blockToPalette, int[] paletteToBlock, int[] blocksCopy, - Function get, - char[] set, + Function get, + DataArray set, CachedBukkitAdapter adapter, short[] nonEmptyBlockCount ) { short nonAir = 4096; int num_palette = 0; - char[] getArr = null; + DataArray getArr = null; for (int i = 0; i < 4096; i++) { - char ordinal = set[i]; + char ordinal = (char) set.getAt(i); switch (ordinal) { case BlockTypesCache.ReservedIDs.__RESERVED__ -> { if (getArr == null) { @@ -90,7 +91,7 @@ public static int createPalette( } // write to set array as this should be a copied array, and will be important when the changes are written // to the GET chunk cached by FAWE. Future dords, actually read this comment please. - set[i] = switch (ordinal = getArr[i]) { + set.setAt(i, switch (ordinal = (char) getArr.getAt(i)) { case BlockTypesCache.ReservedIDs.__RESERVED__ -> { nonAir--; yield (ordinal = BlockTypesCache.ReservedIDs.AIR); @@ -101,7 +102,7 @@ public static int createPalette( yield ordinal; } default -> ordinal; - }; + }); } case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR -> nonAir--; } @@ -122,7 +123,7 @@ public static int createPalette( System.arraycopy(adapter.getOrdinalToIbdID(), 0, blockToPalette, 0, adapter.getOrdinalToIbdID().length); } for (int i = 0; i < 4096; i++) { - char ordinal = set[i]; + char ordinal = (char) set.getAt(i); if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { LOGGER.error("Empty (__RESERVED__) ordinal given where not expected, default to air."); ordinal = BlockTypesCache.ReservedIDs.AIR; diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/PostProcessor.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/PostProcessor.java new file mode 100644 index 0000000000..3f67f81bc2 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/PostProcessor.java @@ -0,0 +1,78 @@ +package com.fastasyncworldedit.bukkit.adapter; + +import com.fastasyncworldedit.core.extent.processor.ProcessorScope; +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.implementation.blocks.DataArray; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypesCache; + +import javax.annotation.Nullable; + +/** + * Common code for post-processing on different versions + */ +public abstract class PostProcessor implements IBatchProcessor { + + @Nullable + @Override + public Extent construct(final Extent child) { + throw new UnsupportedOperationException("Processing only"); + } + + protected boolean wasAdjacentToWater(DataArray get, DataArray set, int i, int x, int y, int z) { + if (set == null || get == null) { + return false; + } + char ordinal; + char reserved = BlockTypesCache.ReservedIDs.__RESERVED__; + if (x > 0 && set.getAt(i - 1) != reserved) { + if (BlockTypesCache.ticking[(ordinal = (char) get.getAt(i - 1))] && isFluid(ordinal)) { + return true; + } + } + if (x < 15 && set.getAt(i + 1) != reserved) { + if (BlockTypesCache.ticking[(ordinal = (char) get.getAt(i + 1))] && isFluid(ordinal)) { + return true; + } + } + if (z > 0 && set.getAt(i - 16) != reserved) { + if (BlockTypesCache.ticking[(ordinal = (char) get.getAt(i - 16))] && isFluid(ordinal)) { + return true; + } + } + if (z < 15 && set.getAt(i + 16) != reserved) { + if (BlockTypesCache.ticking[(ordinal = (char) get.getAt(i + 16))] && isFluid(ordinal)) { + return true; + } + } + if (y > 0 && set.getAt(i - 256) != reserved) { + if (BlockTypesCache.ticking[(ordinal = (char) get.getAt(i - 256))] && isFluid(ordinal)) { + return true; + } + } + if (y < 15 && set.getAt(i + 256) != reserved) { + return BlockTypesCache.ticking[(ordinal = (char) get.getAt(i + 256))] && isFluid(ordinal); + } + return false; + } + + @SuppressWarnings("deprecation") + private boolean isFluid(char ordinal) { + return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid(); + } + + @Override + public ProcessorScope getScope() { + return ProcessorScope.READING_SET_BLOCKS; + } + + @Override + public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { + return set; + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java index 3ed494298f..b6d548c11e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java @@ -2,6 +2,7 @@ import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.extent.filter.block.DataArrayFilterBlock; import com.fastasyncworldedit.core.internal.exception.FaweBlockBagException; import com.fastasyncworldedit.core.internal.exception.FaweException; import com.fastasyncworldedit.core.internal.exception.FaweException.Type; @@ -13,6 +14,7 @@ import com.fastasyncworldedit.core.queue.Pool; import com.fastasyncworldedit.core.queue.Trimable; import com.fastasyncworldedit.core.queue.implementation.QueuePool; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.collection.CleanableThreadLocal; import com.google.common.cache.CacheBuilder; @@ -77,6 +79,16 @@ public enum FaweCache implements Trimable { public final char[] EMPTY_CHAR_4096 = new char[4096]; + /** + * @since TODO + */ + public final DataArray EMPTY_DATA = DataArray.createEmpty(); + + /** + * @since TODO + */ + public final int[] EMPTY_INT_4096 = new int[4096]; + private final IdentityHashMap, Pool> REGISTERED_POOLS = new IdentityHashMap<>(); /* @@ -93,6 +105,7 @@ public synchronized boolean trim(boolean aggressive) { SECTION_BLOCKS.clean(); PALETTE_CACHE.clean(); PALETTE_TO_BLOCK_CHAR.clean(); + PALETTE_TO_BLOCK_INT.clean(); INDEX_STORE.clean(); MUTABLE_VECTOR3.clean(); @@ -144,9 +157,9 @@ public V load(@Nonnull T key) { * from the main thread, it will return a {@link Function} referencing the {@link LoadingCache} returned by * {@link FaweCache#createCache(Supplier)}. If it is called from the main thread, it will return a {@link Function} that * will always return the result of the given {@link Supplier}. It is designed to prevent issues caused by - * internally-mutable and resettable classes such as {@link com.fastasyncworldedit.core.extent.filter.block.CharFilterBlock} + * internally-mutable and resettable classes such as {@link DataArrayFilterBlock} * from causing issues when used in edits on the main thread. - * + *

* This method is designed for specific internal use. * * @param withInitial The supplier used to determine the initial value if a thread cache is created, else to provide a new @@ -262,8 +275,12 @@ public V apply(final long input) { public final CleanableThreadLocal PALETTE_TO_BLOCK_CHAR = new CleanableThreadLocal<>( () -> new char[Character.MAX_VALUE + 1], a -> { Arrays.fill(a, Character.MAX_VALUE); - } - ); + }); + + public final CleanableThreadLocal PALETTE_TO_BLOCK_INT = new CleanableThreadLocal<>( + () -> new int[Character.MAX_VALUE + 1], a -> { + Arrays.fill(a, Integer.MAX_VALUE); + }); public final CleanableThreadLocal BLOCK_STATES = new CleanableThreadLocal<>(() -> new long[2048]); @@ -296,21 +313,10 @@ public static final class Palette { /** * Convert raw char array to palette + * * @return palette */ - public Palette toPalette(int layerOffset, char[] blocks) { - return toPalette(layerOffset, null, blocks); - } - - /** - * Convert raw int array to palette - * @return palette - */ - public Palette toPalette(int layerOffset, int[] blocks) { - return toPalette(layerOffset, blocks, null); - } - - private Palette toPalette(int layerOffset, int[] blocksInts, char[] blocksChars) { + public Palette toPalette(int layerOffset, DataArray blocks) { int[] blockToPalette = BLOCK_TO_PALETTE.get(); int[] paletteToBlock = PALETTE_TO_BLOCK.get(); long[] blockStates = BLOCK_STATES.get(); @@ -320,31 +326,15 @@ private Palette toPalette(int layerOffset, int[] blocksInts, char[] blocksChars) int num_palette = 0; int blockIndexStart = layerOffset << 12; int blockIndexEnd = blockIndexStart + 4096; - if (blocksChars != null) { - for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) { - int ordinal = blocksChars[i]; - int palette = blockToPalette[ordinal]; - if (palette == Integer.MAX_VALUE) { - blockToPalette[ordinal] = palette = num_palette; - paletteToBlock[num_palette] = ordinal; - num_palette++; - } - blocksCopy[j] = palette; - } - } else if (blocksInts != null) { - for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) { - int ordinal = blocksInts[i]; - int palette = blockToPalette[ordinal]; - if (palette == Integer.MAX_VALUE) { -// BlockState state = BlockTypesCache.states[ordinal]; - blockToPalette[ordinal] = palette = num_palette; - paletteToBlock[num_palette] = ordinal; - num_palette++; - } - blocksCopy[j] = palette; + for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) { + int ordinal = blocks.getAt(i); + int palette = blockToPalette[ordinal]; + if (palette == Integer.MAX_VALUE) { + blockToPalette[ordinal] = palette = num_palette; + paletteToBlock[num_palette] = ordinal; + num_palette++; } - } else { - throw new IllegalArgumentException(); + blocksCopy[j] = palette; } for (int i = 0; i < num_palette; i++) { @@ -390,11 +380,7 @@ private Palette toPalette(int layerOffset, int[] blocksInts, char[] blocksChars) * * @return palette */ - public Palette toPaletteUnstretched(int layerOffset, char[] blocks) { - return toPaletteUnstretched(layerOffset, null, blocks); - } - - private Palette toPaletteUnstretched(int layerOffset, int[] blocksInts, char[] blocksChars) { + public Palette toPaletteUnstretched(int layerOffset, DataArray blocks) { int[] blockToPalette = BLOCK_TO_PALETTE.get(); int[] paletteToBlock = PALETTE_TO_BLOCK.get(); long[] blockStates = BLOCK_STATES.get(); @@ -404,31 +390,15 @@ private Palette toPaletteUnstretched(int layerOffset, int[] blocksInts, char[] b int num_palette = 0; int blockIndexStart = layerOffset << 12; int blockIndexEnd = blockIndexStart + 4096; - if (blocksChars != null) { - for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) { - int ordinal = blocksChars[i]; - int palette = blockToPalette[ordinal]; - if (palette == Integer.MAX_VALUE) { - blockToPalette[ordinal] = palette = num_palette; - paletteToBlock[num_palette] = ordinal; - num_palette++; - } - blocksCopy[j] = palette; + for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) { + int ordinal = blocks.getAt(i); + int palette = blockToPalette[ordinal]; + if (palette == Integer.MAX_VALUE) { + blockToPalette[ordinal] = palette = num_palette; + paletteToBlock[num_palette] = ordinal; + num_palette++; } - } else if (blocksInts != null) { - for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) { - int ordinal = blocksInts[i]; - int palette = blockToPalette[ordinal]; - if (palette == Integer.MAX_VALUE) { - // BlockState state = BlockTypesCache.states[ordinal]; - blockToPalette[ordinal] = palette = num_palette; - paletteToBlock[num_palette] = ordinal; - num_palette++; - } - blocksCopy[j] = palette; - } - } else { - throw new IllegalArgumentException(); + blocksCopy[j] = palette; } for (int i = 0; i < num_palette; i++) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java index 2773f0b98a..f6a2844187 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java @@ -6,6 +6,7 @@ import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.fastasyncworldedit.core.util.ExtentTraverser; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extension.factory.parser.DefaultBlockParser; @@ -29,6 +30,8 @@ import java.util.Set; import java.util.stream.Collectors; +import static com.sk89q.worldedit.world.block.BlockTypesCache.states; + public final class DisallowedBlocksExtent extends AbstractDelegateExtent implements IBatchProcessor { private static final BlockState RESERVED = BlockTypes.__RESERVED__.getDefaultState(); @@ -131,17 +134,17 @@ public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChun if (!set.hasSection(layer)) { continue; } - char[] blocks = Objects.requireNonNull(set.loadIfPresent(layer)); + DataArray blocks = Objects.requireNonNull(set.loadIfPresent(layer)); it: - for (int i = 0; i < blocks.length; i++) { - char block = blocks[i]; + for (int i = 0; i < DataArray.CHUNK_SECTION_SIZE; i++) { + int block = blocks.getAt(i); if (block == BlockTypesCache.ReservedIDs.__RESERVED__) { continue; } BlockState state = BlockTypesCache.states[block]; if (blockedBlocks != null) { if (blockedBlocks.contains(state.getBlockType().id())) { - blocks[i] = BlockTypesCache.ReservedIDs.__RESERVED__; + blocks.setAt(i, BlockTypesCache.ReservedIDs.__RESERVED__); continue; } } @@ -150,18 +153,18 @@ public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChun } for (FuzzyBlockState fuzzy : blockedStates) { if (fuzzy.equalsFuzzy(state)) { - blocks[i] = BlockTypesCache.ReservedIDs.__RESERVED__; + blocks.setAt(i, BlockTypesCache.ReservedIDs.__RESERVED__); continue it; } } if (remaps == null || remaps.isEmpty()) { - blocks[i] = block; + blocks.setAt(i, block); continue; } for (PropertyRemap remap : remaps) { state = remap.apply(state); } - blocks[i] = state.getOrdinalChar(); + blocks.setAt(i, state.getOrdinal()); } } return set; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/CountFilter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/CountFilter.java index 7c6767bf20..643fe82214 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/CountFilter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/CountFilter.java @@ -2,10 +2,10 @@ import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; import com.fastasyncworldedit.core.internal.simd.VectorizedFilter; -import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.Vector; import jdk.incubator.vector.VectorMask; -public class CountFilter extends ForkedFilter implements VectorizedFilter { +public class CountFilter extends ForkedFilter> implements VectorizedFilter { private int total; @@ -37,7 +37,7 @@ public int getTotal() { } @Override - public ShortVector applyVector(final ShortVector get, final ShortVector set, VectorMask mask) { + public Vector applyVector(final Vector get, final Vector set, VectorMask mask) { total += mask.trueCount(); return set; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/LinkedFilter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/LinkedFilter.java index ff57a4625d..2bde31b30d 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/LinkedFilter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/LinkedFilter.java @@ -5,7 +5,7 @@ import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IChunk; import com.sk89q.worldedit.regions.Region; -import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.Vector; import jdk.incubator.vector.VectorMask; import org.jetbrains.annotations.Nullable; @@ -70,16 +70,16 @@ public void join() { getRight().join(); } - private final static class VectorizedLinkedFilter - extends LinkedFilter implements VectorizedFilter { + private final static class VectorizedLinkedFilter, R extends VectorizedFilter, V> + extends LinkedFilter implements VectorizedFilter { public VectorizedLinkedFilter(final L left, final R right) { super(left, right); } @Override - public ShortVector applyVector(final ShortVector get, final ShortVector set, VectorMask mask) { - ShortVector res = getLeft().applyVector(get, set, mask); + public Vector applyVector(final Vector get, final Vector set, VectorMask mask) { + Vector res = getLeft().applyVector(get, set, mask); return getRight().applyVector(get, res, mask); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/MaskFilter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/MaskFilter.java index 2ce1527dfe..8c6f8c737b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/MaskFilter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/MaskFilter.java @@ -8,7 +8,7 @@ import com.fastasyncworldedit.core.queue.Filter; import com.sk89q.worldedit.function.mask.AbstractExtentMask; import com.sk89q.worldedit.function.mask.Mask; -import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.Vector; import jdk.incubator.vector.VectorMask; import jdk.incubator.vector.VectorOperators; @@ -67,9 +67,9 @@ public Filter fork() { return new MaskFilter<>(getParent().fork(), mask.copy(), changes); } - public static class VectorizedMaskFilter extends MaskFilter implements VectorizedFilter { + public static class VectorizedMaskFilter, V> extends MaskFilter implements VectorizedFilter { - private final VectorizedMask vectorizedMask; + private final VectorizedMask vectorizedMask; public VectorizedMaskFilter(final T other, final Mask root) { super(other, root); @@ -82,11 +82,11 @@ public VectorizedMaskFilter(final T other, final Mask root, AtomicInteger change } @Override - public ShortVector applyVector(final ShortVector get, final ShortVector set, VectorMask mask) { + public Vector applyVector(final Vector get, final Vector set, VectorMask mask) { final T parent = getParent(); - VectorMask masked = vectorizedMask.compareVector(set, get); - ShortVector res = parent.applyVector(get, set, mask.and(masked)); - VectorMask changed = res.compare(VectorOperators.NE, set); + VectorMask masked = vectorizedMask.compareVector(set, get); + Vector res = parent.applyVector(get, set, mask.and(masked)); + VectorMask changed = res.compare(VectorOperators.NE, set); changes.getAndAdd(changed.trueCount()); return res; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/CharFilterBlock.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/DataArrayFilterBlock.java similarity index 90% rename from worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/CharFilterBlock.java rename to worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/DataArrayFilterBlock.java index 718171efd7..f9dffa4314 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/CharFilterBlock.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/DataArrayFilterBlock.java @@ -8,7 +8,8 @@ import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.implementation.Flood; -import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArrayGetBlocks; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; @@ -30,18 +31,18 @@ import static com.sk89q.worldedit.world.block.BlockTypesCache.states; @ApiStatus.NonExtendable -public class CharFilterBlock extends ChunkFilterBlock { +public class DataArrayFilterBlock extends ChunkFilterBlock { - private static final SetDelegate FULL = (block, value) -> block.setArr[block.index] = value; + private static final SetDelegate FULL = (block, value) -> block.setArr.setAt(block.index, value); private static final SetDelegate NULL = (block, value) -> block.initSet().set(block, value); private int maxLayer; private int minLayer; - private CharGetBlocks get; + private DataArrayGetBlocks get; private IChunkSet set; - protected char[] getArr; + protected DataArray getArr; @Nullable - protected char[] setArr; + protected DataArray setArr; protected SetDelegate delegate; // local private int layer; @@ -55,7 +56,7 @@ public class CharFilterBlock extends ChunkFilterBlock { private int chunkX; private int chunkZ; - public CharFilterBlock(Extent extent) { + public DataArrayFilterBlock(Extent extent) { super(extent); } @@ -70,12 +71,12 @@ public synchronized final ChunkFilterBlock initChunk(int chunkX, int chunkZ) { @Override public synchronized final ChunkFilterBlock initLayer(IBlocks iget, IChunkSet iset, int layer) { - this.get = (CharGetBlocks) iget; + this.get = (DataArrayGetBlocks) iget; minLayer = this.get.getMinSectionPosition(); maxLayer = this.get.getMaxSectionPosition(); this.layer = layer; if (!iget.hasSection(layer)) { - getArr = FaweCache.INSTANCE.EMPTY_CHAR_4096; + getArr = FaweCache.INSTANCE.EMPTY_DATA; } else { getArr = iget.load(layer); } @@ -234,22 +235,22 @@ public final int getChunkZ() { } public final char getOrdinalChar() { - return getArr[index]; + return (char) getArr.getAt(index); } @Override public final int getOrdinal() { - return getArr[index]; + return getArr.getAt(index); } @Override public void setOrdinal(int ordinal) { - delegate.set(this, (char) ordinal); + delegate.set(this, ordinal); } @Override public final BlockState getBlock() { - final int ordinal = getArr[index]; + final int ordinal = getArr.getAt(index); return BlockTypesCache.states[ordinal]; } @@ -318,7 +319,7 @@ public boolean hasNbtData() { @Override public final BlockState getBlockNorth() { if (z > 0) { - return states[getArr[index - 16]]; + return states[getArr.getAt(index - 16)]; } return getExtent().getBlock(x(), y(), z() - 1); } @@ -326,7 +327,7 @@ public final BlockState getBlockNorth() { @Override public final BlockState getBlockEast() { if (x < 15) { - return states[getArr[index + 1]]; + return states[getArr.getAt(index + 1)]; } return getExtent().getBlock(x() + 1, y(), z()); } @@ -334,7 +335,7 @@ public final BlockState getBlockEast() { @Override public final BlockState getBlockSouth() { if (z < 15) { - return states[getArr[index + 16]]; + return states[getArr.getAt(index + 16)]; } return getExtent().getBlock(x(), y(), z() + 1); } @@ -342,7 +343,7 @@ public final BlockState getBlockSouth() { @Override public final BlockState getBlockWest() { if (x > 0) { - return states[getArr[index - 1]]; + return states[getArr.getAt(index - 1)]; } return getExtent().getBlock(x() - 1, y(), z()); } @@ -350,11 +351,11 @@ public final BlockState getBlockWest() { @Override public final BlockState getBlockBelow() { if (y > 0) { - return states[getArr[index - 256]]; + return states[getArr.getAt(index - 256)]; } if (layer > minLayer) { final int newLayer = layer - 1; - final CharGetBlocks chunk = this.get; + final DataArrayGetBlocks chunk = this.get; return states[chunk.sections[newLayer].get(chunk, newLayer, index + 3840)]; } return BlockTypes.__RESERVED__.getDefaultState(); @@ -363,11 +364,11 @@ public final BlockState getBlockBelow() { @Override public final BlockState getBlockAbove() { if (y < 16) { - return states[getArr[index + 256]]; + return states[getArr.getAt(index + 256)]; } if (layer < maxLayer) { final int newLayer = layer + 1; - final CharGetBlocks chunk = this.get; + final DataArrayGetBlocks chunk = this.get; return states[chunk.sections[newLayer].get(chunk, newLayer, index - 3840)]; } return BlockTypes.__RESERVED__.getDefaultState(); @@ -378,7 +379,7 @@ public final BlockState getBlockRelativeY(int y) { final int newY = this.y + y; final int layerAdd = newY >> 4; if (layerAdd == 0) { - return states[getArr[this.index + (y << 8)]]; + return states[getArr.getAt(this.index + (y << 8))]; } else if ((layerAdd > 0 && layerAdd < (maxLayer - layer)) || (layerAdd < 0 && layerAdd < (minLayer - layer))) { final int newLayer = layer + layerAdd; final int index = this.index + ((y & 15) << 8); @@ -431,7 +432,7 @@ public boolean setBiome(int x, int y, int z, BiomeType biome) { @ApiStatus.Internal protected interface SetDelegate { - void set(@Nonnull CharFilterBlock block, char value); + void set(@Nonnull DataArrayFilterBlock block, int value); } 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..a4fcf908a3 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 @@ -1,11 +1,11 @@ package com.fastasyncworldedit.core.extent.processor.heightmap; -import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.extent.processor.ProcessorScope; 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.implementation.blocks.DataArray; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; @@ -19,11 +19,10 @@ public class HeightmapProcessor implements IBatchProcessor { private static final int BLOCKS_PER_Y_SHIFT = 8; // log2(256) private static final int BLOCKS_PER_Y = 256; // 16 x 16 private static final boolean[] COMPLETE = new boolean[BLOCKS_PER_Y]; - private static final char[] AIR_LAYER = new char[4096]; + private static final DataArray AIR_LAYER = DataArray.createFilled(BlockTypesCache.ReservedIDs.AIR); static { Arrays.fill(COMPLETE, true); - Arrays.fill(AIR_LAYER, (char) BlockTypesCache.ReservedIDs.AIR); } private final int minY; @@ -59,21 +58,21 @@ public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { if (!(hasSectionSet || hasSectionGet)) { continue; } - char[] setSection = hasSectionSet ? set.loadIfPresent(layer) : null; - if (setSection == null || Arrays.equals(setSection, FaweCache.INSTANCE.EMPTY_CHAR_4096) || - Arrays.equals(setSection, AIR_LAYER)) { + DataArray setSection = hasSectionSet ? set.loadIfPresent(layer) : null; + if (setSection == null || setSection.isEmpty() || + setSection.equals(AIR_LAYER)) { hasSectionSet = false; } if (!hasSectionSet && !hasSectionGet) { continue; } - char[] getSection = null; + DataArray getSection = null; for (int y = 15; y >= 0; y--) { // We don't need to actually iterate over x and z as we're both reading and writing an index for (int j = 0; j < BLOCKS_PER_Y; j++) { - char ordinal = BlockTypesCache.ReservedIDs.__RESERVED__; + int ordinal = BlockTypesCache.ReservedIDs.__RESERVED__; if (hasSectionSet) { - ordinal = setSection[index(y, j)]; + ordinal = setSection.getAt(index(y, j)); } if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { if (!hasSectionGet) { @@ -84,8 +83,8 @@ public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { } else if (getSection == null) { getSection = get.load(layer); // skip empty layer - if (Arrays.equals(getSection, FaweCache.INSTANCE.EMPTY_CHAR_4096) - || Arrays.equals(getSection, AIR_LAYER)) { + if (getSection.isEmpty() + || getSection.equals(AIR_LAYER)) { hasSectionGet = false; if (!hasSectionSet) { continue layer; @@ -93,28 +92,19 @@ public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { continue; } } - ordinal = getSection[index(y, j)]; - } - // fast skip if block isn't relevant for any height map (air or empty) - if (ordinal < 4) { - continue; - } - BlockState block = BlockTypesCache.states[ordinal]; - if (block == null) { - continue; - } - for (int i = 0; i < TYPES.length; i++) { - if ((skip & (1 << i)) != 0) { - continue; // skip finished height map - } - HeightMapType type = TYPES[i]; - // ignore if that position was already set - if (!updated[i][j] && type.includes(block)) { - // mc requires + 1, heightmaps are normalized internally, thus we need to "zero" them. - heightmaps[i][j] = ((layer - get.getMinSectionPosition()) << 4) + y + 1; - updated[i][j] = true; // mark as updated - } + ordinal = getSection.getAt(index(y, j)); } + skipOrUpdateHeightmap( + get, + heightmaps, + updated, + skip, + layer, + y, + j, + BlockTypesCache.states[ordinal], + ordinal + ); } } for (int i = 0; i < updated.length; i++) { @@ -134,6 +124,38 @@ public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { return set; } + private void skipOrUpdateHeightmap( + final IChunkGet get, + final int[][] heightmaps, + final boolean[][] updated, + final int skip, + final int layer, + final int y, + final int j, + final BlockState state, + final int ordinal + ) { + // fast skip if block isn't relevant for any height map (air or empty) + if (ordinal < 4) { + return; + } + if (state == null) { + return; + } + for (int i = 0; i < TYPES.length; i++) { + if ((skip & (1 << i)) != 0) { + continue; // skip finished height map + } + HeightMapType type = TYPES[i]; + // ignore if that position was already set + if (!updated[i][j] && type.includes(state)) { + // mc requires + 1, heightmaps are normalized internally, thus we need to "zero" them. + heightmaps[i][j] = ((layer - get.getMinSectionPosition()) << 4) + y + 1; + updated[i][j] = true; // mark as updated + } + } + } + @Override @Nullable public Extent construct(Extent child) { 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..82ef31e885 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 @@ -9,6 +9,7 @@ import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.util.concurrent.Futures; @@ -166,17 +167,18 @@ public final synchronized IChunkSet processSet(IChunk chunk, IChunkGet get, IChu if (!set.hasSection(layer)) { continue; } + // add each block and tile - char[] blocksGet; - char[] tmp = get.load(layer); - if (tmp == null) { - blocksGet = FaweCache.INSTANCE.EMPTY_CHAR_4096; + DataArray blocksGet; + DataArray tmpGet = get.load(layer); + if (tmpGet == null) { + blocksGet = FaweCache.INSTANCE.EMPTY_DATA; } else { - System.arraycopy(tmp, 0, (blocksGet = new char[4096]), 0, 4096); + blocksGet = DataArray.createCopy(tmpGet); } - char[] blocksSet; // loadIfPresent shouldn't be null if set.hasSection(layer) is true - System.arraycopy(Objects.requireNonNull(set.loadIfPresent(layer)), 0, (blocksSet = new char[4096]), 0, 4096); + DataArray tmpSet = Objects.requireNonNull(set.loadIfPresent(layer)); + DataArray blocksSet = DataArray.createCopy(tmpSet); // Account for negative layers int by = layer << 4; @@ -185,10 +187,10 @@ public final synchronized IChunkSet processSet(IChunk chunk, IChunkGet get, IChu for (int z = 0; z < 16; z++) { int zz = z + bz; for (int x = 0; x < 16; x++, index++) { - final int combinedTo = blocksSet[index]; + final int combinedTo = blocksSet.getAt(index); if (combinedTo != BlockTypesCache.ReservedIDs.__RESERVED__) { int xx = bx + x; - int from = blocksGet[index]; + int from = blocksGet.getAt(index); if (from == BlockTypesCache.ReservedIDs.__RESERVED__) { from = BlockTypesCache.ReservedIDs.AIR; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/SimdSupport.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/SimdSupport.java index eefdc19297..f25959f8f0 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/SimdSupport.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/SimdSupport.java @@ -5,6 +5,7 @@ import com.fastasyncworldedit.core.function.mask.InverseMask; import com.fastasyncworldedit.core.function.mask.SingleBlockStateMask; import com.fastasyncworldedit.core.queue.Filter; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.sk89q.worldedit.function.mask.ExistingBlockMask; import com.sk89q.worldedit.function.mask.InverseSingleBlockStateMask; import com.sk89q.worldedit.function.mask.Mask; @@ -13,9 +14,10 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypesCache; -import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.Vector; import jdk.incubator.vector.VectorMask; import jdk.incubator.vector.VectorOperators; +import jdk.incubator.vector.VectorSpecies; import javax.annotation.Nullable; @@ -41,7 +43,7 @@ public static boolean useVectorApi() { return VECTOR_API_PRESENT && Settings.settings().EXPERIMENTAL.USE_VECTOR_API; } - public static @Nullable VectorizedMask vectorizedTargetMask(Mask mask) { + public static @Nullable VectorizedMask vectorizedTargetMask(Mask mask) { if (!useVectorApi()) { return null; } @@ -50,7 +52,7 @@ public static boolean useVectorApi() { case InverseSingleBlockStateMask inverse -> vectorizedTargetMaskInverse(inverse.getBlockState().getOrdinalChar()); case ExistingBlockMask ignored -> vectorizedTargetMaskNonAir(); case InverseMask inverse -> { - final VectorizedMask base = vectorizedTargetMask(inverse.inverse()); + final VectorizedMask base = vectorizedTargetMask(inverse.inverse()); if (base == null) { yield null; } @@ -60,16 +62,16 @@ public static boolean useVectorApi() { }; } - private static VectorizedMask vectorizedTargetMaskNonAir() { + private static VectorizedMask vectorizedTargetMaskNonAir() { // everything > VOID_AIR is not air return (set, get) -> get.compare(VectorOperators.UNSIGNED_GT, BlockTypesCache.ReservedIDs.VOID_AIR); } - private static VectorizedMask vectorizedTargetMask(char ordinal) { + private static VectorizedMask vectorizedTargetMask(char ordinal) { return (set, get) -> get.compare(VectorOperators.EQ, (short) ordinal); } - private static VectorizedMask vectorizedTargetMaskInverse(char ordinal) { + private static VectorizedMask vectorizedTargetMaskInverse(char ordinal) { return (set, get) -> get.compare(VectorOperators.NE, (short) ordinal); } @@ -92,7 +94,7 @@ private static VectorizedMask vectorizedTargetMaskInverse(char ordinal) { }; } - private static final class VectorizedPattern extends DelegateFilter implements VectorizedFilter { + private static final class VectorizedPattern extends DelegateFilter implements VectorizedFilter { private final char ordinal; @@ -102,9 +104,10 @@ public VectorizedPattern(final T parent, char ordinal) { } @Override - public ShortVector applyVector(final ShortVector get, final ShortVector set, VectorMask mask) { + public Vector applyVector(final Vector get, final Vector set, VectorMask mask) { // only change the lanes the mask dictates us to change, keep the rest - return set.blend(ShortVector.broadcast(ShortVector.SPECIES_PREFERRED, ordinal), mask); + VectorSpecies species = DataArray.preferredSpecies(); + return set.blend(species.broadcast(ordinal), mask); } @Override diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedCharFilterBlock.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedCharFilterBlock.java index 9f557b1347..4dd666cea9 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedCharFilterBlock.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedCharFilterBlock.java @@ -1,13 +1,14 @@ package com.fastasyncworldedit.core.internal.simd; -import com.fastasyncworldedit.core.extent.filter.block.CharFilterBlock; +import com.fastasyncworldedit.core.extent.filter.block.DataArrayFilterBlock; import com.fastasyncworldedit.core.queue.Filter; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.sk89q.worldedit.extent.Extent; -import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.Vector; import jdk.incubator.vector.VectorMask; import jdk.incubator.vector.VectorSpecies; -public class VectorizedCharFilterBlock extends CharFilterBlock { +public class VectorizedCharFilterBlock extends DataArrayFilterBlock { public VectorizedCharFilterBlock(final Extent extent) { super(extent); @@ -15,22 +16,21 @@ public VectorizedCharFilterBlock(final Extent extent) { @Override public synchronized void filter(final Filter filter) { - if (!(filter instanceof VectorizedFilter vecFilter)) { + if (!(filter instanceof VectorizedFilter vecFilter)) { throw new IllegalStateException("Unexpected VectorizedCharFilterBlock " + filter); } - final VectorSpecies species = ShortVector.SPECIES_PREFERRED; + filter(vecFilter); + } + + private void filter(VectorizedFilter vecFilter) { + final VectorSpecies species = DataArray.preferredSpecies(); // TODO can we avoid eager initSet? initSet(); // set array is null before - char[] setArr = this.setArr; + DataArray setArr = this.setArr; assert setArr != null; - char[] getArr = this.getArr; - // assume setArr.length == getArr.length == 4096 - VectorMask affectAll = species.maskAll(true); - for (int i = 0; i < 4096; i += species.length()) { - ShortVector set = ShortVector.fromCharArray(species, setArr, i); - ShortVector get = ShortVector.fromCharArray(species, getArr, i); - ShortVector res = vecFilter.applyVector(get, set, affectAll); - res.intoCharArray(setArr, i); - } + DataArray getArr = this.getArr; + VectorMask affectAll = species.maskAll(true); + setArr.processSet(getArr, (Vector set, Vector get) -> vecFilter.applyVector(get, set, affectAll)); } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedFilter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedFilter.java index b0801fcd04..f0bffaecca 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedFilter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedFilter.java @@ -1,10 +1,10 @@ package com.fastasyncworldedit.core.internal.simd; import com.fastasyncworldedit.core.queue.Filter; -import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.Vector; import jdk.incubator.vector.VectorMask; -public interface VectorizedFilter extends Filter { +public interface VectorizedFilter extends Filter { /** * Applies a filter to a vector pair of get and set. @@ -14,6 +14,6 @@ public interface VectorizedFilter extends Filter { * @param mask the mask with the lanes set to true which should be affected by the filter * @return the resulting set vector. */ - ShortVector applyVector(ShortVector get, ShortVector set, VectorMask mask); + Vector applyVector(Vector get, Vector set, VectorMask mask); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedMask.java index a6fd18b488..69b21e2117 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedMask.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedMask.java @@ -3,33 +3,26 @@ import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.sk89q.worldedit.world.block.BlockTypesCache; -import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.Vector; import jdk.incubator.vector.VectorMask; -import jdk.incubator.vector.VectorSpecies; -public interface VectorizedMask { +public interface VectorizedMask { default void processChunks(IChunk chunk, IChunkGet get, IChunkSet set) { for (int layer = get.getMinSectionPosition(); layer <= get.getMaxSectionPosition(); layer++) { - final char[] sectionSet = set.loadIfPresent(layer); + final DataArray sectionSet = set.loadIfPresent(layer); if (sectionSet == null) { continue; } - final char[] sectionGet = get.load(layer); + final DataArray sectionGet = get.load(layer); processSection(layer, sectionSet, sectionGet); } } - default void processSection(int layer, char[] set, char[] get) { - final VectorSpecies species = ShortVector.SPECIES_PREFERRED; - // assume that set.length % species.elementSize() == 0 - for (int i = 0; i < set.length; i += species.length()) { - ShortVector vectorSet = ShortVector.fromCharArray(species, set, i); - ShortVector vectorGet = ShortVector.fromCharArray(species, get, i); - vectorSet = processVector(vectorSet, vectorGet); - vectorSet.intoCharArray(set, i); - } + default void processSection(int layer, DataArray set, DataArray get) { + set.processSet(get, this::processVector); } /** @@ -38,7 +31,7 @@ default void processSection(int layer, char[] set, char[] get) { * @param set the set vector * @param get the get vector */ - default ShortVector processVector(ShortVector set, ShortVector get) { + default Vector processVector(Vector set, Vector get) { return set.blend(BlockTypesCache.ReservedIDs.__RESERVED__, compareVector(set, get).not()); } @@ -48,6 +41,6 @@ default ShortVector processVector(ShortVector set, ShortVector get) { * @param set the set vector * @param get the get vector */ - VectorMask compareVector(ShortVector set, ShortVector get); + VectorMask compareVector(Vector set, Vector get); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/BitArrayUnstretched.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/BitArrayUnstretched.java index 94f1249254..523b1bf3a8 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/BitArrayUnstretched.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/BitArrayUnstretched.java @@ -1,5 +1,6 @@ package com.fastasyncworldedit.core.math; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.fastasyncworldedit.core.util.MathMan; public final class BitArrayUnstretched { @@ -108,7 +109,7 @@ public int[] toRaw(int[] buffer) { return buffer; } - public char[] toRaw(char[] buffer) { + public DataArray toRaw(DataArray buffer) { final long[] data = this.data; final int bitsPerEntry = this.bitsPerEntry; final int maxSeqLocIndex = this.maxSeqLocIndex; @@ -118,9 +119,9 @@ public char[] toRaw(char[] buffer) { for (int i = 0; i < longLen; i++) { long l = data[i]; char lastVal; - for (; localStart <= maxSeqLocIndex && arrI < buffer.length; localStart += bitsPerEntry) { + for (; localStart <= maxSeqLocIndex && arrI < DataArray.CHUNK_SECTION_SIZE; localStart += bitsPerEntry) { lastVal = (char) (l >>> localStart & this.mask); - buffer[arrI++] = lastVal; + buffer.setAt(arrI++, lastVal); } localStart = 0; } 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 7bce208fca..1e9c0dc8dd 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 @@ -3,6 +3,7 @@ import com.fastasyncworldedit.core.extent.processor.EmptyBatchProcessor; import com.fastasyncworldedit.core.extent.processor.MultiBatchProcessor; import com.fastasyncworldedit.core.extent.processor.ProcessorScope; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.WorldEdit; @@ -79,23 +80,19 @@ default boolean trimY(IChunkSet set, int minY, int maxY, final boolean keepInsid if (layer > minLayer && layer < maxLayer) { continue; } - char[] blocks = set.loadIfPresent(layer); + DataArray blocks = set.loadIfPresent(layer); if (blocks == null) { continue; } // When on the minimum layer (as defined by minY), remove blocks up to minY (exclusive) if (layer == minLayer) { int index = (minY & 15) << 8; - for (int i = 0; i < index; i++) { - blocks[i] = BlockTypesCache.ReservedIDs.__RESERVED__; - } + blocks.setRange(0, index, BlockTypesCache.ReservedIDs.__RESERVED__); } // When on the maximum layer (as defined by maxY), remove blocks above maxY (exclusive) if (layer == maxLayer) { int index = ((maxY & 15) + 1) << 8; - for (int i = index; i < blocks.length; i++) { - blocks[i] = BlockTypesCache.ReservedIDs.__RESERVED__; - } + blocks.setRange(index, DataArray.CHUNK_SECTION_SIZE, BlockTypesCache.ReservedIDs.__RESERVED__); } set.setBlocks(layer, blocks); } @@ -125,21 +122,17 @@ default boolean trimY(IChunkSet set, int minY, int maxY, final boolean keepInsid continue; } if (layer == minLayer) { - char[] arr = set.loadIfPresent(layer); + DataArray arr = set.loadIfPresent(layer); if (arr != null) { int index = (minY & 15) << 8; - for (int i = index; i < 4096; i++) { - arr[i] = BlockTypesCache.ReservedIDs.__RESERVED__; - } + arr.setRange(index, DataArray.CHUNK_SECTION_SIZE, BlockTypesCache.ReservedIDs.__RESERVED__); } set.setBlocks(layer, arr); } else if (layer == maxLayer) { - char[] arr = set.loadIfPresent(layer); + DataArray arr = set.loadIfPresent(layer); if (arr != null) { int index = ((maxY + 1) & 15) << 8; - for (int i = 0; i < index; i++) { - arr[i] = BlockTypesCache.ReservedIDs.__RESERVED__; - } + arr.setRange(0, index, BlockTypesCache.ReservedIDs.__RESERVED__); } set.setBlocks(layer, arr); } else { 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 3a9d71da1c..e93761e047 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 @@ -5,6 +5,7 @@ import com.fastasyncworldedit.core.internal.io.FaweOutputStream; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.util.collection.AdaptedMap; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.platform.Capability; @@ -45,7 +46,7 @@ public interface IBlocks extends Trimable { * @param layer chunk section layer (may be negative) * @return char array of ordinals of the chunk section */ - char[] load(int layer); + DataArray load(int layer); /** * Obtain the specified chunk section stored as an array of ordinals if present or null. Uses normal minecraft chunk-section @@ -55,7 +56,7 @@ public interface IBlocks extends Trimable { * @return char array of ordinals of the chunk section if present */ @Nullable - char[] loadIfPresent(int layer); + DataArray loadIfPresent(int layer); BlockState getBlock(int x, int y, int z); @@ -142,14 +143,14 @@ default byte[] toByteArray(byte[] buffer, int bitMask, boolean full, boolean str continue; } - char[] ids = this.load(layer); + DataArray ids = this.load(layer); int nonEmpty = 0; // TODO optimize into same loop as toPalette - for (int i = 0; i < ids.length; i++) { - char ordinal = ids[i]; + for (int i = 0; i < DataArray.CHUNK_SECTION_SIZE; i++) { + int ordinal = ids.getAt(i); switch (ordinal) { case BlockTypesCache.ReservedIDs.__RESERVED__, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR: - ids[i] = BlockTypesCache.ReservedIDs.AIR; + ids.setAt(i, BlockTypesCache.ReservedIDs.AIR); case BlockTypesCache.ReservedIDs.AIR: continue; default: diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java index fc1482d6ec..ac3d9bdaff 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java @@ -2,6 +2,7 @@ import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.OutputExtent; @@ -33,7 +34,7 @@ default boolean setBiome(BlockVector3 position, BiomeType biome) { @Override > boolean setBlock(int x, int y, int z, T holder); - void setBlocks(int layer, char[] data); + void setBlocks(int layer, DataArray data); boolean isEmpty(); 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 33a1ec1944..02648a7d8a 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 @@ -224,8 +224,8 @@ public > int setBlocks(Region region, B block) thr @Override public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException { - VectorizedFilter vectorizedPattern = SimdSupport.vectorizedPattern(pattern); - var filter = LinkedFilter.of(vectorizedPattern == null ? pattern : vectorizedPattern, new CountFilter()); + VectorizedFilter vectorizedPattern = SimdSupport.vectorizedPattern(pattern); + var filter = LinkedFilter.of(vectorizedPattern == null ? pattern : vectorizedPattern, new CountFilter<>()); return this.changes = apply(region, filter, true).getRight().getTotal(); } 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 802561ce9e..d2ba0fbdf8 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 @@ -3,7 +3,7 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.PassthroughExtent; -import com.fastasyncworldedit.core.extent.filter.block.CharFilterBlock; +import com.fastasyncworldedit.core.extent.filter.block.DataArrayFilterBlock; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.processor.EmptyBatchProcessor; import com.fastasyncworldedit.core.extent.processor.ExtentBatchProcessorHolder; @@ -14,7 +14,7 @@ import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; -import com.fastasyncworldedit.core.queue.implementation.blocks.CharSetBlocks; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArraySetBlocks; import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkHolder; import com.fastasyncworldedit.core.queue.implementation.chunk.NullChunk; import com.fastasyncworldedit.core.util.MathMan; @@ -171,7 +171,7 @@ public synchronized void init(Extent extent, IChunkCache get, IChunkC }; } if (set == null) { - set = (x, z) -> CharSetBlocks.newInstance(); + set = (x, z) -> DataArraySetBlocks.newInstance(); } this.cacheGet = get; this.cacheSet = set; @@ -188,6 +188,7 @@ public synchronized void init(Extent extent, IChunkCache get, IChunkC } } + @Override public int size() { return chunks.size() + submissions.size(); @@ -474,7 +475,7 @@ public synchronized void flush() { @Override public ChunkFilterBlock createFilterBlock() { - return new CharFilterBlock(this); + return new DataArrayFilterBlock(this); } @Override 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..00a8b8fc85 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 @@ -54,14 +54,14 @@ public > boolean setBlock(int x, int y, int z, T h } @Override - public void setBlocks(int layer, char[] data) { + public void setBlocks(int layer, final DataArray data) { layer -= minSectionPosition; row.reset(layer); int by = layer << 4; for (int y = 0, index = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++, index++) { - if (data[index] != BlockTypesCache.ReservedIDs.__RESERVED__) { + if (data.getAt(index) != BlockTypesCache.ReservedIDs.__RESERVED__) { row.set(null, x, by + y, z, minSectionPosition, maxSectionPosition); } } @@ -124,9 +124,9 @@ public BlockState getBlock(int x, int y, int z) { } @Override - public char[] load(int layer) { + public DataArray load(int layer) { layer -= minSectionPosition; - char[] arr = FaweCache.INSTANCE.SECTION_BITS_TO_CHAR.get(); + DataArray array = DataArray.createEmpty(); MemBlockSet.IRow nullRowY = row.getRow(layer); if (nullRowY instanceof MemBlockSet.RowY rowY) { char value = blockState.getOrdinalChar(); @@ -136,14 +136,14 @@ public char[] load(int layer) { long bitBuffer = bits[longIndex]; if (bitBuffer != 0) { if (bitBuffer == -1L) { - Arrays.fill(arr, blockIndex, blockIndex + 64, value); + array.setRange(blockIndex, blockIndex + 64, value); continue; } - Arrays.fill(arr, Character.MIN_VALUE); + array.setAll(Character.MIN_VALUE); do { final long lowBit = Long.lowestOneBit(bitBuffer); final int bitIndex = Long.bitCount(lowBit - 1); - arr[blockIndex + bitIndex] = value; + array.setAt(blockIndex + bitIndex, value); bitBuffer = bitBuffer ^ lowBit; } while (bitBuffer != 0); } @@ -151,13 +151,13 @@ public char[] load(int layer) { } } } - return arr; + return array; } // No need to do anything different @Nullable @Override - public char[] loadIfPresent(final int layer) { + public DataArray loadIfPresent(final int layer) { return load(layer); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharDataArray.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharDataArray.java new file mode 100644 index 0000000000..709724428f --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharDataArray.java @@ -0,0 +1,85 @@ +package com.fastasyncworldedit.core.queue.implementation.blocks; + +import com.fastasyncworldedit.core.FaweCache; +import com.sk89q.worldedit.world.block.BlockTypesCache; +import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.Vector; +import jdk.incubator.vector.VectorSpecies; + +import java.util.Arrays; +import java.util.function.BinaryOperator; + +final class CharDataArray implements DataArray { + static final boolean CAN_USE_CHAR_ARRAY = Boolean.getBoolean("fawe.test") || BlockTypesCache.states.length < Character.MAX_VALUE; + private final char[] data; + + public CharDataArray() { + this.data = new char[CHUNK_SECTION_SIZE]; + } + + @Override + public int getAt(final int index) { + return this.data[index]; + } + + @Override + public void setAt(final int index, final int value) { + this.data[index] = (char) value; + } + + @Override + public void setRange(final int start, final int end, final int value) { + Arrays.fill(this.data, start, end, (char) value); + } + + @Override + public void setAll(final int value) { + Arrays.fill(this.data, (char) value); + } + + @Override + public void copyInto(final DataArray other) { + assert other.getClass() == CharDataArray.class; + final char[] otherData = ((CharDataArray) other).data; + System.arraycopy(this.data, 0, otherData, 0, CHUNK_SECTION_SIZE); + } + + @Override + public boolean isEmpty() { + return Arrays.equals(this.data, FaweCache.INSTANCE.EMPTY_CHAR_4096); + } + + @Override + public void processSet(final DataArray get, final BinaryOperator> op) { + // TODO is this sane??? + BinaryOperator cOp = cast(op); + VectorSpecies species = ShortVector.SPECIES_PREFERRED; + CharDataArray setArr = this; + CharDataArray getArr = (CharDataArray) get; + for (int i = 0; i < CHUNK_SECTION_SIZE; i += species.length()) { + ShortVector vectorSet = ShortVector.fromCharArray(species, setArr.data, i); + ShortVector vectorGet = ShortVector.fromCharArray(species, getArr.data, i); + vectorSet = cOp.apply(vectorSet, vectorGet); + vectorSet.intoCharArray(setArr.data, i); + } + } + + @Override + public boolean equals(final Object obj) { + if (!(obj instanceof CharDataArray other)) { + return false; + } + return Arrays.equals(this.data, other.data); + } + + @Override + public int hashCode() { + return Arrays.hashCode(this.data); + } + + @SuppressWarnings("unchecked") + private static BinaryOperator cast(final BinaryOperator> op) { + return (BinaryOperator) op; + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ChunkSectionedChunk.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ChunkSectionedChunk.java new file mode 100644 index 0000000000..90105f7ed9 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ChunkSectionedChunk.java @@ -0,0 +1,45 @@ +package com.fastasyncworldedit.core.queue.implementation.blocks; + +import com.sk89q.worldedit.world.biome.BiomeType; + +public class ChunkSectionedChunk { + + public Object[] sectionLocks; + protected int minSectionPosition; + protected int maxSectionPosition; + protected int sectionCount; + + static BiomeType getBiomeType( + final int x, + final int y, + final int z, + final BiomeType[][] biomes, + final int minSectionPosition, + final int maxSectionPosition + ) { + int layer; + if (biomes == null || (y >> 4) < minSectionPosition || (y >> 4) > maxSectionPosition) { + return null; + } else if (biomes[(layer = (y >> 4) - minSectionPosition)] == null) { + return null; + } + return biomes[layer][(y & 15) >> 2 | (z >> 2) << 2 | x >> 2]; + } + + /** + * Get the number of stored sections + */ + public int getSectionCount() { + return sectionCount; + } + + public int getMaxSectionPosition() { + return maxSectionPosition; + } + + public int getMinSectionPosition() { + return minSectionPosition; + } + + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/DataArray.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/DataArray.java new file mode 100644 index 0000000000..2060bbb2eb --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/DataArray.java @@ -0,0 +1,112 @@ +package com.fastasyncworldedit.core.queue.implementation.blocks; + +import jdk.incubator.vector.IntVector; +import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.Vector; +import jdk.incubator.vector.VectorSpecies; + +import java.util.function.BinaryOperator; + +/** + * This interface represents the block states stored in a chunk section. + * It provides methods for efficient bulk operations. + */ +public sealed interface DataArray permits CharDataArray, IntDataArray { + + /** + * The amount of entries in a {@link DataArray}. + */ + int CHUNK_SECTION_SIZE = 16 * 16 * 16; + + /** + * Creates a new {@link DataArray} with all entries set to {@code 0}. + * + * @return an empty {@link DataArray}. + */ + static DataArray createEmpty() { + if (CharDataArray.CAN_USE_CHAR_ARRAY) { + return new CharDataArray(); + } + return new IntDataArray(); + } + + @SuppressWarnings("unchecked") + static VectorSpecies preferredSpecies() { + if (CharDataArray.CAN_USE_CHAR_ARRAY) { + return (VectorSpecies) ShortVector.SPECIES_PREFERRED; + } + return (VectorSpecies) IntVector.SPECIES_PREFERRED; + } + + /** + * @param value the value to set all entries to. + * {@return a {@link DataArray} with all entries set to the given value} + */ + static DataArray createFilled(int value) { + final DataArray array = createEmpty(); + array.setAll(value); + return array; + } + + /** + * @param other the {@link DataArray} to copy. + * @return a copy of the given {@link DataArray}. + */ + static DataArray createCopy(DataArray other) { + final DataArray array = createEmpty(); + other.copyInto(array); + return array; + } + + /** + * @param index the index to look up. + * @return the value at the given index. + */ + int getAt(int index); + + /** + * Sets the value at the given index to the given value. + * + * @param index the index to set. + * @param value the value to set at the index. + * @throws IndexOutOfBoundsException if {@code index > } {@value CHUNK_SECTION_SIZE} + * or {@code index < 0}. + */ + void setAt(int index, int value); + + /** + * Sets all values in the given range to the given value. + * + * @param start the start of the range to set, inclusive. + * @param end the end of the range to set, exclusive + * @param value the value to set all entries in the given range to. + * @throws IndexOutOfBoundsException if {@code start > } {@value CHUNK_SECTION_SIZE} + * or {@code start < 0} + * or {@code end + 1 > } {@value CHUNK_SECTION_SIZE} + * or {@code end < 0}. + */ + void setRange(int start, int end, int value); + + /** + * Sets all entries to the given value. + * This is equivalent to calling {@link #setRange(int, int, int) setRange(0, CHUNK_SECTION_SIZE, value) } + * + * @param value the value to set all entries to. + */ + void setAll(int value); + + /** + * Copies the data from this array into {@code other}. + * + * @param other the {@link DataArray} to copy the values from this array into. + */ + void copyInto(DataArray other); + + /** + * {@return {@code true} if all values are {@code 0}} + */ + boolean isEmpty(); + + void processSet(DataArray get, BinaryOperator> op); + +} 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/DataArrayBlocks.java similarity index 76% rename from worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java rename to worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/DataArrayBlocks.java index c338033f0a..b2dd857a26 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/DataArrayBlocks.java @@ -10,14 +10,14 @@ import javax.annotation.Nullable; -public abstract class CharBlocks implements IBlocks { +public abstract class DataArrayBlocks extends ChunkSectionedChunk implements IBlocks { private static final Logger LOGGER = LogManagerCompat.getLogger(); protected static final Section FULL = new Section() { @Override - public char[] get(CharBlocks blocks, int layer) { - char[] arr = blocks.blocks[layer]; + public DataArray get(DataArrayBlocks blocks, int layer) { + DataArray arr = blocks.blocks[layer]; if (arr == null) { // Chunk probably trimmed mid-operations, but do nothing about it to avoid other issues return EMPTY.get(blocks, layer, false); @@ -27,8 +27,8 @@ public char[] get(CharBlocks blocks, int layer) { // Ignore aggressive switch here. @Override - public char[] get(CharBlocks blocks, int layer, boolean aggressive) { - char[] arr = blocks.blocks[layer]; + public DataArray get(DataArrayBlocks blocks, int layer, boolean aggressive) { + DataArray arr = blocks.blocks[layer]; if (arr == null) { // Chunk probably trimmed mid-operations, but do nothing about it to avoid other issues synchronized (blocks.sectionLocks[layer]) { @@ -45,13 +45,13 @@ public boolean isFull() { }; protected static final Section EMPTY = new Section() { @Override - public char[] get(CharBlocks blocks, int layer) { + public DataArray get(DataArrayBlocks blocks, int layer) { // Defaults to aggressive as it should only be avoided where we know we've reset a chunk during an edit return get(blocks, layer, true); } @Override - public char[] get(CharBlocks blocks, int layer, boolean aggressive) { + public DataArray get(DataArrayBlocks blocks, int layer, boolean aggressive) { synchronized (blocks.sectionLocks[layer]) { if (blocks.sections[layer] == FULL) { return FULL.get(blocks, layer); @@ -65,21 +65,17 @@ public boolean isFull() { return false; } }; - public char[][] blocks; + public DataArray[] blocks; public Section[] sections; - public Object[] sectionLocks; - protected int minSectionPosition; - protected int maxSectionPosition; - protected int sectionCount; /** * New instance given initial min/max section indices. Can be negative. */ - public CharBlocks(int minSectionPosition, int maxSectionPosition) { + public DataArrayBlocks(int minSectionPosition, int maxSectionPosition) { this.minSectionPosition = minSectionPosition; this.maxSectionPosition = maxSectionPosition; this.sectionCount = maxSectionPosition - minSectionPosition + 1; - blocks = new char[sectionCount][]; + blocks = new DataArray[sectionCount]; sections = new Section[sectionCount]; sectionLocks = new Object[sectionCount]; for (int i = 0; i < sectionCount; i++) { @@ -130,13 +126,11 @@ public void reset(int layer) { } } - public char[] update(int layer, char[] data, boolean aggressive) { + public DataArray update(int layer, DataArray data, boolean aggressive) { if (data == null) { - return new char[4096]; - } - for (int i = 0; i < 4096; i++) { - data[i] = defaultOrdinal(); + return DataArray.createEmpty(); } + data.setAll(defaultOrdinal()); return data; } @@ -148,7 +142,7 @@ public boolean hasSection(int layer) { } @Override - public char[] load(int layer) { + public DataArray load(int layer) { layer -= minSectionPosition; synchronized (sectionLocks[layer]) { return sections[layer].get(this, layer); @@ -157,7 +151,7 @@ public char[] load(int layer) { @Nullable @Override - public char[] loadIfPresent(int layer) { + public DataArray loadIfPresent(int layer) { if (layer < minSectionPosition || layer > maxSectionPosition) { return null; } @@ -165,27 +159,12 @@ public char[] loadIfPresent(int layer) { return sections[layer].isFull() ? blocks[layer] : null; } - @Override - public int getSectionCount() { - return sectionCount; - } - - @Override - public int getMaxSectionPosition() { - return maxSectionPosition; - } - - @Override - public int getMinSectionPosition() { - return minSectionPosition; - } - @Override public BlockState getBlock(int x, int y, int z) { return BlockTypesCache.states[get(x, y, z)]; } - public char get(int x, int y, int z) { + public int get(int x, int y, int z) { int layer = y >> 4; final int index = (y & 15) << 8 | z << 4 | x; if (layer > maxSectionPosition || layer < minSectionPosition) { @@ -200,7 +179,7 @@ public char get(int x, int y, int z) { protected abstract char defaultOrdinal(); // Not synchronized as it refers to a synchronized method and includes nothing that requires synchronization - public void set(int x, int y, int z, char value) { + public void set(int x, int y, int z, int value) { final int layer = y >> 4; final int index = (y & 15) << 8 | z << 4 | x; try { @@ -216,41 +195,41 @@ public void set(int x, int y, int z, char value) { Section */ - public final char get(int layer, int index) { + public final int get(int layer, int index) { return sections[layer - minSectionPosition].get(this, layer, index); } - public final void set(int layer, int index, char value) throws ArrayIndexOutOfBoundsException { + public final void set(int layer, int index, int value) throws ArrayIndexOutOfBoundsException { sections[layer - minSectionPosition].set(this, layer, index, value); } public abstract static class Section { - abstract char[] get(CharBlocks blocks, int layer); + abstract DataArray get(DataArrayBlocks blocks, int layer); - abstract char[] get(CharBlocks blocks, int layer, boolean aggressive); + abstract DataArray get(DataArrayBlocks blocks, int layer, boolean aggressive); public abstract boolean isFull(); - public final char get(CharBlocks blocks, int layer, int index) { + public final int get(DataArrayBlocks blocks, int layer, int index) { int normalized = layer - blocks.minSectionPosition; - char[] section = get(blocks, normalized); + DataArray section = get(blocks, normalized); if (section == null) { synchronized (blocks.sectionLocks[normalized]) { blocks.reset(layer); section = EMPTY.get(blocks, normalized, false); } } - return section[index]; + return section.getAt(index); } - public final synchronized void set(CharBlocks blocks, int layer, int index, char value) { + public final synchronized void set(DataArrayBlocks blocks, int layer, int index, int value) { layer -= blocks.minSectionPosition; - get(blocks, layer)[index] = value; + get(blocks, layer).setAt(index, value); } - static char[] getSkipFull(CharBlocks blocks, int layer, boolean aggressive) { - char[] arr = blocks.blocks[layer]; + static DataArray getSkipFull(DataArrayBlocks blocks, int layer, boolean aggressive) { + DataArray arr = blocks.blocks[layer]; if (arr == null) { arr = blocks.blocks[layer] = blocks.update(layer, null, aggressive); if (arr == null) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharGetBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/DataArrayGetBlocks.java similarity index 79% rename from worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharGetBlocks.java rename to worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/DataArrayGetBlocks.java index cd1c1b76f4..2a4b673371 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharGetBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/DataArrayGetBlocks.java @@ -7,14 +7,12 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; -import java.util.Arrays; - -public abstract class CharGetBlocks extends CharBlocks implements IChunkGet { +public abstract class DataArrayGetBlocks extends DataArrayBlocks implements IChunkGet { /** * New instance given the min/max section indices */ - public CharGetBlocks(final int minSectionPosition, final int maxSectionPosition) { + public DataArrayGetBlocks(final int minSectionPosition, final int maxSectionPosition) { super(minSectionPosition, maxSectionPosition); } @@ -34,11 +32,11 @@ public synchronized boolean trim(boolean aggressive) { } @Override - public char[] update(int layer, char[] data, boolean aggressive) { + public DataArray update(int layer, DataArray data, boolean aggressive) { if (data == null) { - data = new char[4096]; + data = DataArray.createEmpty(); } - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); + data.setAll(BlockTypesCache.ReservedIDs.AIR); return data; } @@ -60,5 +58,4 @@ public IChunkSet reset() { super.reset(); return null; } - } 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/DataArraySetBlocks.java similarity index 93% rename from worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java rename to worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/DataArraySetBlocks.java index 8d7c059edf..c69c59bf5d 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/DataArraySetBlocks.java @@ -23,15 +23,15 @@ import java.util.Set; import java.util.UUID; -public class CharSetBlocks extends CharBlocks implements IChunkSet { +public class DataArraySetBlocks extends DataArrayBlocks implements IChunkSet { - private static final Pool POOL = FaweCache.INSTANCE.registerPool( - CharSetBlocks.class, - CharSetBlocks::new, + private static final Pool POOL = FaweCache.INSTANCE.registerPool( + DataArraySetBlocks.class, + DataArraySetBlocks::new, Settings.settings().QUEUE.POOL ); - public static CharSetBlocks newInstance() { + public static DataArraySetBlocks newInstance() { return POOL.poll(); } @@ -45,7 +45,7 @@ public static CharSetBlocks newInstance() { private boolean fastMode = false; private int bitMask = -1; - private CharSetBlocks() { + private DataArraySetBlocks() { // Expand as we go super(0, 15); } @@ -63,13 +63,7 @@ public BiomeType[][] getBiomes() { @Override public BiomeType getBiomeType(int x, int y, int z) { - int layer; - if (biomes == null || (y >> 4) < minSectionPosition || (y >> 4) > maxSectionPosition) { - return null; - } else if (biomes[(layer = (y >> 4) - minSectionPosition)] == null) { - return null; - } - return biomes[layer][(y & 15) >> 2 | (z >> 2) << 2 | x >> 2]; + return getBiomeType(x, y, z, biomes, minSectionPosition, maxSectionPosition); } @Override @@ -120,7 +114,7 @@ public > boolean setBlock(int x, int y, int z, T h } @Override - public void setBlocks(int layer, char[] data) { + public void setBlocks(int layer, final DataArray data) { updateSectionIndexRange(layer); layer -= minSectionPosition; this.sections[layer] = data == null ? EMPTY : FULL; @@ -339,11 +333,10 @@ public boolean hasBiomes(int layer) { @Override public ThreadUnsafeCharBlocks createCopy() { - char[][] blocksCopy = new char[sectionCount][]; + DataArray[] blocksCopy = new DataArray[sectionCount]; for (int i = 0; i < sectionCount; i++) { if (blocks[i] != null) { - blocksCopy[i] = new char[FaweCache.INSTANCE.BLOCKS_PER_LAYER]; - System.arraycopy(blocks[i], 0, blocksCopy[i], 0, FaweCache.INSTANCE.BLOCKS_PER_LAYER); + blocksCopy[i] = DataArray.createCopy(blocks[i]); } } BiomeType[][] biomesCopy; @@ -394,7 +387,7 @@ static char[][] createLightCopy(char[][] lightArr, int sectionCount) { } @Override - public char[] load(final int layer) { + public DataArray load(final int layer) { updateSectionIndexRange(layer); return super.load(layer); } @@ -423,7 +416,7 @@ private void updateSectionIndexRange(int layer) { } private void resizeSectionsArrays(int diff, boolean appendNew) { - char[][] tmpBlocks = new char[sectionCount][]; + DataArray[] tmpBlocks = new DataArray[sectionCount]; Section[] tmpSections = new Section[sectionCount]; Object[] tmpSectionLocks = new Object[sectionCount]; int destPos = appendNew ? 0 : diff; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/IntDataArray.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/IntDataArray.java new file mode 100644 index 0000000000..fe6757ee6c --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/IntDataArray.java @@ -0,0 +1,84 @@ +package com.fastasyncworldedit.core.queue.implementation.blocks; + +import com.fastasyncworldedit.core.FaweCache; +import jdk.incubator.vector.IntVector; +import jdk.incubator.vector.Vector; +import jdk.incubator.vector.VectorSpecies; + +import java.util.Arrays; +import java.util.function.BinaryOperator; + +final class IntDataArray implements DataArray { + private final int[] data; + + public IntDataArray() { + this.data = new int[CHUNK_SECTION_SIZE]; + } + + @Override + public int getAt(final int index) { + return this.data[index]; + } + + @Override + public void setAt(final int index, final int value) { + this.data[index] = value; + } + + @Override + public void setRange(final int start, final int end, final int value) { + Arrays.fill(this.data, start, end, value); + } + + @Override + public void setAll(final int value) { + Arrays.fill(this.data, value); + } + + @Override + public void copyInto(final DataArray other) { + assert other.getClass() == IntDataArray.class; + final int[] otherData = ((IntDataArray) other).data; + System.arraycopy(this.data, 0, otherData, 0, CHUNK_SECTION_SIZE); + } + + @Override + public boolean isEmpty() { + return Arrays.equals(this.data, FaweCache.INSTANCE.EMPTY_INT_4096); + } + + @Override + public void processSet(final DataArray get, final BinaryOperator> op) { + // TODO is this sane??? + BinaryOperator cOp = cast(op); + VectorSpecies species = IntVector.SPECIES_PREFERRED; + IntDataArray setArr = this; + IntDataArray getArr = (IntDataArray) get; + for (int i = 0; i < CHUNK_SECTION_SIZE; i += species.length()) { + IntVector vectorSet = IntVector.fromArray(species, setArr.data, i); + IntVector vectorGet = IntVector.fromArray(species, getArr.data, i); + vectorSet = cOp.apply(vectorSet, vectorGet); + vectorSet.intoArray(setArr.data, i); + } + + } + + @Override + public boolean equals(final Object obj) { + if (!(obj instanceof IntDataArray other)) { + return false; + } + return Arrays.equals(this.data, other.data); + } + + @Override + public int hashCode() { + return Arrays.hashCode(this.data); + } + + @SuppressWarnings("unchecked") + private static BinaryOperator cast(final BinaryOperator> op) { + return (BinaryOperator) op; + } + +} 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..605b2a9a45 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 @@ -124,13 +124,13 @@ public > T call(@Nonnull IChunkSet set, @Nonnull Runnable fi } @Nonnull - public char[] load(int layer) { - return FaweCache.INSTANCE.EMPTY_CHAR_4096; + public DataArray load(int layer) { + return FaweCache.INSTANCE.EMPTY_DATA; } @Nullable @Override - public char[] loadIfPresent(final int layer) { + public DataArray loadIfPresent(final int layer) { return null; } 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..323cabff6d 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 @@ -1,7 +1,6 @@ package com.fastasyncworldedit.core.queue.implementation.blocks; import com.fastasyncworldedit.core.Fawe; -import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.math.BlockVector3ChunkMap; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; @@ -27,8 +26,8 @@ import java.util.UUID; /** - * Equivalent to {@link CharSetBlocks} without any attempt to make thread-safe for improved performance. - * This is currently only used as a "copy" of {@link CharSetBlocks} to provide to + * Equivalent to {@link DataArraySetBlocks} without any attempt to make thread-safe for improved performance. + * This is currently only used as a "copy" of {@link DataArraySetBlocks} to provide to * {@link com.fastasyncworldedit.core.queue.IBatchProcessor} instances for processing without overlapping the continuing edit. * * @since 2.6.2 @@ -38,7 +37,7 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { private static final Logger LOGGER = LogManagerCompat.getLogger(); private final char defaultOrdinal; - private char[][] blocks; + private DataArray[] blocks; private int minSectionPosition; private int maxSectionPosition; private int sectionCount; @@ -53,12 +52,12 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { private int bitMask; /** - * New instance given the data stored in a {@link CharSetBlocks} instance. + * New instance given the data stored in a {@link DataArraySetBlocks} instance. * * @since 2.6.2 */ ThreadUnsafeCharBlocks( - char[][] blocks, + DataArray[] blocks, int minSectionPosition, int maxSectionPosition, BiomeType[][] biomes, @@ -92,23 +91,23 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { @Override public boolean hasSection(int layer) { layer -= minSectionPosition; - return layer >= 0 && layer < blocks.length && blocks[layer] != null && blocks[layer].length == FaweCache.INSTANCE.BLOCKS_PER_LAYER; + return layer >= 0 && layer < blocks.length && blocks[layer] != null; } @Override - public char[] load(int layer) { + public DataArray load(int layer) { updateSectionIndexRange(layer); layer -= minSectionPosition; - char[] arr = blocks[layer]; + DataArray arr = blocks[layer]; if (arr == null) { - arr = blocks[layer] = new char[FaweCache.INSTANCE.BLOCKS_PER_LAYER]; + arr = blocks[layer] = DataArray.createEmpty(); } return arr; } @Nullable @Override - public char[] loadIfPresent(int layer) { + public DataArray loadIfPresent(int layer) { if (layer < minSectionPosition || layer > maxSectionPosition) { return null; } @@ -184,18 +183,12 @@ public char get(int x, int y, int z) { return defaultOrdinal; } final int index = (y & 15) << 8 | z << 4 | x; - return blocks[layer - minSectionPosition][index]; + return (char) blocks[layer - minSectionPosition].getAt(index); } @Override public BiomeType getBiomeType(int x, int y, int z) { - int layer; - if (biomes == null || (y >> 4) < minSectionPosition || (y >> 4) > maxSectionPosition) { - return null; - } else if (biomes[(layer = (y >> 4) - minSectionPosition)] == null) { - return null; - } - return biomes[layer][(y & 15) >> 2 | (z >> 2) << 2 | x >> 2]; + return ChunkSectionedChunk.getBiomeType(x, y, z, biomes, minSectionPosition, maxSectionPosition); } @Override @@ -226,7 +219,7 @@ public void set(int x, int y, int z, char value) { final int layer = (y >> 4) - minSectionPosition; final int index = (y & 15) << 8 | z << 4 | x; try { - blocks[layer][index] = value; + blocks[layer].setAt(index, value); } catch (ArrayIndexOutOfBoundsException exception) { LOGGER.error("Tried setting block at coordinates (" + x + "," + y + "," + z + ")"); assert Fawe.platform() != null; @@ -243,7 +236,7 @@ public > boolean setBlock(int x, int y, int z, T h } @Override - public void setBlocks(int layer, char[] data) { + public void setBlocks(int layer, DataArray data) { updateSectionIndexRange(layer); layer -= minSectionPosition; this.blocks[layer] = data; @@ -426,7 +419,7 @@ public boolean hasLight() { @Override public IChunkSet reset() { - blocks = new char[sectionCount][]; + blocks = new DataArray[sectionCount]; biomes = new BiomeType[sectionCount][]; light = new char[sectionCount][]; skyLight = new char[sectionCount][]; @@ -445,11 +438,12 @@ public boolean hasBiomes(int layer) { @Override public IChunkSet createCopy() { - char[][] blocksCopy = new char[sectionCount][]; + DataArray[] blocksCopy = new DataArray[sectionCount]; for (int i = 0; i < sectionCount; i++) { - blocksCopy[i] = new char[FaweCache.INSTANCE.BLOCKS_PER_LAYER]; if (blocks[i] != null) { - System.arraycopy(blocks[i], 0, blocksCopy[i], 0, FaweCache.INSTANCE.BLOCKS_PER_LAYER); + blocksCopy[i] = DataArray.createCopy(blocks[i]); + } else { + blocksCopy[i] = DataArray.createEmpty(); } } BiomeType[][] biomesCopy; @@ -464,8 +458,8 @@ public IChunkSet createCopy() { } } } - char[][] lightCopy = CharSetBlocks.createLightCopy(light, sectionCount); - char[][] skyLightCopy = CharSetBlocks.createLightCopy(skyLight, sectionCount); + char[][] lightCopy = DataArraySetBlocks.createLightCopy(light, sectionCount); + char[][] skyLightCopy = DataArraySetBlocks.createLightCopy(skyLight, sectionCount); return new ThreadUnsafeCharBlocks( blocksCopy, minSectionPosition, @@ -508,7 +502,7 @@ private void updateSectionIndexRange(int layer) { } private void resizeSectionsArrays(int layer, int diff, boolean appendNew) { - char[][] tmpBlocks = new char[sectionCount][]; + DataArray[] tmpBlocks = new DataArray[sectionCount]; int destPos = appendNew ? 0 : diff; System.arraycopy(blocks, 0, tmpBlocks, destPos, blocks.length); blocks = tmpBlocks; 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 5e951cd2bf..ee21a1d6ed 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.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; @@ -108,18 +109,18 @@ public char[][] getSkyLight() { } @Override - public void setBlocks(int layer, char[] data) { + public void setBlocks(int layer, DataArray data) { delegate.set(this).setBlocks(layer, data); } @Override - public char[] load(int layer) { + public DataArray load(int layer) { return getOrCreateGet().load(layer); } @Nullable @Override - public char[] loadIfPresent(final int layer) { + public DataArray loadIfPresent(final int layer) { if (chunkExisting == null) { return null; } 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..a395543b72 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.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.biome.BiomeType; @@ -133,7 +134,8 @@ public > boolean setBlock(int x, int y, int z, T h return false; } - public void setBlocks(int layer, @Nonnull char[] data) { + @Override + public void setBlocks(int layer, @Nonnull DataArray data) { } @Nullable @@ -172,13 +174,13 @@ public BaseBlock getFullBlock(int x, int y, int z) { @Nullable - public char[] load(int layer) { + public DataArray load(int layer) { return null; } @Nullable @Override - public char[] loadIfPresent(final int layer) { + public DataArray loadIfPresent(final int layer) { return null; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java index 77f9aa1c75..ae7157c4c2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java @@ -20,7 +20,7 @@ package com.sk89q.worldedit.extent; import com.fastasyncworldedit.core.FaweCache; -import com.fastasyncworldedit.core.extent.filter.block.CharFilterBlock; +import com.fastasyncworldedit.core.extent.filter.block.DataArrayFilterBlock; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; import com.fastasyncworldedit.core.extent.processor.ProcessorScope; @@ -65,7 +65,7 @@ public MaskingExtent(Extent extent, Mask mask) { this.mask = mask; this.vectorizedMask = SimdSupport.vectorizedTargetMask(mask); //FAWE start - this.getOrCreateFilterBlock = FaweCache.INSTANCE.createMainThreadSafeCache(() -> new CharFilterBlock(getExtent())); + this.getOrCreateFilterBlock = FaweCache.INSTANCE.createMainThreadSafeCache(() -> new DataArrayFilterBlock(getExtent())); //FAWE end } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java index 85e85a8c20..394f99d69f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java @@ -70,10 +70,10 @@ default Mask tryOptimize() { @SuppressWarnings({"unchecked", "rawtypes"}) default MaskFilter toFilter(T filter) { - final VectorizedMask mask = SimdSupport.vectorizedTargetMask(this); + final VectorizedMask mask = SimdSupport.vectorizedTargetMask(this); if (mask != null) { - VectorizedFilter vectorizedFilter = null; - if (filter instanceof VectorizedFilter vf) { + VectorizedFilter vectorizedFilter = null; + if (filter instanceof VectorizedFilter vf) { vectorizedFilter = vf; } else if (filter instanceof Pattern p) { vectorizedFilter = SimdSupport.vectorizedPattern(p); 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..bc9a5e822d 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 @@ -27,6 +27,7 @@ import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.World; @@ -831,7 +832,7 @@ public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { if (!set.hasSection(layer)) { continue; } - char[] arr = Objects.requireNonNull(set.loadIfPresent(layer)); // This shouldn't be null if above is true + DataArray arr = Objects.requireNonNull(set.loadIfPresent(layer)); // This shouldn't be null if above is true int indexY = 0; for (int y = 0; y < 16; y++, indexY += 256) { // For each y layer within a chunk section int index; @@ -839,34 +840,26 @@ public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { index = indexY; for (int z = 0; z < lowerZ; z++) { // null the z values - for (int x = 0; x < 16; x++, index++) { - arr[index] = BlockTypesCache.ReservedIDs.__RESERVED__; - } + arr.setRange(index, index += 16, BlockTypesCache.ReservedIDs.__RESERVED__); } index = indexY + upperZi; for (int z = upperZ + 1; z < 16; z++) { // null the z values - for (int x = 0; x < 16; x++, index++) { - arr[index] = BlockTypesCache.ReservedIDs.__RESERVED__; - } + arr.setRange(index, index += 16, BlockTypesCache.ReservedIDs.__RESERVED__); } } if (trimX) { index = indexY + lowerZi; // Skip blocks already removed by trimZ for (int z = lowerZ; z <= upperZ; z++, index += 16) { - for (int x = 0; x < lowerX; x++) { - // null the x values - arr[index + x] = BlockTypesCache.ReservedIDs.__RESERVED__; - } - for (int x = upperX + 1; x < 16; x++) { - // null the x values - arr[index + x] = BlockTypesCache.ReservedIDs.__RESERVED__; - } + // null the x values + arr.setRange(index, index + lowerX, BlockTypesCache.ReservedIDs.__RESERVED__); + arr.setRange(index + upperX + 1, index + upperX + 16, BlockTypesCache.ReservedIDs.__RESERVED__); } } } set.setBlocks(layer, arr); } + final BlockVector3 chunkPos = BlockVector3.at(chunk.getX() << 4, 0, chunk.getZ() << 4); trimNBT(set, this::contains, pos -> this.contains(pos.add(chunkPos))); return set; @@ -915,7 +908,7 @@ public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set, boolean if (!set.hasSection(layer)) { continue; } - char[] arr = Objects.requireNonNull(set.loadIfPresent(layer)); // This shouldn't be null if above is true + DataArray arr = Objects.requireNonNull(set.loadIfPresent(layer)); // This shouldn't be null if above is true if (!(trimX || trimZ)) { continue; } @@ -926,18 +919,14 @@ public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set, boolean index = indexY; for (int z = lowerZ; z <= upperZ; z++) { // null the z values - for (int x = 0; x < 16; x++, index++) { - arr[index] = BlockTypesCache.ReservedIDs.__RESERVED__; - } + arr.setRange(index, index += 16, BlockTypesCache.ReservedIDs.__RESERVED__); } } if (trimX) { index = indexY + lowerZi; // Skip blocks already removed by trimZ for (int z = lowerZ; z <= upperZ; z++, index += 16) { - for (int x = lowerX; x <= upperX; x++) { - // null the x values - arr[index + x] = BlockTypesCache.ReservedIDs.__RESERVED__; - } + // null the x values + arr.setRange(index + lowerX, index + upperX + 1, BlockTypesCache.ReservedIDs.__RESERVED__); } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java index c236992cf8..3f2bcef415 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java @@ -29,6 +29,7 @@ import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extent.Extent; @@ -411,20 +412,11 @@ default IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { int ty = by + 15; if (!containsEntireCuboid(bx, tx, by, ty, bz, tz)) { processExtra = true; - char[] arr = set.loadIfPresent(layer); + DataArray arr = set.loadIfPresent(layer); if (arr == null) { continue; } - for (int y = 0, index = 0; y < 16; y++) { - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++, index++) { - if (arr[index] != BlockTypesCache.ReservedIDs.__RESERVED__ && !contains(bx + x, by + y, bz + z)) { - arr[index] = BlockTypesCache.ReservedIDs.__RESERVED__; - } - } - } - } - set.setBlocks(layer, arr); + processCuboid(set, layer, arr); } } if (processExtra) { @@ -437,6 +429,19 @@ default IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { } } + private void processCuboid(IChunkSet set, int layer, DataArray dataArray) { + for (int y = 0, index = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++, index++) { + if (dataArray.getAt(index) != BlockTypesCache.ReservedIDs.__RESERVED__ && !contains(x, y, z)) { + dataArray.setAt(index, BlockTypesCache.ReservedIDs.__RESERVED__); + } + } + } + } + set.setBlocks(layer, dataArray); + } + /** * Process the chunk, with the option to process as if the region is a blacklisted region, and thus any contained blocks * should be removed, rather than uncontained blocks being removed. @@ -461,24 +466,11 @@ default IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set, boolean int by = layer << 4; int ty = by + 15; if (containsEntireCuboid(bx, tx, by, ty, bz, tz)) { - set.setBlocks(layer, FaweCache.INSTANCE.EMPTY_CHAR_4096); + set.setBlocks(layer, FaweCache.INSTANCE.EMPTY_DATA); processExtra = true; continue; } - char[] arr = set.load(layer); - for (int y = 0, index = 0; y < 16; y++) { - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++, index++) { - if (arr[index] != BlockTypesCache.ReservedIDs.__RESERVED__ && contains(x, y, z)) { - arr[index] = BlockTypesCache.ReservedIDs.__RESERVED__; - processExtra = true; - } - } - } - } - if (processExtra) { - set.setBlocks(layer, arr); - } + processExtra = isProcessExtra(set, processExtra, layer, set.load(layer)); } if (processExtra) { BlockVector3 chunkPos = chunk.getChunkBlockCoord().withY(0); @@ -490,6 +482,23 @@ default IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set, boolean } } + private boolean isProcessExtra(IChunkSet set, boolean processExtra, int layer, DataArray arr) { + for (int y = 0, index = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++, index++) { + if (arr.getAt(index) != BlockTypesCache.ReservedIDs.__RESERVED__ && contains(x, y, z)) { + arr.setAt(index, BlockTypesCache.ReservedIDs.__RESERVED__); + processExtra = true; + } + } + } + } + if (processExtra) { + set.setBlocks(layer, arr); + } + return processExtra; + } + @Override default Extent construct(Extent child) { if (isGlobal()) { diff --git a/worldedit-core/src/test/java/com/fastasyncworldedit/core/queue/IBatchProcessorTest.java b/worldedit-core/src/test/java/com/fastasyncworldedit/core/queue/IBatchProcessorTest.java index e28d0d236f..1369f4ac3a 100644 --- a/worldedit-core/src/test/java/com/fastasyncworldedit/core/queue/IBatchProcessorTest.java +++ b/worldedit-core/src/test/java/com/fastasyncworldedit/core/queue/IBatchProcessorTest.java @@ -1,17 +1,20 @@ package com.fastasyncworldedit.core.queue; +import com.fastasyncworldedit.core.queue.implementation.blocks.DataArray; import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.world.block.BlockTypesCache; import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.parallel.Isolated; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.opentest4j.AssertionFailedError; -import java.util.Arrays; import java.util.stream.Stream; +import static com.sk89q.worldedit.world.block.BlockTypesCache.ReservedIDs.AIR; +import static com.sk89q.worldedit.world.block.BlockTypesCache.ReservedIDs.__RESERVED__; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @@ -21,15 +24,18 @@ class IBatchProcessorTest { @Isolated class trimY { - private static final char[] CHUNK_DATA = new char[16 * 16 * 16]; - private static final char[] SLICE_AIR = new char[16 * 16]; - private static final char[] SLICE_RESERVED = new char[16 * 16]; + static { + // this must happen before DataArray/CharDataArray is initialized + System.setProperty("fawe.test", "true"); + } + + private static final DataArray CHUNK_DATA = DataArray.createFilled(AIR); private final IBatchProcessor processor = new NoopBatchProcessor(); - static { - Arrays.fill(CHUNK_DATA, (char) BlockTypesCache.ReservedIDs.AIR); - Arrays.fill(SLICE_AIR, (char) BlockTypesCache.ReservedIDs.AIR); - Arrays.fill(SLICE_RESERVED, (char) BlockTypesCache.ReservedIDs.__RESERVED__); + @AfterAll + static void tearDown() { + // remove again + System.getProperties().remove("fawe.test"); } @ParameterizedTest @@ -37,9 +43,9 @@ class trimY { void testFullChunkSelectedInBoundedRegion(int minY, int maxY, int minSection, int maxSection) { final IChunkSet set = mock(); - char[][] sections = new char[(320 + 64) >> 4][CHUNK_DATA.length]; - for (final char[] chars : sections) { - System.arraycopy(CHUNK_DATA, 0, chars, 0, CHUNK_DATA.length); + DataArray[] sections = new DataArray[(320 + 64) >> 4]; + for (int i = 0; i < sections.length; i++) { + sections[i] = DataArray.createCopy(CHUNK_DATA); } when(set.getMinSectionPosition()).thenReturn(-64 >> 4); @@ -56,7 +62,7 @@ void testFullChunkSelectedInBoundedRegion(int minY, int maxY, int minSection, in for (int section = -64 >> 4; section < 320 >> 4; section++) { int sectionIndex = section + 4; - char[] palette = sections[sectionIndex]; + DataArray palette = sections[sectionIndex]; if (section < minSection) { assertNull(palette, "expected section below minimum section to be null"); continue; @@ -72,28 +78,26 @@ void testFullChunkSelectedInBoundedRegion(int minY, int maxY, int minSection, in if (section == maxSection) { shouldContainBlocks &= slice <= (maxY % 16); } - assertArrayEquals( - shouldContainBlocks ? SLICE_AIR : SLICE_RESERVED, - Arrays.copyOfRange(palette, slice << 8, (slice + 1) << 8), - ("[lower] slice %d (y=%d) expected to contain " + (shouldContainBlocks ? "air" : "nothing")) - .formatted(slice, ((section << 4) + slice)) - ); + try { + assertSliceMatches(palette, slice << 8, (slice + 1) << 8, shouldContainBlocks ? AIR : __RESERVED__); + } catch (AssertionFailedError error) { + fail("[lower] slice %d (y=%d) expected to contain " + (shouldContainBlocks ? "air" : "nothing"), error); + } } continue; } if (section == maxSection) { for (int slice = 0; slice < 16; slice++) { boolean shouldContainBlocks = slice <= (maxY % 16); - assertArrayEquals( - shouldContainBlocks ? SLICE_AIR : SLICE_RESERVED, - Arrays.copyOfRange(palette, slice << 8, (slice + 1) << 8), - ("[upper] slice %d (y=%d) expected to contain " + (shouldContainBlocks ? "air" : "nothing")) - .formatted(slice, ((section << 4) + slice)) - ); + try { + assertSliceMatches(palette, slice << 8, (slice + 1) << 8, shouldContainBlocks ? AIR : __RESERVED__); + } catch (AssertionFailedError error) { + fail("[upper] slice %d (y=%d) expected to contain " + (shouldContainBlocks ? "air" : "nothing"), error); + } } continue; } - assertArrayEquals(CHUNK_DATA, palette, "full captured chunk @ %d should contain full data".formatted(section)); + assertEquals(CHUNK_DATA, palette, "full captured chunk @ %d should contain full data".formatted(section)); } } @@ -133,4 +137,10 @@ public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChun } + private static void assertSliceMatches(DataArray dataArray, int sliceStart, int sliceEnd, int expectedValue) { + for (int i = sliceStart; i < sliceEnd; i++) { + assertEquals(expectedValue, dataArray.getAt(i), "mismatch at index " + i); + } + } + }