From f6af9925e8366ee7055a7dd95515c09021a152b4 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Tue, 8 Jun 2021 15:28:16 +0100 Subject: [PATCH] Char block null check (#1030) **Add a null-check to CharBlocks FULL section layer-retrieval.** - It is possible to trim CharBlocks whilst it is attempting to read data due to the batching of chunks to help reduce memory - This is done when the number of chunks sitting loaded in memory with having been "submitted" to the queue for writing to disk becomes high - Seconday operations such as heightmap processing and lighting will quickly load chunks, meaning many chunks are submitted early - This leads to much higher chances of the chunk being submitted and subsequently trimmed given heightmap and light processing is done layer-by-layer over many chunks, rather than chunk-by-chunk - thus leading to NPEs. - By adding synchronisation to and around only the specific sections when loading/updating, and not blocking the whole chunk, many access can still be thread-safe without causing deadlocks - This allows removal of lots of the needless and very-slowing synchronisation on get**Block** methods **Remove much of the synchronisation from ChunkHolder** - We shouldn't be synchronising with call() and safety should be added elsewhere. (plus it's making edits very very slow when queue target size is hit) - Also remove much of synchronisation because we've added the null-check and section-specific synchronisation to CharBlocks **Some QOL/thread-safe data access changes** - Replaces the Array#clone seen in the get blocks classes with System#arraycopy as deep cloning is not required, and is also slower than arraycopy - Add System#arraycopy when accessing chunk section data via history to ensure it is not altered whilst being written - Renaming EMPTY to empty means it is not implied to be a static variable Fixes https://github.com/IntellectualSites/FastAsyncWorldEdit/issues/1028 Fixes https://github.com/IntellectualSites/FastAsyncWorldEdit/issues/1025 Fixes https://github.com/IntellectualSites/FastAsyncWorldEdit/issues/1089 Fixes https://github.com/IntellectualSites/FastAsyncWorldEdit/issues/1091 Fixes https://github.com/IntellectualSites/FastAsyncWorldEdit/issues/1097 --- .../mc1_15_2/BukkitGetBlocks_1_15_2.java | 45 +++++++++----- .../mc1_16_1/BukkitGetBlocks_1_16_1.java | 45 +++++++++----- .../mc1_16_2/BukkitGetBlocks_1_16_2.java | 45 +++++++++----- .../mc1_16_5/BukkitGetBlocks_1_16_5.java | 45 +++++++++----- .../implementation/blocks/CharBlocks.java | 47 ++++++++++---- .../implementation/blocks/CharGetBlocks.java | 9 +-- .../implementation/blocks/CharSetBlocks.java | 4 +- .../implementation/chunk/ChunkHolder.java | 62 +++++++++---------- .../object/changeset/AbstractChangeSet.java | 10 ++- 9 files changed, 197 insertions(+), 115 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java index f7c41d278..172cd38e4 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java @@ -349,17 +349,20 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks implements BukkitGetBl }; } - private void updateGet(BukkitGetBlocks_1_15_2 get, Chunk nmsChunk, ChunkSection[] sections, ChunkSection section, char[] arr, int layer) { + private void updateGet(BukkitGetBlocks_1_15_2 get, Chunk nmsChunk, ChunkSection[] chunkSections, ChunkSection section, char[] arr, int layer) { synchronized (get) { if (this.getChunk() != nmsChunk) { this.nmsChunk = nmsChunk; - this.sections = sections.clone(); + this.sections = new ChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); this.reset(); } if (this.sections == null) { - this.sections = sections.clone(); + this.sections = new ChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); } if (this.sections[layer] != section) { + // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords this.sections[layer] = new ChunkSection[]{section}.clone()[0]; } this.blocks[layer] = arr; @@ -421,9 +424,14 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks implements BukkitGetBl bitMask |= 1 << layer; - char[] setArr = set.load(layer).clone(); + char[] tmp = set.load(layer); + char[] setArr = new char[4096]; + System.arraycopy(tmp, 0, setArr, 0, 4096); if (createCopy) { - copy.storeSection(layer, loadPrivately(layer).clone()); + char[] tmpLoad = loadPrivately(layer); + char[] copyArr = new char[4096]; + System.arraycopy(tmpLoad, 0, copyArr, 0, 4096); + copy.storeSection(layer, copyArr); } ChunkSection newSection; @@ -457,7 +465,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks implements BukkitGetBl } else if (existingSection != getSections(false)[layer]) { this.sections[layer] = existingSection; this.reset(); - } else if (!Arrays.equals(update(layer, new char[4096]), loadPrivately(layer))) { + } else if (!Arrays.equals(update(layer, new char[4096], true), loadPrivately(layer))) { this.reset(layer); } else if (lock.isModified()) { this.reset(layer); @@ -675,11 +683,13 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks implements BukkitGetBl } } - private char[] loadPrivately(int layer) { - if (super.sections[layer].isFull()) { - return super.blocks[layer]; + private synchronized char[] loadPrivately(int layer) { + if (super.blocks[layer] != null) { + char[] blocks = new char[4096]; + System.arraycopy(super.blocks[layer], 0, blocks, 0, 4096); + return blocks; } else { - return BukkitGetBlocks_1_15_2.this.update(layer, null); + return BukkitGetBlocks_1_15_2.this.update(layer, null, true); } } @@ -689,8 +699,8 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks implements BukkitGetBl } @Override - public synchronized char[] update(int layer, char[] data) { - ChunkSection section = getSections(true)[layer]; + public synchronized char[] update(int layer, char[] data, boolean aggressive) { + ChunkSection section = getSections(aggressive)[layer]; // Section is null, return empty array if (section == null) { data = new char[4096]; @@ -807,15 +817,20 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks implements BukkitGetBl public ChunkSection[] getSections(boolean force) { if (force && forceLoadSections) { - return sections = getChunk().getSections().clone(); + ChunkSection[] sections = getChunk().getSections(); + ChunkSection[] copy = new ChunkSection[sections.length]; + System.arraycopy(sections, 0, copy, 0, sections.length); + return copy; } ChunkSection[] tmp = sections; if (tmp == null) { synchronized (this) { tmp = sections; if (tmp == null) { - Chunk chunk = getChunk(); - sections = tmp = chunk.getSections().clone(); + ChunkSection[] chunkSections = getChunk().getSections(); + tmp = new ChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, tmp, 0, chunkSections.length); + sections = tmp; } } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java index 3c236dd77..7a8cd131d 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java @@ -349,17 +349,20 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks implements BukkitGetBl }; } - private void updateGet(BukkitGetBlocks_1_16_1 get, Chunk nmsChunk, ChunkSection[] sections, ChunkSection section, char[] arr, int layer) { + private void updateGet(BukkitGetBlocks_1_16_1 get, Chunk nmsChunk, ChunkSection[] chunkSections, ChunkSection section, char[] arr, int layer) { synchronized (get) { if (this.getChunk() != nmsChunk) { this.nmsChunk = nmsChunk; - this.sections = sections.clone(); + this.sections = new ChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); this.reset(); } if (this.sections == null) { - this.sections = sections.clone(); + this.sections = new ChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); } if (this.sections[layer] != section) { + // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords this.sections[layer] = new ChunkSection[]{section}.clone()[0]; } this.blocks[layer] = arr; @@ -421,9 +424,14 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks implements BukkitGetBl bitMask |= 1 << layer; - char[] setArr = set.load(layer).clone(); + char[] tmp = set.load(layer); + char[] setArr = new char[4096]; + System.arraycopy(tmp, 0, setArr, 0, 4096); if (createCopy) { - copy.storeSection(layer, loadPrivately(layer).clone()); + char[] tmpLoad = loadPrivately(layer); + char[] copyArr = new char[4096]; + System.arraycopy(tmpLoad, 0, copyArr, 0, 4096); + copy.storeSection(layer, copyArr); } ChunkSection newSection; @@ -457,7 +465,7 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks implements BukkitGetBl } else if (existingSection != getSections(false)[layer]) { this.sections[layer] = existingSection; this.reset(); - } else if (!Arrays.equals(update(layer, new char[4096]), loadPrivately(layer))) { + } else if (!Arrays.equals(update(layer, new char[4096], true), loadPrivately(layer))) { this.reset(layer); } else if (lock.isModified()) { this.reset(layer); @@ -676,11 +684,13 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks implements BukkitGetBl } } - private char[] loadPrivately(int layer) { - if (super.sections[layer].isFull()) { - return super.blocks[layer]; + private synchronized char[] loadPrivately(int layer) { + if (super.blocks[layer] != null) { + char[] blocks = new char[4096]; + System.arraycopy(super.blocks[layer], 0, blocks, 0, 4096); + return blocks; } else { - return BukkitGetBlocks_1_16_1.this.update(layer, null); + return BukkitGetBlocks_1_16_1.this.update(layer, null, true); } } @@ -690,8 +700,8 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks implements BukkitGetBl } @Override - public synchronized char[] update(int layer, char[] data) { - ChunkSection section = getSections(true)[layer]; + public synchronized char[] update(int layer, char[] data, boolean aggressive) { + ChunkSection section = getSections(aggressive)[layer]; // Section is null, return empty array if (section == null) { data = new char[4096]; @@ -808,15 +818,20 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks implements BukkitGetBl public ChunkSection[] getSections(boolean force) { if (force && forceLoadSections) { - return sections = getChunk().getSections().clone(); + ChunkSection[] sections = getChunk().getSections(); + ChunkSection[] copy = new ChunkSection[sections.length]; + System.arraycopy(sections, 0, copy, 0, sections.length); + return copy; } ChunkSection[] tmp = sections; if (tmp == null) { synchronized (this) { tmp = sections; if (tmp == null) { - Chunk chunk = getChunk(); - sections = tmp = chunk.getSections().clone(); + ChunkSection[] chunkSections = getChunk().getSections(); + tmp = new ChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, tmp, 0, chunkSections.length); + sections = tmp; } } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2.java index ca173b940..c14b87dbd 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2.java @@ -352,17 +352,20 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks implements BukkitGetBl }; } - private void updateGet(BukkitGetBlocks_1_16_2 get, Chunk nmsChunk, ChunkSection[] sections, ChunkSection section, char[] arr, int layer) { + private void updateGet(BukkitGetBlocks_1_16_2 get, Chunk nmsChunk, ChunkSection[] chunkSections, ChunkSection section, char[] arr, int layer) { synchronized (get) { if (this.getChunk() != nmsChunk) { this.nmsChunk = nmsChunk; - this.sections = sections.clone(); + this.sections = new ChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); this.reset(); } if (this.sections == null) { - this.sections = sections.clone(); + this.sections = new ChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); } if (this.sections[layer] != section) { + // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords this.sections[layer] = new ChunkSection[]{section}.clone()[0]; } this.blocks[layer] = arr; @@ -424,9 +427,14 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks implements BukkitGetBl bitMask |= 1 << layer; - char[] setArr = set.load(layer).clone(); + char[] tmp = set.load(layer); + char[] setArr = new char[4096]; + System.arraycopy(tmp, 0, setArr, 0, 4096); if (createCopy) { - copy.storeSection(layer, loadPrivately(layer).clone()); + char[] tmpLoad = loadPrivately(layer); + char[] copyArr = new char[4096]; + System.arraycopy(tmpLoad, 0, copyArr, 0, 4096); + copy.storeSection(layer, copyArr); } ChunkSection newSection; @@ -460,7 +468,7 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks implements BukkitGetBl } else if (existingSection != getSections(false)[layer]) { this.sections[layer] = existingSection; this.reset(); - } else if (!Arrays.equals(update(layer, new char[4096]), loadPrivately(layer))) { + } else if (!Arrays.equals(update(layer, new char[4096], true), loadPrivately(layer))) { this.reset(layer); } else if (lock.isModified()) { this.reset(layer); @@ -679,11 +687,13 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks implements BukkitGetBl } } - private char[] loadPrivately(int layer) { - if (super.sections[layer].isFull()) { - return super.blocks[layer]; + private synchronized char[] loadPrivately(int layer) { + if (super.blocks[layer] != null) { + char[] blocks = new char[4096]; + System.arraycopy(super.blocks[layer], 0, blocks, 0, 4096); + return blocks; } else { - return BukkitGetBlocks_1_16_2.this.update(layer, null); + return BukkitGetBlocks_1_16_2.this.update(layer, null, true); } } @@ -693,8 +703,8 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks implements BukkitGetBl } @Override - public synchronized char[] update(int layer, char[] data) { - ChunkSection section = getSections(true)[layer]; + public synchronized char[] update(int layer, char[] data, boolean aggressive) { + ChunkSection section = getSections(aggressive)[layer]; // Section is null, return empty array if (section == null) { data = new char[4096]; @@ -811,15 +821,20 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks implements BukkitGetBl public ChunkSection[] getSections(boolean force) { if (force && forceLoadSections) { - return sections = getChunk().getSections().clone(); + ChunkSection[] sections = getChunk().getSections(); + ChunkSection[] copy = new ChunkSection[sections.length]; + System.arraycopy(sections, 0, copy, 0, sections.length); + return copy; } ChunkSection[] tmp = sections; if (tmp == null) { synchronized (this) { tmp = sections; if (tmp == null) { - Chunk chunk = getChunk(); - sections = tmp = chunk.getSections().clone(); + ChunkSection[] chunkSections = getChunk().getSections(); + tmp = new ChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, tmp, 0, chunkSections.length); + sections = tmp; } } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_5/BukkitGetBlocks_1_16_5.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_5/BukkitGetBlocks_1_16_5.java index 8dca055a8..0445ca405 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_5/BukkitGetBlocks_1_16_5.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_5/BukkitGetBlocks_1_16_5.java @@ -352,17 +352,20 @@ public class BukkitGetBlocks_1_16_5 extends CharGetBlocks implements BukkitGetBl }; } - private void updateGet(BukkitGetBlocks_1_16_5 get, Chunk nmsChunk, ChunkSection[] sections, ChunkSection section, char[] arr, int layer) { + private void updateGet(BukkitGetBlocks_1_16_5 get, Chunk nmsChunk, ChunkSection[] chunkSections, ChunkSection section, char[] arr, int layer) { synchronized (get) { if (this.getChunk() != nmsChunk) { this.nmsChunk = nmsChunk; - this.sections = sections.clone(); + this.sections = new ChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); this.reset(); } if (this.sections == null) { - this.sections = sections.clone(); + this.sections = new ChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); } if (this.sections[layer] != section) { + // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords this.sections[layer] = new ChunkSection[]{section}.clone()[0]; } this.blocks[layer] = arr; @@ -424,9 +427,14 @@ public class BukkitGetBlocks_1_16_5 extends CharGetBlocks implements BukkitGetBl bitMask |= 1 << layer; - char[] setArr = set.load(layer).clone(); + char[] tmp = set.load(layer); + char[] setArr = new char[4096]; + System.arraycopy(tmp, 0, setArr, 0, 4096); if (createCopy) { - copy.storeSection(layer, loadPrivately(layer).clone()); + char[] tmpLoad = loadPrivately(layer); + char[] copyArr = new char[4096]; + System.arraycopy(tmpLoad, 0, copyArr, 0, 4096); + copy.storeSection(layer, copyArr); } ChunkSection newSection; @@ -460,7 +468,7 @@ public class BukkitGetBlocks_1_16_5 extends CharGetBlocks implements BukkitGetBl } else if (existingSection != getSections(false)[layer]) { this.sections[layer] = existingSection; this.reset(); - } else if (!Arrays.equals(update(layer, new char[4096]), loadPrivately(layer))) { + } else if (!Arrays.equals(update(layer, new char[4096], true), loadPrivately(layer))) { this.reset(layer); } else if (lock.isModified()) { this.reset(layer); @@ -679,11 +687,13 @@ public class BukkitGetBlocks_1_16_5 extends CharGetBlocks implements BukkitGetBl } } - private char[] loadPrivately(int layer) { - if (super.sections[layer].isFull()) { - return super.blocks[layer]; + private synchronized char[] loadPrivately(int layer) { + if (super.blocks[layer] != null) { + char[] blocks = new char[4096]; + System.arraycopy(super.blocks[layer], 0, blocks, 0, 4096); + return blocks; } else { - return BukkitGetBlocks_1_16_5.this.update(layer, null); + return BukkitGetBlocks_1_16_5.this.update(layer, null, false); } } @@ -693,8 +703,8 @@ public class BukkitGetBlocks_1_16_5 extends CharGetBlocks implements BukkitGetBl } @Override - public synchronized char[] update(int layer, char[] data) { - ChunkSection section = getSections(true)[layer]; + public synchronized char[] update(int layer, char[] data, boolean aggressive) { + ChunkSection section = getSections(aggressive)[layer]; // Section is null, return empty array if (section == null) { data = new char[4096]; @@ -811,15 +821,20 @@ public class BukkitGetBlocks_1_16_5 extends CharGetBlocks implements BukkitGetBl public ChunkSection[] getSections(boolean force) { if (force && forceLoadSections) { - return sections = getChunk().getSections().clone(); + ChunkSection[] sections = getChunk().getSections(); + ChunkSection[] copy = new ChunkSection[sections.length]; + System.arraycopy(sections, 0, copy, 0, sections.length); + return copy; } ChunkSection[] tmp = sections; if (tmp == null) { synchronized (this) { tmp = sections; if (tmp == null) { - Chunk chunk = getChunk(); - sections = tmp = chunk.getSections().clone(); + ChunkSection[] chunkSections = getChunk().getSections(); + tmp = new ChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, tmp, 0, chunkSections.length); + sections = tmp; } } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java index 7f19b2da3..57c1d90d7 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java @@ -19,22 +19,34 @@ public abstract class CharBlocks implements IBlocks { return blocks.blocks[layer]; } + // Ignore aggressive switch here. + @Override + public char[] get(CharBlocks blocks, @Range(from = 0, to = 15) int layer, boolean aggressive) { + return blocks.blocks[layer]; + } + @Override public final boolean isFull() { return true; } }; - protected final Section EMPTY = new Section() { + protected final Section empty = new Section() { @Override public final synchronized char[] get(CharBlocks 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 synchronized char[] get(CharBlocks blocks, @Range(from = 0, to = 15) int layer, boolean aggressive) { char[] arr = blocks.blocks[layer]; if (arr == null) { - arr = blocks.blocks[layer] = blocks.update(layer, null); + arr = blocks.blocks[layer] = blocks.update(layer, null, aggressive); if (arr == null) { throw new IllegalStateException("Array cannot be null: " + blocks.getClass()); } } else { - blocks.blocks[layer] = blocks.update(layer, arr); + blocks.blocks[layer] = blocks.update(layer, arr, aggressive); if (blocks.blocks[layer] == null) { throw new IllegalStateException("Array cannot be null (update): " + blocks.getClass()); } @@ -57,7 +69,7 @@ public abstract class CharBlocks implements IBlocks { blocks = new char[16][]; sections = new Section[16]; for (int i = 0; i < 16; i++) { - sections[i] = EMPTY; + sections[i] = empty; } } @@ -88,16 +100,16 @@ public abstract class CharBlocks implements IBlocks { @Override public synchronized IChunkSet reset() { for (int i = 0; i < 16; i++) { - sections[i] = EMPTY; + sections[i] = empty; } return null; } public synchronized void reset(@Range(from = 0, to = 15) int layer) { - sections[layer] = EMPTY; + sections[layer] = empty; } - public synchronized char[] update(int layer, char[] data) { + public synchronized char[] update(int layer, char[] data, boolean aggressive) { if (data == null) { return new char[4096]; } @@ -114,16 +126,18 @@ public abstract class CharBlocks implements IBlocks { } @Override - public synchronized char[] load(@Range(from = 0, to = 15) int layer) { - return sections[layer].get(this, layer); + public char[] load(@Range(from = 0, to = 15) int layer) { + synchronized (sections[layer]) { + return sections[layer].get(this, layer); + } } @Override - public synchronized BlockState getBlock(int x, int y, int z) { + public BlockState getBlock(int x, int y, int z) { return BlockTypesCache.states[get(x, y, z)]; } - public synchronized char get(int x, @Range(from = 0, to = 255) int y, int z) { + public char get(int x, @Range(from = 0, to = 255) int y, int z) { final int layer = y >> 4; final int index = (y & 15) << 8 | z << 4 | x; if (layer >= sections.length || layer < 0) { @@ -149,7 +163,7 @@ public abstract class CharBlocks implements IBlocks { Section */ - public synchronized final char get(@Range(from = 0, to = 15) int layer, int index) { + public final char get(@Range(from = 0, to = 15) int layer, int index) { return sections[layer].get(this, layer, index); } @@ -161,10 +175,17 @@ public abstract class CharBlocks implements IBlocks { public abstract char[] get(CharBlocks blocks, @Range(from = 0, to = 15) int layer); + public abstract char[] get(CharBlocks blocks, @Range(from = 0, to = 15) int layer, boolean aggressive); + public abstract boolean isFull(); public final char get(CharBlocks blocks, @Range(from = 0, to = 15) int layer, int index) { - return get(blocks, layer)[index]; + char[] section = get(blocks, layer); + if (section == null) { + blocks.reset(layer); + section = blocks.empty.get(blocks, layer, false); + } + return section[index]; } public final void set(CharBlocks blocks, @Range(from = 0, to = 15) int layer, int index, char value) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharGetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharGetBlocks.java index 9f3fd88a8..1219740cd 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharGetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharGetBlocks.java @@ -19,13 +19,14 @@ public abstract class CharGetBlocks extends CharBlocks implements IChunkGet { @Override public boolean trim(boolean aggressive) { for (int i = 0; i < 16; i++) { - sections[i] = EMPTY; + sections[i] = empty; blocks[i] = null; } return true; } - public char[] update(int layer, char[] data) { + @Override + public char[] update(int layer, char[] data, boolean aggressive) { if (data == null) { data = new char[4096]; } @@ -34,8 +35,8 @@ public abstract class CharGetBlocks extends CharBlocks implements IChunkGet { } @Override - public boolean trim(boolean aggressive, int layer) { - sections[layer] = EMPTY; + public synchronized boolean trim(boolean aggressive, int layer) { + sections[layer] = empty; blocks[layer] = null; return true; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java index 771c955d2..aed341a25 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java @@ -43,7 +43,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { } @Override - public void recycle() { + public synchronized void recycle() { POOL.offer(this); } @@ -104,7 +104,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { @Override public void setBlocks(int layer, char[] data) { this.blocks[layer] = data; - this.sections[layer] = data == null ? EMPTY : FULL; + this.sections[layer] = data == null ? empty : FULL; } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java index 932f900a1..4ee2f90b6 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java @@ -141,7 +141,7 @@ public class ChunkHolder> implements IQueueChunk { } @Override - public synchronized CompoundTag getEntity(UUID uuid) { + public CompoundTag getEntity(UUID uuid) { return delegate.get(this).getEntity(uuid); } @@ -156,21 +156,21 @@ public class ChunkHolder> implements IQueueChunk { } @Override - public synchronized void setLightingToGet(char[][] lighting) { + public void setLightingToGet(char[][] lighting) { delegate.setLightingToGet(this, lighting); } @Override - public synchronized void setSkyLightingToGet(char[][] lighting) { + public void setSkyLightingToGet(char[][] lighting) { delegate.setSkyLightingToGet(this, lighting); } @Override - public synchronized void setHeightmapToGet(HeightMapType type, int[] data) { + public void setHeightmapToGet(HeightMapType type, int[] data) { delegate.setHeightmapToGet(this, type, data); } - public synchronized void flushLightToGet(boolean heightmaps) { + public void flushLightToGet(boolean heightmaps) { delegate.flushLightToGet(this, heightmaps); } @@ -800,22 +800,22 @@ public class ChunkHolder> implements IQueueChunk { }; @Override - public synchronized Map getTiles() { + public Map getTiles() { return delegate.get(this).getTiles(); } @Override - public synchronized Set getEntities() { + public Set getEntities() { return delegate.get(this).getEntities(); } @Override - public synchronized boolean hasSection(int layer) { + public boolean hasSection(int layer) { return chunkExisting != null && chunkExisting.hasSection(layer); } @Override - public synchronized void filterBlocks(Filter filter, ChunkFilterBlock block, @Nullable Region region, boolean full) { + public void filterBlocks(Filter filter, ChunkFilterBlock block, @Nullable Region region, boolean full) { final IChunkGet get = getOrCreateGet(); final IChunkSet set = getOrCreateSet(); set.setFastMode(fastmode); @@ -863,7 +863,7 @@ public class ChunkHolder> implements IQueueChunk { /** * Get or create the existing part of this chunk. */ - public synchronized final IChunkGet getOrCreateGet() { + public final IChunkGet getOrCreateGet() { if (chunkExisting == null) { chunkExisting = newWrappedGet(); } @@ -873,7 +873,7 @@ public class ChunkHolder> implements IQueueChunk { /** * Get or create the settable part of this chunk. */ - public synchronized final IChunkSet getOrCreateSet() { + public final IChunkSet getOrCreateSet() { if (chunkSet == null) { chunkSet = newWrappedSet(); } @@ -885,7 +885,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 IChunkSet newWrappedSet() { + private IChunkSet newWrappedSet() { return extent.getCachedSet(chunkX, chunkZ); } @@ -917,11 +917,7 @@ public class ChunkHolder> implements IQueueChunk { public synchronized T call() { if (chunkSet != null) { chunkSet.setBitMask(bitMask); - return this.call(chunkSet, () -> { - synchronized (this) { - this.recycle(); - } - }); + return this.call(chunkSet, this::recycle); } return null; } @@ -963,82 +959,82 @@ public class ChunkHolder> implements IQueueChunk { } @Override - public synchronized boolean setBiome(int x, int y, int z, BiomeType biome) { + public boolean setBiome(int x, int y, int z, BiomeType biome) { return delegate.setBiome(this, x, y, z, biome); } @Override - public synchronized > boolean setBlock(int x, int y, int z, B block) { + public > boolean setBlock(int x, int y, int z, B block) { return delegate.setBlock(this, x, y, z, block); } @Override - public synchronized BiomeType getBiomeType(int x, int y, int z) { + public BiomeType getBiomeType(int x, int y, int z) { return delegate.getBiome(this, x, y, z); } @Override - public synchronized BlockState getBlock(int x, int y, int z) { + public BlockState getBlock(int x, int y, int z) { return delegate.getBlock(this, x, y, z); } @Override - public synchronized BaseBlock getFullBlock(int x, int y, int z) { + public BaseBlock getFullBlock(int x, int y, int z) { return delegate.getFullBlock(this, x, y, z); } @Override - public synchronized void setSkyLight(int x, int y, int z, int value) { + public void setSkyLight(int x, int y, int z, int value) { delegate.setSkyLight(this, x, y, z, value); } @Override - public synchronized void setHeightMap(HeightMapType type, int[] heightMap) { + public void setHeightMap(HeightMapType type, int[] heightMap) { delegate.setHeightMap(this, type, heightMap); } @Override - public synchronized void removeSectionLighting(int layer, boolean sky) { + public void removeSectionLighting(int layer, boolean sky) { delegate.removeSectionLighting(this, layer, sky); } @Override - public synchronized void setFullBright(int layer) { + public void setFullBright(int layer) { delegate.setFullBright(this, layer); } @Override - public synchronized void setBlockLight(int x, int y, int z, int value) { + public void setBlockLight(int x, int y, int z, int value) { delegate.setBlockLight(this, x, y, z, value); } @Override - public synchronized void setLightLayer(int layer, char[] toSet) { + public void setLightLayer(int layer, char[] toSet) { delegate.setLightLayer(this, layer, toSet); } @Override - public synchronized void setSkyLightLayer(int layer, char[] toSet) { + public void setSkyLightLayer(int layer, char[] toSet) { delegate.setSkyLightLayer(this, layer, toSet); } @Override - public synchronized int getSkyLight(int x, int y, int z) { + public int getSkyLight(int x, int y, int z) { return delegate.getSkyLight(this, x, y, z); } @Override - public synchronized int getEmmittedLight(int x, int y, int z) { + public int getEmmittedLight(int x, int y, int z) { return delegate.getEmmittedLight(this, x, y, z); } @Override - public synchronized int getBrightness(int x, int y, int z) { + public int getBrightness(int x, int y, int z) { return delegate.getBrightness(this, x, y, z); } @Override - public synchronized int getOpacity(int x, int y, int z) { + public int getOpacity(int x, int y, int z) { return delegate.getOpacity(this, x, y, z); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java index d70791e63..d3947f130 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java @@ -162,11 +162,15 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { continue; } // add each block and tile - char[] blocksGet = get.load(layer); - if (blocksGet == null) { + char[] blocksGet; + char[] tmp = get.load(layer); + if (tmp == null) { blocksGet = FaweCache.IMP.EMPTY_CHAR_4096; + } else { + System.arraycopy(tmp, 0, (blocksGet = new char[4096]), 0, 4096); } - char[] blocksSet = set.load(layer); + char[] blocksSet; + System.arraycopy(set.load(layer), 0, (blocksSet = new char[4096]), 0, 4096); int by = layer << 4; for (int y = 0, index = 0; y < 16; y++) {