diff --git a/.github/workflows/label-merge-conflicts.yaml b/.github/workflows/label-merge-conflicts.yaml new file mode 100644 index 000000000..d189f5520 --- /dev/null +++ b/.github/workflows/label-merge-conflicts.yaml @@ -0,0 +1,23 @@ +name: "Label conflicting PRs" +on: + push: + pull_request_target: + types: [ synchronize ] + pull_request: + types: [ synchronize ] + +permissions: + pull-requests: write + +jobs: + main: + if: github.event.pull_request.user.login != 'dependabot[bot]' + runs-on: ubuntu-latest + steps: + - name: Label conflicting PRs + uses: eps1lon/actions-label-merge-conflict@v2.1.0 + with: + dirtyLabel: "unresolved-merge-conflict" + repoToken: "${{ secrets.GITHUB_TOKEN }}" + commentOnDirty: "Please take a moment and address the merge conflicts of your pull request. Thanks!" + continueOnMissingPermissions: true diff --git a/buildSrc/src/main/kotlin/CommonJavaConfig.kt b/buildSrc/src/main/kotlin/CommonJavaConfig.kt index 549a26037..8a111a2e7 100644 --- a/buildSrc/src/main/kotlin/CommonJavaConfig.kt +++ b/buildSrc/src/main/kotlin/CommonJavaConfig.kt @@ -59,10 +59,8 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean options.encoding = "UTF-8" links( "https://jd.advntr.dev/api/latest/", - "https://logging.apache.org/log4j/2.x/log4j-api/apidocs/", + "https://logging.apache.org/log4j/2.x/javadoc/log4j-api/", "https://www.antlr.org/api/Java/", - "https://docs.enginehub.org/javadoc/org.enginehub.piston/core/0.5.7/", - "https://docs.enginehub.org/javadoc/org.enginehub.piston/default-impl/0.5.7/", "https://jd.papermc.io/paper/1.20/", "https://intellectualsites.github.io/fastasyncworldedit-javadocs/worldedit-core/" ) diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java index d7c64e800..eaaf5554d 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java @@ -74,9 +74,11 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.concurrent.Semaphore; import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; import java.util.stream.Collectors; @@ -93,6 +95,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc .getInstance() .getBukkitImplAdapter()); private final ReadWriteLock sectionLock = new ReentrantReadWriteLock(); + private final ReentrantLock callLock = new ReentrantLock(); private final ServerLevel serverLevel; private final int chunkX; private final int chunkZ; @@ -100,14 +103,16 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc private final int maxHeight; private final int minSectionPosition; private final int maxSectionPosition; + private final ConcurrentHashMap copies = new ConcurrentHashMap<>(); + private final Object sendLock = new Object(); private LevelChunkSection[] sections; private LevelChunk levelChunk; private DataLayer[] blockLight; private DataLayer[] skyLight; private boolean createCopy = false; - private PaperweightGetBlocks_Copy copy = null; private boolean forceLoadSections = true; private boolean lightUpdate = false; + private int copyKey = 0; public PaperweightGetBlocks(World world, int chunkX, int chunkZ) { this(((CraftWorld) world).getHandle(), chunkX, chunkZ); @@ -142,13 +147,27 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public void setCreateCopy(boolean createCopy) { + public int setCreateCopy(boolean createCopy) { + if (!callLock.isHeldByCurrentThread()) { + throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); + } this.createCopy = createCopy; + return ++this.copyKey; } @Override - public IChunkGet getCopy() { - return copy; + public IChunkGet getCopy(final int key) { + return copies.remove(key); + } + + @Override + public void lockCall() { + this.callLock.lock(); + } + + @Override + public void unlockCall() { + this.callLock.unlock(); } @Override @@ -444,8 +463,17 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override @SuppressWarnings("rawtypes") public synchronized > T call(IChunkSet set, Runnable finalizer) { + if (!callLock.isHeldByCurrentThread()) { + throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); + } forceLoadSections = false; - copy = createCopy ? new PaperweightGetBlocks_Copy(getChunk()) : null; + PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null; + if (createCopy) { + if (copies.containsKey(copyKey)) { + throw new IllegalStateException("Copy key already used."); + } + copies.put(copyKey, copy); + } try { ServerLevel nmsWorld = serverLevel; LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ); @@ -882,9 +910,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc if (super.sections[layer] != null) { synchronized (super.sectionLocks[layer]) { if (super.sections[layer].isFull() && super.blocks[layer] != null) { - char[] blocks = new char[4096]; - System.arraycopy(super.blocks[layer], 0, blocks, 0, 4096); - return blocks; + return super.blocks[layer]; } } } @@ -892,8 +918,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public synchronized void send(int mask, boolean lighting) { - PaperweightPlatformAdapter.sendChunk(serverLevel, chunkX, chunkZ, lighting); + public void send(int mask, boolean lighting) { + synchronized (sendLock) { + PaperweightPlatformAdapter.sendChunk(serverLevel, chunkX, chunkZ, lighting); + } } /** @@ -997,9 +1025,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc public LevelChunkSection[] getSections(boolean force) { force &= forceLoadSections; - sectionLock.readLock().lock(); LevelChunkSection[] tmp = sections; - sectionLock.readLock().unlock(); if (tmp == null || force) { try { sectionLock.writeLock().lock(); diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks_Copy.java index ba75c127f..a48c101d0 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks_Copy.java @@ -22,6 +22,7 @@ import net.minecraft.world.level.chunk.ChunkBiomeContainer; import net.minecraft.world.level.chunk.LevelChunk; import javax.annotation.Nullable; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -108,7 +109,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { } @Override - public void setCreateCopy(boolean createCopy) { + public int setCreateCopy(boolean createCopy) { + return -1; } @Override @@ -215,6 +217,10 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { @Override public char[] load(int layer) { layer -= getMinSectionPosition(); + if (blocks[layer] == null) { + blocks[layer] = new char[4096]; + Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR); + } return blocks[layer]; } diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java index 0c8cccb4c..4ffcab790 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java @@ -76,9 +76,11 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.concurrent.Semaphore; import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; import java.util.stream.Collectors; @@ -94,6 +96,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc .getInstance() .getBukkitImplAdapter()); private final ReadWriteLock sectionLock = new ReentrantReadWriteLock(); + private final ReentrantLock callLock = new ReentrantLock(); private final ServerLevel serverLevel; private final int chunkX; private final int chunkZ; @@ -103,14 +106,16 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc private final int maxSectionPosition; private final Registry biomeRegistry; private final IdMap> biomeHolderIdMap; + private final ConcurrentHashMap copies = new ConcurrentHashMap<>(); + private final Object sendLock = new Object(); private LevelChunkSection[] sections; private LevelChunk levelChunk; private DataLayer[] blockLight; private DataLayer[] skyLight; private boolean createCopy = false; - private PaperweightGetBlocks_Copy copy = null; private boolean forceLoadSections = true; private boolean lightUpdate = false; + private int copyKey = 0; public PaperweightGetBlocks(World world, int chunkX, int chunkZ) { this(((CraftWorld) world).getHandle(), chunkX, chunkZ); @@ -147,13 +152,27 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public void setCreateCopy(boolean createCopy) { + public int setCreateCopy(boolean createCopy) { + if (!callLock.isHeldByCurrentThread()) { + throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); + } this.createCopy = createCopy; + return ++this.copyKey; } @Override - public IChunkGet getCopy() { - return copy; + public IChunkGet getCopy(final int key) { + return copies.remove(key); + } + + @Override + public void lockCall() { + this.callLock.lock(); + } + + @Override + public void unlockCall() { + this.callLock.unlock(); } @Override @@ -433,8 +452,17 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override @SuppressWarnings("rawtypes") public synchronized > T call(IChunkSet set, Runnable finalizer) { + if (!callLock.isHeldByCurrentThread()) { + throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); + } forceLoadSections = false; - copy = createCopy ? new PaperweightGetBlocks_Copy(getChunk()) : null; + PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null; + if (createCopy) { + if (copies.containsKey(copyKey)) { + throw new IllegalStateException("Copy key already used."); + } + copies.put(copyKey, copy); + } try { ServerLevel nmsWorld = serverLevel; LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ); @@ -928,9 +956,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc if (super.sections[layer] != null) { synchronized (super.sectionLocks[layer]) { if (super.sections[layer].isFull() && super.blocks[layer] != null) { - char[] blocks = new char[4096]; - System.arraycopy(super.blocks[layer], 0, blocks, 0, 4096); - return blocks; + return super.blocks[layer]; } } } @@ -938,8 +964,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public synchronized void send(int mask, boolean lighting) { - PaperweightPlatformAdapter.sendChunk(serverLevel, chunkX, chunkZ, lighting); + public void send(int mask, boolean lighting) { + synchronized (sendLock) { + PaperweightPlatformAdapter.sendChunk(serverLevel, chunkX, chunkZ, lighting); + } } /** @@ -1050,9 +1078,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc public LevelChunkSection[] getSections(boolean force) { force &= forceLoadSections; - sectionLock.readLock().lock(); LevelChunkSection[] tmp = sections; - sectionLock.readLock().unlock(); if (tmp == null || force) { try { sectionLock.writeLock().lock(); diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks_Copy.java index be24d8b90..c29e16c6b 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks_Copy.java @@ -11,7 +11,6 @@ import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; @@ -24,6 +23,7 @@ import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.PalettedContainer; import javax.annotation.Nullable; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -110,7 +110,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { } @Override - public void setCreateCopy(boolean createCopy) { + public int setCreateCopy(boolean createCopy) { + return -1; } @Override @@ -206,6 +207,10 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { @Override public char[] load(int layer) { layer -= getMinSectionPosition(); + if (blocks[layer] == null) { + blocks[layer] = new char[4096]; + Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR); + } return blocks[layer]; } diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java index 7c7446ad2..6f8408295 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java @@ -77,9 +77,11 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.concurrent.Semaphore; import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; import java.util.stream.Collectors; @@ -97,6 +99,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc .getInstance() .getBukkitImplAdapter()); private final ReadWriteLock sectionLock = new ReentrantReadWriteLock(); + private final ReentrantLock callLock = new ReentrantLock(); private final ServerLevel serverLevel; private final int chunkX; private final int chunkZ; @@ -106,14 +109,16 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc private final int maxSectionPosition; private final Registry biomeRegistry; private final IdMap> biomeHolderIdMap; + private final ConcurrentHashMap copies = new ConcurrentHashMap<>(); + private final Object sendLock = new Object(); private LevelChunkSection[] sections; private LevelChunk levelChunk; private DataLayer[] blockLight; private DataLayer[] skyLight; private boolean createCopy = false; - private PaperweightGetBlocks_Copy copy = null; private boolean forceLoadSections = true; private boolean lightUpdate = false; + private int copyKey = 0; public PaperweightGetBlocks(World world, int chunkX, int chunkZ) { this(((CraftWorld) world).getHandle(), chunkX, chunkZ); @@ -150,13 +155,27 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public void setCreateCopy(boolean createCopy) { + public int setCreateCopy(boolean createCopy) { + if (!callLock.isHeldByCurrentThread()) { + throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); + } this.createCopy = createCopy; + return ++this.copyKey; } @Override - public IChunkGet getCopy() { - return copy; + public IChunkGet getCopy(final int key) { + return copies.remove(key); + } + + @Override + public void lockCall() { + this.callLock.lock(); + } + + @Override + public void unlockCall() { + this.callLock.unlock(); } @Override @@ -436,8 +455,17 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override @SuppressWarnings("rawtypes") public synchronized > T call(IChunkSet set, Runnable finalizer) { + if (!callLock.isHeldByCurrentThread()) { + throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); + } forceLoadSections = false; - copy = createCopy ? new PaperweightGetBlocks_Copy(getChunk()) : null; + PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null; + if (createCopy) { + if (copies.containsKey(copyKey)) { + throw new IllegalStateException("Copy key already used."); + } + copies.put(copyKey, copy); + } try { ServerLevel nmsWorld = serverLevel; LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ); @@ -930,9 +958,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc if (super.sections[layer] != null) { synchronized (super.sectionLocks[layer]) { if (super.sections[layer].isFull() && super.blocks[layer] != null) { - char[] blocks = new char[4096]; - System.arraycopy(super.blocks[layer], 0, blocks, 0, 4096); - return blocks; + return super.blocks[layer]; } } } @@ -940,8 +966,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public synchronized void send(int mask, boolean lighting) { - PaperweightPlatformAdapter.sendChunk(serverLevel, chunkX, chunkZ, lighting); + public void send(int mask, boolean lighting) { + synchronized (sendLock) { + PaperweightPlatformAdapter.sendChunk(serverLevel, chunkX, chunkZ, lighting); + } } /** @@ -1052,9 +1080,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc public LevelChunkSection[] getSections(boolean force) { force &= forceLoadSections; - sectionLock.readLock().lock(); LevelChunkSection[] tmp = sections; - sectionLock.readLock().unlock(); if (tmp == null || force) { try { sectionLock.writeLock().lock(); diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks_Copy.java index 0dd7da167..0f02d4cd3 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks_Copy.java @@ -26,6 +26,7 @@ import net.minecraft.world.level.chunk.PalettedContainerRO; import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -114,7 +115,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { } @Override - public void setCreateCopy(boolean createCopy) { + public int setCreateCopy(boolean createCopy) { + return -1; } @Override @@ -218,6 +220,10 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { @Override public char[] load(int layer) { layer -= getMinSectionPosition(); + if (blocks[layer] == null) { + blocks[layer] = new char[4096]; + Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR); + } return blocks[layer]; } diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java index 7e2f3eaee..17b354535 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java @@ -152,7 +152,7 @@ public class PaperweightRegen extends Regenerator biomeRegistry; private final IdMap> biomeHolderIdMap; + private final ConcurrentHashMap copies = new ConcurrentHashMap<>(); + private final Object sendLock = new Object(); private LevelChunkSection[] sections; private LevelChunk levelChunk; private DataLayer[] blockLight; private DataLayer[] skyLight; private boolean createCopy = false; - private PaperweightGetBlocks_Copy copy = null; private boolean forceLoadSections = true; private boolean lightUpdate = false; + private int copyKey = 0; public PaperweightGetBlocks(World world, int chunkX, int chunkZ) { this(((CraftWorld) world).getHandle(), chunkX, chunkZ); @@ -150,13 +155,27 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public void setCreateCopy(boolean createCopy) { + public int setCreateCopy(boolean createCopy) { + if (!callLock.isHeldByCurrentThread()) { + throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); + } this.createCopy = createCopy; + return ++this.copyKey; } @Override - public IChunkGet getCopy() { - return copy; + public IChunkGet getCopy(final int key) { + return copies.remove(key); + } + + @Override + public void lockCall() { + this.callLock.lock(); + } + + @Override + public void unlockCall() { + this.callLock.unlock(); } @Override @@ -435,8 +454,17 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override @SuppressWarnings("rawtypes") public synchronized > T call(IChunkSet set, Runnable finalizer) { + if (!callLock.isHeldByCurrentThread()) { + throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); + } forceLoadSections = false; - copy = createCopy ? new PaperweightGetBlocks_Copy(getChunk()) : null; + PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null; + if (createCopy) { + if (copies.containsKey(copyKey)) { + throw new IllegalStateException("Copy key already used."); + } + copies.put(copyKey, copy); + } try { ServerLevel nmsWorld = serverLevel; LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ); @@ -928,9 +956,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc if (super.sections[layer] != null) { synchronized (super.sectionLocks[layer]) { if (super.sections[layer].isFull() && super.blocks[layer] != null) { - char[] blocks = new char[4096]; - System.arraycopy(super.blocks[layer], 0, blocks, 0, 4096); - return blocks; + return super.blocks[layer]; } } } @@ -938,8 +964,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public synchronized void send(int mask, boolean lighting) { - PaperweightPlatformAdapter.sendChunk(serverLevel, chunkX, chunkZ, lighting); + public void send(int mask, boolean lighting) { + synchronized (sendLock) { + PaperweightPlatformAdapter.sendChunk(serverLevel, chunkX, chunkZ, lighting); + } } /** @@ -1050,9 +1078,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc public LevelChunkSection[] getSections(boolean force) { force &= forceLoadSections; - sectionLock.readLock().lock(); LevelChunkSection[] tmp = sections; - sectionLock.readLock().unlock(); if (tmp == null || force) { try { sectionLock.writeLock().lock(); diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks_Copy.java index 3b47efcaa..d60e9fc20 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks_Copy.java @@ -26,6 +26,7 @@ import net.minecraft.world.level.chunk.PalettedContainerRO; import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -114,7 +115,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { } @Override - public void setCreateCopy(boolean createCopy) { + public int setCreateCopy(boolean createCopy) { + return -1; } @Override @@ -218,6 +220,10 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { @Override public char[] load(int layer) { layer -= getMinSectionPosition(); + if (blocks[layer] == null) { + blocks[layer] = new char[4096]; + Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR); + } return blocks[layer]; } 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 c4055e196..ed6d3ae88 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 @@ -55,9 +55,11 @@ import org.bukkit.event.entity.CreatureSpawnEvent; import javax.annotation.Nonnull; import java.util.*; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.concurrent.Semaphore; import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; import java.util.stream.Collectors; @@ -75,6 +77,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc .getInstance() .getBukkitImplAdapter()); private final ReadWriteLock sectionLock = new ReentrantReadWriteLock(); + private final ReentrantLock callLock = new ReentrantLock(); private final ServerLevel serverLevel; private final int chunkX; private final int chunkZ; @@ -84,14 +87,16 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc private final int maxSectionPosition; private final Registry biomeRegistry; private final IdMap> biomeHolderIdMap; + private final ConcurrentHashMap copies = new ConcurrentHashMap<>(); + private final Object sendLock = new Object(); private LevelChunkSection[] sections; private LevelChunk levelChunk; private DataLayer[] blockLight; private DataLayer[] skyLight; private boolean createCopy = false; - private PaperweightGetBlocks_Copy copy = null; private boolean forceLoadSections = true; private boolean lightUpdate = false; + private int copyKey = 0; public PaperweightGetBlocks(World world, int chunkX, int chunkZ) { this(((CraftWorld) world).getHandle(), chunkX, chunkZ); @@ -128,13 +133,27 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public void setCreateCopy(boolean createCopy) { + public int setCreateCopy(boolean createCopy) { + if (!callLock.isHeldByCurrentThread()) { + throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); + } this.createCopy = createCopy; + return ++this.copyKey; } @Override - public IChunkGet getCopy() { - return copy; + public IChunkGet getCopy(final int key) { + return copies.remove(key); + } + + @Override + public void lockCall() { + this.callLock.lock(); + } + + @Override + public void unlockCall() { + this.callLock.unlock(); } @Override @@ -298,7 +317,15 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public CompoundTag getEntity(UUID uuid) { - Entity entity = serverLevel.getEntity(uuid); + ensureLoaded(serverLevel, chunkX, chunkZ); + List entities = PaperweightPlatformAdapter.getEntities(getChunk()); + Entity entity = null; + for (Entity e : entities) { + if (e.getUUID().equals(uuid)) { + entity = e; + break; + } + } if (entity != null) { org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity(); return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); @@ -414,8 +441,17 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override @SuppressWarnings("rawtypes") public synchronized > T call(IChunkSet set, Runnable finalizer) { + if (!callLock.isHeldByCurrentThread()) { + throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); + } forceLoadSections = false; - copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null; + PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null; + if (createCopy) { + if (copies.containsKey(copyKey)) { + throw new IllegalStateException("Copy key already used."); + } + copies.put(copyKey, copy); + } try { ServerLevel nmsWorld = serverLevel; LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ); @@ -907,9 +943,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc if (super.sections[layer] != null) { synchronized (super.sectionLocks[layer]) { if (super.sections[layer].isFull() && super.blocks[layer] != null) { - char[] blocks = new char[4096]; - System.arraycopy(super.blocks[layer], 0, blocks, 0, 4096); - return blocks; + return super.blocks[layer]; } } } @@ -917,8 +951,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public synchronized void send(int mask, boolean lighting) { - PaperweightPlatformAdapter.sendChunk(serverLevel, chunkX, chunkZ, lighting); + public void send(int mask, boolean lighting) { + synchronized (sendLock) { + PaperweightPlatformAdapter.sendChunk(serverLevel, chunkX, chunkZ, lighting); + } } /** @@ -1029,9 +1065,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc public LevelChunkSection[] getSections(boolean force) { force &= forceLoadSections; - sectionLock.readLock().lock(); LevelChunkSection[] tmp = sections; - sectionLock.readLock().unlock(); if (tmp == null || force) { try { sectionLock.writeLock().lock(); 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 a74f6e7d7..4fc6bb255 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 @@ -26,6 +26,7 @@ import net.minecraft.world.level.chunk.PalettedContainerRO; import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -114,7 +115,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { } @Override - public void setCreateCopy(boolean createCopy) { + public int setCreateCopy(boolean createCopy) { + return -1; } @Override @@ -218,6 +220,10 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { @Override public char[] load(int layer) { layer -= getMinSectionPosition(); + if (blocks[layer] == null) { + blocks[layer] = new char[4096]; + Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR); + } return blocks[layer]; } diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java index d0d9bb652..607bc75bf 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java @@ -41,6 +41,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; import java.util.function.Function; /** @@ -158,16 +159,14 @@ public abstract class Regenerator(), - new ThreadFactoryBuilder().setNameFormat("fawe-clipboard-%d").build() + new ThreadFactoryBuilder().setNameFormat("FAWE Clipboard - %d").build() )); } 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 af14624df..d8e1474e6 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java @@ -18,6 +18,7 @@ import com.fastasyncworldedit.core.util.collection.CleanableThreadLocal; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.ByteTag; import com.sk89q.jnbt.CompoundTag; @@ -48,7 +49,6 @@ import java.util.Map.Entry; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -616,7 +616,7 @@ public enum FaweCache implements Trimable { ArrayBlockingQueue queue = new ArrayBlockingQueue<>(nThreads, true); return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, queue, - Executors.defaultThreadFactory(), + new ThreadFactoryBuilder().setNameFormat("FAWE Blocking Executor - %d").build(), new ThreadPoolExecutor.CallerRunsPolicy() ) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java index 9222abc52..a3c554c49 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java @@ -59,13 +59,31 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput { boolean isCreateCopy(); - void setCreateCopy(boolean createCopy); + /** + * Not for external API use. Internal use only. + */ + int setCreateCopy(boolean createCopy); @Nullable - default IChunkGet getCopy() { + default IChunkGet getCopy(int key) { return null; } + /** + * Lock the {@link IChunkGet#call(IChunkSet, Runnable)} method to the current thread using a reentrant lock. Also locks + * related methods e.g. {@link IChunkGet#setCreateCopy(boolean)} + * + * @since TODO + */ + default void lockCall() {} + + /** + * Unlock {@link IChunkGet#call(IChunkSet, Runnable)} (and other related methods) to executions from other threads + * + * @since TODO + */ + default void unlockCall() {} + /** * Flush the block lighting array (section*blocks) to the chunk GET between the given section indices. Negative allowed. * diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueChunk.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueChunk.java index 72732378d..9dda1d006 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueChunk.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueChunk.java @@ -36,4 +36,18 @@ public interface IQueueChunk> extends IChunk, Callable { } } + /** + * Get if the thank has any running tasks, locked locks, etc. + */ + default boolean hasRunning() { + return false; + } + + /** + * Prevent set operations to the chunk, should typically be used when a chunk is submitted before the edit is necessarily + * completed. + */ + default void lockSet() { + } + } 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 1d85e1f42..2e3781776 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 @@ -62,8 +62,8 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen private boolean initialized; private Thread currentThread; // Last access pointers - private IQueueChunk lastChunk; - private long lastPair = Long.MAX_VALUE; + private volatile IQueueChunk lastChunk; + private volatile long lastPair = Long.MAX_VALUE; private boolean enabledQueue = true; private boolean fastmode = false; // Array for lazy avoidance of concurrent modification exceptions and needless overcomplication of code (synchronisation is @@ -283,6 +283,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen private ChunkHolder poolOrCreate(int chunkX, int chunkZ) { ChunkHolder next = create(false); next.init(this, chunkX, chunkZ); + next.setFastMode(isFastMode()); return next; } @@ -454,7 +455,8 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen if (!chunks.isEmpty()) { getChunkLock.lock(); if (MemUtil.isMemoryLimited()) { - for (IQueueChunk chunk : chunks.values()) { + while (!chunks.isEmpty()) { + IQueueChunk chunk = chunks.removeFirst(); final Future future = submitUnchecked(chunk); if (future != null && !future.isDone()) { pollSubmissions(Settings.settings().QUEUE.PARALLEL_THREADS, true); @@ -462,14 +464,14 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen } } } else { - for (IQueueChunk chunk : chunks.values()) { + while (!chunks.isEmpty()) { + IQueueChunk chunk = chunks.removeFirst(); final Future future = submitUnchecked(chunk); if (future != null && !future.isDone()) { submissions.add(future); } } } - chunks.clear(); getChunkLock.unlock(); } pollSubmissions(0, true); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java index 3487aa750..155f3404f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java @@ -128,6 +128,7 @@ public abstract class CharBlocks implements IBlocks { public synchronized IChunkSet reset() { for (int i = 0; i < sectionCount; i++) { sections[i] = EMPTY; + blocks[i] = null; } return null; } 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 ca7c167ab..c8ccd254d 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 @@ -75,7 +75,8 @@ public final class NullChunkGet implements IChunkGet { } @Override - public void setCreateCopy(boolean createCopy) { + public int setCreateCopy(boolean createCopy) { + return -1; } @Override 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 33931e337..e0fc471c0 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 @@ -26,8 +26,6 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.Future; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; /** * An abstract {@link IChunk} class that implements basic get/set blocks. @@ -45,8 +43,6 @@ public class ChunkHolder> implements IQueueChunk { return POOL.poll(); } - private final Lock calledLock = new ReentrantLock(); - private volatile IChunkGet chunkExisting; // The existing chunk (e.g. a clipboard, or the world, before changes) private volatile IChunkSet chunkSet; // The blocks to be set to the chunkExisting private IBlockDelegate delegate; // delegate handles the abstraction of the chunk layers @@ -69,7 +65,6 @@ public class ChunkHolder> implements IQueueChunk { @Override public synchronized void recycle() { - calledLock.lock(); delegate = NULL; if (chunkSet != null) { chunkSet.recycle(); @@ -78,7 +73,6 @@ public class ChunkHolder> implements IQueueChunk { chunkExisting = null; extent = null; POOL.offer(this); - calledLock.unlock(); } public long initAge() { @@ -89,68 +83,49 @@ public class ChunkHolder> implements IQueueChunk { return delegate; } - /** - * If the chunk is currently being "called", this method will block until completed. - */ - private void checkAndWaitOnCalledLock() { - if (!calledLock.tryLock()) { - calledLock.lock(); - } - calledLock.unlock(); - } - @Override public boolean setTile(int x, int y, int z, CompoundTag tag) { - checkAndWaitOnCalledLock(); return delegate.set(this).setTile(x, y, z, tag); } @Override public CompoundTag getTile(int x, int y, int z) { - checkAndWaitOnCalledLock(); return delegate.set(this).getTile(x, y, z); } @Override public void setEntity(CompoundTag tag) { - checkAndWaitOnCalledLock(); delegate.set(this).setEntity(tag); } @Override public void removeEntity(UUID uuid) { - checkAndWaitOnCalledLock(); delegate.set(this).removeEntity(uuid); } @Override public Set getEntityRemoves() { - checkAndWaitOnCalledLock(); return delegate.set(this).getEntityRemoves(); } @Override public BiomeType[][] getBiomes() { - checkAndWaitOnCalledLock(); // Uses set as this method is only used to retrieve biomes that have been set to the extent/chunk. return delegate.set(this).getBiomes(); } @Override public char[][] getLight() { - checkAndWaitOnCalledLock(); return delegate.set(this).getLight(); } @Override public char[][] getSkyLight() { - checkAndWaitOnCalledLock(); return delegate.set(this).getSkyLight(); } @Override public void setBlocks(int layer, char[] data) { - checkAndWaitOnCalledLock(); delegate.set(this).setBlocks(layer, data); } @@ -175,12 +150,10 @@ public class ChunkHolder> implements IQueueChunk { @Override public void setFastMode(boolean fastmode) { - checkAndWaitOnCalledLock(); this.fastmode = fastmode; } public void setBitMask(int bitMask) { - checkAndWaitOnCalledLock(); this.bitMask = bitMask; } @@ -190,7 +163,6 @@ public class ChunkHolder> implements IQueueChunk { @Override public boolean hasBiomes(final int layer) { - checkAndWaitOnCalledLock(); // No need to go through delegate. hasBiomes is SET only. return chunkSet != null && chunkSet.hasBiomes(layer); } @@ -201,14 +173,13 @@ public class ChunkHolder> implements IQueueChunk { @Override public CompoundTag getEntity(UUID uuid) { - checkAndWaitOnCalledLock(); return delegate.get(this).getEntity(uuid); } @Override - public void setCreateCopy(boolean createCopy) { - checkAndWaitOnCalledLock(); + public int setCreateCopy(boolean createCopy) { this.createCopy = createCopy; + return -1; } @Override @@ -218,19 +189,16 @@ public class ChunkHolder> implements IQueueChunk { @Override public void setLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) { - checkAndWaitOnCalledLock(); delegate.setLightingToGet(this, lighting); } @Override public void setSkyLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) { - checkAndWaitOnCalledLock(); delegate.setSkyLightingToGet(this, lighting); } @Override public void setHeightmapToGet(HeightMapType type, int[] data) { - checkAndWaitOnCalledLock(); delegate.setHeightmapToGet(this, type, data); } @@ -255,7 +223,6 @@ public class ChunkHolder> implements IQueueChunk { } public void flushLightToGet() { - checkAndWaitOnCalledLock(); delegate.flushLightToGet(this); } @@ -922,13 +889,11 @@ public class ChunkHolder> implements IQueueChunk { @Override public Map getTiles() { - checkAndWaitOnCalledLock(); return delegate.get(this).getTiles(); } @Override public Set getEntities() { - checkAndWaitOnCalledLock(); return delegate.get(this).getEntities(); } @@ -940,7 +905,6 @@ public class ChunkHolder> implements IQueueChunk { @Override public boolean hasSection(int layer) { - checkAndWaitOnCalledLock(); return chunkExisting != null && chunkExisting.hasSection(layer); } @@ -965,6 +929,7 @@ public class ChunkHolder> implements IQueueChunk { if (result) { delegate = NULL; chunkExisting = null; + chunkSet.recycle(); chunkSet = null; return true; } @@ -992,7 +957,6 @@ public class ChunkHolder> implements IQueueChunk { @Override public boolean isEmpty() { - checkAndWaitOnCalledLock(); return chunkSet == null || chunkSet.isEmpty(); } @@ -1000,7 +964,6 @@ public class ChunkHolder> implements IQueueChunk { * Get or create the existing part of this chunk. */ public final IChunkGet getOrCreateGet() { - checkAndWaitOnCalledLock(); if (chunkExisting == null) { chunkExisting = newWrappedGet(); chunkExisting.trim(false); @@ -1012,7 +975,6 @@ public class ChunkHolder> implements IQueueChunk { * Get or create the settable part of this chunk. */ public final IChunkSet getOrCreateSet() { - checkAndWaitOnCalledLock(); if (chunkSet == null) { chunkSet = newWrappedSet(); } @@ -1033,7 +995,7 @@ public class ChunkHolder> implements IQueueChunk { * - The purpose of wrapping is to allow different extents to intercept / alter behavior * - e.g., caching, optimizations, filtering */ - private synchronized IChunkGet newWrappedGet() { + private IChunkGet newWrappedGet() { return extent.getCachedGet(chunkX, chunkZ); } @@ -1055,42 +1017,42 @@ public class ChunkHolder> implements IQueueChunk { @Override public synchronized T call() { - calledLock.lock(); if (chunkSet != null && !chunkSet.isEmpty()) { - this.delegate = GET; chunkSet.setBitMask(bitMask); - try { - IChunkSet copy = chunkSet.createCopy(); - chunkSet = null; - return this.call(copy, () -> { - // Do nothing - }); - } catch (Throwable t) { - calledLock.unlock(); - throw t; - } + IChunkSet copy = chunkSet.createCopy(); + return this.call(copy, () -> { + // Do nothing + }); } + recycle(); return null; } + /** + * This method should never be called from outside ChunkHolder + */ @Override public synchronized T call(IChunkSet set, Runnable finalize) { if (set != null) { IChunkGet get = getOrCreateGet(); - boolean postProcess = !(getExtent().getPostProcessor() instanceof EmptyBatchProcessor); - get.setCreateCopy(postProcess); - final IChunkSet iChunkSet = getExtent().processSet(this, get, set); - Runnable finalizer; - if (postProcess) { - finalizer = () -> { - getExtent().postProcess(this, get.getCopy(), iChunkSet); - finalize.run(); - }; - } else { - finalizer = finalize; + try { + get.lockCall(); + boolean postProcess = !(getExtent().getPostProcessor() instanceof EmptyBatchProcessor); + final IChunkSet iChunkSet = getExtent().processSet(this, get, set); + Runnable finalizer; + if (postProcess) { + int copyKey = get.setCreateCopy(true); + finalizer = () -> { + getExtent().postProcess(this, get.getCopy(copyKey), iChunkSet); + finalize.run(); + }; + } else { + finalizer = finalize; + } + return get.call(set, finalizer); + } finally { + get.unlockCall(); } - calledLock.unlock(); - return get.call(set, finalizer); } return null; } @@ -1114,103 +1076,86 @@ public class ChunkHolder> implements IQueueChunk { @Override public boolean setBiome(int x, int y, int z, BiomeType biome) { - checkAndWaitOnCalledLock(); return delegate.setBiome(this, x, y, z, biome); } @Override public > boolean setBlock(int x, int y, int z, B block) { - checkAndWaitOnCalledLock(); return delegate.setBlock(this, x, y, z, block); } @Override public BiomeType getBiomeType(int x, int y, int z) { - checkAndWaitOnCalledLock(); return delegate.getBiome(this, x, y, z); } @Override public BlockState getBlock(int x, int y, int z) { - checkAndWaitOnCalledLock(); return delegate.getBlock(this, x, y, z); } @Override public BaseBlock getFullBlock(int x, int y, int z) { - checkAndWaitOnCalledLock(); return delegate.getFullBlock(this, x, y, z); } @Override public void setSkyLight(int x, int y, int z, int value) { - checkAndWaitOnCalledLock(); delegate.setSkyLight(this, x, y, z, value); } @Override public void setHeightMap(HeightMapType type, int[] heightMap) { - checkAndWaitOnCalledLock(); delegate.setHeightMap(this, type, heightMap); } @Override public void removeSectionLighting(int layer, boolean sky) { - checkAndWaitOnCalledLock(); delegate.removeSectionLighting(this, layer, sky); } @Override public void setFullBright(int layer) { - checkAndWaitOnCalledLock(); delegate.setFullBright(this, layer); } @Override public void setBlockLight(int x, int y, int z, int value) { - checkAndWaitOnCalledLock(); delegate.setBlockLight(this, x, y, z, value); } @Override public void setLightLayer(int layer, char[] toSet) { - checkAndWaitOnCalledLock(); delegate.setLightLayer(this, layer, toSet); } @Override public void setSkyLightLayer(int layer, char[] toSet) { - checkAndWaitOnCalledLock(); delegate.setSkyLightLayer(this, layer, toSet); } @Override public int getSkyLight(int x, int y, int z) { - checkAndWaitOnCalledLock(); return delegate.getSkyLight(this, x, y, z); } @Override public int getEmittedLight(int x, int y, int z) { - checkAndWaitOnCalledLock(); return delegate.getEmittedLight(this, x, y, z); } @Override public int getBrightness(int x, int y, int z) { - checkAndWaitOnCalledLock(); return delegate.getBrightness(this, x, y, z); } @Override public int getOpacity(int x, int y, int z) { - checkAndWaitOnCalledLock(); return delegate.getOpacity(this, x, y, z); } @Override public int[] getHeightMap(HeightMapType type) { - checkAndWaitOnCalledLock(); return delegate.getHeightMap(this, type); } 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 b636cebdf..24b58ba18 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 @@ -189,8 +189,8 @@ public final class NullChunk implements IQueueChunk { return Collections.emptySet(); } - @Override - public void setCreateCopy(boolean createCopy) { + public int setCreateCopy(boolean createCopy) { + return -1; } @Override diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/FaweForkJoinWorkerThreadFactory.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/FaweForkJoinWorkerThreadFactory.java index 589b5b863..2c8353ce8 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/FaweForkJoinWorkerThreadFactory.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/FaweForkJoinWorkerThreadFactory.java @@ -2,19 +2,22 @@ package com.fastasyncworldedit.core.util.task; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinWorkerThread; +import java.util.concurrent.atomic.AtomicInteger; public class FaweForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { private final String nameFormat; + private final AtomicInteger idCounter; public FaweForkJoinWorkerThreadFactory(String nameFormat) { this.nameFormat = nameFormat; + this.idCounter = new AtomicInteger(0); } @Override public ForkJoinWorkerThread newThread(ForkJoinPool pool) { final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); - worker.setName(String.format(nameFormat, worker.getPoolIndex())); + worker.setName(String.format(nameFormat, idCounter.getAndIncrement())); return worker; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CylinderRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CylinderRegionSelector.java index 689cd3701..0798b06e6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CylinderRegionSelector.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CylinderRegionSelector.java @@ -254,6 +254,8 @@ public class CylinderRegionSelector implements RegionSelector, CUIRegion { @Override public void clear() { region = new CylinderRegion(region.getWorld()); + selectedCenter = false; + selectedRadius = false; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/EllipsoidRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/EllipsoidRegionSelector.java index 02d570969..1f7c8a420 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/EllipsoidRegionSelector.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/EllipsoidRegionSelector.java @@ -227,6 +227,8 @@ public class EllipsoidRegionSelector implements RegionSelector, CUIRegion { public void clear() { region.setCenter(BlockVector3.ZERO); region.setRadius(Vector3.ZERO); + started = false; + selectedRadius = false; } @Override