diff --git a/README.md b/README.md index 0a9757109..cb22e55f7 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ FAWE is a fork of WorldEdit that has huge speed and memory improvements and cons * [Discord](https://discord.gg/KxkjDVg) * [Wiki](https://github.com/IntellectualSites/FastAsyncWorldEdit-1.13/wiki) * [Report Issue](https://github.com/IntellectualSites/FastAsyncWorldEdit-1.13/issues) +* [Crowdin](https://intellectualsites.crowdin.com/fastasyncworldedit) ## Downloads ### 1.13+ diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf01..490fda857 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4a6ebceac..4c5803d13 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 83f2acfdc..2fe81a7d9 100755 --- a/gradlew +++ b/gradlew @@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -175,14 +175,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 24467a141..9109989e3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/NMSAdapter.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/NMSAdapter.java index 38284966c..0160fdd51 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/NMSAdapter.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/NMSAdapter.java @@ -1,18 +1,23 @@ package com.boydti.fawe.bukkit.adapter; +import com.boydti.fawe.config.Settings; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockID; import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypesCache; import java.util.Map; import java.util.function.Function; public class NMSAdapter { public static int createPalette(int[] blockToPalette, int[] paletteToBlock, int[] blocksCopy, - int[] num_palette_buffer, char[] set, Map ticking_blocks) { + int[] num_palette_buffer, char[] set, Map ticking_blocks, boolean fastmode) { int air = 0; int num_palette = 0; + char lastOrdinal = BlockID.__RESERVED__; + boolean lastticking = false; + boolean tick_placed = Settings.IMP.EXPERIMENTAL.ALLOW_TICK_PLACED; for (int i = 0; i < 4096; i++) { char ordinal = set[i]; switch (ordinal) { @@ -24,11 +29,22 @@ public class NMSAdapter { air++; break; default: - BlockState state = BlockState.getFromOrdinal(ordinal); - if (state.getMaterial().isTicksRandomly()) { - ticking_blocks.put(BlockVector3.at(i & 15, (i >> 8) & 15, (i >> 4) & 15), - WorldEditPlugin.getInstance().getBukkitImplAdapter() - .getInternalBlockStateId(state).orElse(0)); + if (!fastmode && !tick_placed) { + boolean ticking; + if (ordinal != lastOrdinal) { + ticking = BlockTypesCache.ticking[ordinal]; + lastOrdinal = ordinal; + lastticking = ticking; + } else { + ticking = lastticking; + } + if (ticking) { + BlockState state = BlockState.getFromOrdinal(ordinal); + ticking_blocks + .put(BlockVector3.at(i & 15, (i >> 8) & 15, (i >> 4) & 15), + WorldEditPlugin.getInstance().getBukkitImplAdapter() + .getInternalBlockStateId(state).orElse(0)); + } } } int palette = blockToPalette[ordinal]; @@ -45,10 +61,14 @@ public class NMSAdapter { public static int createPalette(int layer, int[] blockToPalette, int[] paletteToBlock, int[] blocksCopy, int[] num_palette_buffer, Function get, char[] set, - Map ticking_blocks) { + Map ticking_blocks, boolean fastmode) { int air = 0; int num_palette = 0; char[] getArr = null; + char lastOrdinal = BlockID.__RESERVED__; + boolean lastticking = false; + boolean tick_placed = Settings.IMP.EXPERIMENTAL.ALLOW_TICK_PLACED; + boolean tick_existing = Settings.IMP.EXPERIMENTAL.ALLOW_TICK_EXISTING; for (int i = 0; i < 4096; i++) { char ordinal = set[i]; switch (ordinal) { @@ -65,6 +85,24 @@ public class NMSAdapter { case BlockID.VOID_AIR: air++; break; + default: + if (!fastmode && !tick_placed && tick_existing) { + boolean ticking; + if (ordinal != lastOrdinal) { + ticking = BlockTypesCache.ticking[ordinal]; + lastOrdinal = ordinal; + lastticking = ticking; + } else { + ticking = lastticking; + } + if (ticking) { + BlockState state = BlockState.getFromOrdinal(ordinal); + ticking_blocks + .put(BlockVector3.at(i & 15, (i >> 8) & 15, (i >> 4) & 15), + WorldEditPlugin.getInstance().getBukkitImplAdapter() + .getInternalBlockStateId(state).orElse(0)); + } + } } set[i] = ordinal; break; @@ -75,11 +113,21 @@ public class NMSAdapter { air++; break; } - BlockState state = BlockState.getFromOrdinal(ordinal); - if (state.getMaterial().isTicksRandomly()) { - ticking_blocks.put(BlockVector3.at(i & 15, (i >> 8) & 15, (i >> 4) & 15), - WorldEditPlugin.getInstance().getBukkitImplAdapter() - .getInternalBlockStateId(state).orElse(0)); + if (!fastmode && tick_placed) { + boolean ticking; + if (ordinal != lastOrdinal) { + ticking = BlockTypesCache.ticking[ordinal]; + lastOrdinal = ordinal; + lastticking = ticking; + } else { + ticking = lastticking; + } + if (ticking) { + BlockState state = BlockState.getFromOrdinal(ordinal); + ticking_blocks.put(BlockVector3.at(i & 15, (i >> 8) & 15, (i >> 4) & 15), + WorldEditPlugin.getInstance().getBukkitImplAdapter() + .getInternalBlockStateId(state).orElse(0)); + } } int palette = blockToPalette[ordinal]; if (palette == Integer.MAX_VALUE) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java index be718e964..7542e8257 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java @@ -199,11 +199,11 @@ public final class BukkitAdapter_1_14 extends NMSAdapter { /* NMS conversion */ - public static ChunkSection newChunkSection(final int layer, final char[] blocks) { - return newChunkSection(layer, null, blocks); + public static ChunkSection newChunkSection(final int layer, final char[] blocks, boolean fastmode) { + return newChunkSection(layer, null, blocks, fastmode); } - public static ChunkSection newChunkSection(final int layer, final Function get, char[] set) { + public static ChunkSection newChunkSection(final int layer, final Function get, char[] set, boolean fastmode) { if (set == null) { return newChunkSection(layer); } @@ -216,9 +216,9 @@ public final class BukkitAdapter_1_14 extends NMSAdapter { Map ticking_blocks = new HashMap<>(); int air; if (get == null) { - air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, set, ticking_blocks); + air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, set, ticking_blocks, fastmode); } else { - air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, get, set, ticking_blocks); + air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, get, set, ticking_blocks, fastmode); } int num_palette = num_palette_buffer[0]; // BlockStates diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java index 6514b8053..db000c51d 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java @@ -5,10 +5,12 @@ import static org.slf4j.LoggerFactory.getLogger; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.beta.implementation.blocks.CharBlocks; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; import com.boydti.fawe.beta.implementation.queue.QueueHandler; import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.mc1_14.nbt.LazyCompoundTag_1_14; +import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.AdaptedMap; import com.boydti.fawe.object.collection.BitArray; import com.google.common.base.Suppliers; @@ -227,6 +229,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { try { WorldServer nmsWorld = world; Chunk nmsChunk = ensureLoaded(nmsWorld, X, Z); + boolean fastmode = set.isFastMode() && Settings.IMP.QUEUE.NO_TICK_FASTMODE; // Remove existing tiles { @@ -262,7 +265,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { ChunkSection newSection; ChunkSection existingSection = sections[layer]; if (existingSection == null) { - newSection = BukkitAdapter_1_14.newChunkSection(layer, setArr); + newSection = BukkitAdapter_1_14.newChunkSection(layer, setArr, fastmode); if (BukkitAdapter_1_14.setSectionAtomic(sections, null, newSection, layer)) { updateGet(this, nmsChunk, sections, newSection, setArr, layer); continue; @@ -274,6 +277,10 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { } } } + + //ensure that the server doesn't try to tick the chunksection while we're editing it. + BukkitAdapter_1_14.fieldTickingBlockCount.set(existingSection, (short) 0); + DelegateLock lock = BukkitAdapter_1_14.applyLock(existingSection); synchronized (this) { synchronized (lock) { @@ -290,7 +297,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { } else if (lock.isModified()) { this.reset(layer); } - newSection = BukkitAdapter_1_14.newChunkSection(layer, this::load, setArr); + newSection = BukkitAdapter_1_14.newChunkSection(layer, this::load, setArr, fastmode); if (!BukkitAdapter_1_14.setSectionAtomic(sections, existingSection, newSection, layer)) { System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); continue; @@ -620,7 +627,37 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { if (aggressive) { sections = null; nmsChunk = null; + return super.trim(true); + } else { + for (int i = 0; i < 16; i++) { + if (!hasSection(i) || super.sections[i] == CharBlocks.EMPTY) { + continue; + } + ChunkSection existing = getSections()[i]; + try { + final DataPaletteBlock blocksExisting = existing.getBlocks(); + + final DataPalette palette = (DataPalette) BukkitAdapter_1_14.fieldPalette.get(blocksExisting); + int paletteSize; + + if (palette instanceof DataPaletteLinear) { + paletteSize = ((DataPaletteLinear) palette).b(); + } else if (palette instanceof DataPaletteHash) { + paletteSize = ((DataPaletteHash) palette).b(); + } else { + super.trim(false, i); + continue; + } + if (paletteSize == 1) { + //If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks. + continue; + } + super.trim(false, i); + } catch (IllegalAccessException ignored) { + super.trim(false, i); + } + } + return true; } - return super.trim(aggressive); } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitAdapter_1_15.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitAdapter_1_15.java index 92293e721..6e4c60a45 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitAdapter_1_15.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitAdapter_1_15.java @@ -186,11 +186,11 @@ public final class BukkitAdapter_1_15 extends NMSAdapter { /* NMS conversion */ - public static ChunkSection newChunkSection(final int layer, final char[] blocks) { - return newChunkSection(layer, null, blocks); + public static ChunkSection newChunkSection(final int layer, final char[] blocks, boolean fastmode) { + return newChunkSection(layer, null, blocks, fastmode); } - public static ChunkSection newChunkSection(final int layer, final Function get, char[] set) { + public static ChunkSection newChunkSection(final int layer, final Function get, char[] set, boolean fastmode) { if (set == null) { return newChunkSection(layer); } @@ -203,9 +203,9 @@ public final class BukkitAdapter_1_15 extends NMSAdapter { Map ticking_blocks = new HashMap<>(); int air; if (get == null) { - air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, set, ticking_blocks); + air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, set, ticking_blocks, fastmode); } else { - air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, get, set, ticking_blocks); + air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, get, set, ticking_blocks, fastmode); } int num_palette = num_palette_buffer[0]; // BlockStates diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java index 8f05ad098..68ef24f11 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java @@ -5,10 +5,12 @@ import static org.slf4j.LoggerFactory.getLogger; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.beta.implementation.blocks.CharBlocks; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; import com.boydti.fawe.beta.implementation.queue.QueueHandler; import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.mc1_15.nbt.LazyCompoundTag_1_15; +import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.AdaptedMap; import com.boydti.fawe.object.collection.BitArray; import com.google.common.base.Suppliers; @@ -235,6 +237,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { try { WorldServer nmsWorld = world; Chunk nmsChunk = ensureLoaded(nmsWorld, X, Z); + boolean fastmode = set.isFastMode() && Settings.IMP.QUEUE.NO_TICK_FASTMODE; // Remove existing tiles { @@ -270,7 +273,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { ChunkSection newSection; ChunkSection existingSection = sections[layer]; if (existingSection == null) { - newSection = BukkitAdapter_1_15.newChunkSection(layer, setArr); + newSection = BukkitAdapter_1_15.newChunkSection(layer, setArr, fastmode); if (BukkitAdapter_1_15.setSectionAtomic(sections, null, newSection, layer)) { updateGet(this, nmsChunk, sections, newSection, setArr, layer); continue; @@ -282,6 +285,10 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { } } } + + //ensure that the server doesn't try to tick the chunksection while we're editing it. + BukkitAdapter_1_15.fieldTickingBlockCount.set(existingSection, (short) 0); + DelegateLock lock = BukkitAdapter_1_15.applyLock(existingSection); synchronized (this) { synchronized (lock) { @@ -298,7 +305,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { } else if (lock.isModified()) { this.reset(layer); } - newSection = BukkitAdapter_1_15.newChunkSection(layer, this::load, setArr); + newSection = BukkitAdapter_1_15.newChunkSection(layer, this::load, setArr, fastmode); if (!BukkitAdapter_1_15.setSectionAtomic(sections, existingSection, newSection, layer)) { System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); continue; @@ -640,7 +647,37 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { if (aggressive) { sections = null; nmsChunk = null; + return super.trim(true); + } else { + for (int i = 0; i < 16; i++) { + if (!hasSection(i) || super.sections[i] == CharBlocks.EMPTY) { + continue; + } + ChunkSection existing = getSections()[i]; + try { + final DataPaletteBlock blocksExisting = existing.getBlocks(); + + final DataPalette palette = (DataPalette) BukkitAdapter_1_15.fieldPalette.get(blocksExisting); + int paletteSize; + + if (palette instanceof DataPaletteLinear) { + paletteSize = ((DataPaletteLinear) palette).b(); + } else if (palette instanceof DataPaletteHash) { + paletteSize = ((DataPaletteHash) palette).b(); + } else { + super.trim(false, i); + continue; + } + if (paletteSize == 1) { + //If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks. + continue; + } + super.trim(false, i); + } catch (IllegalAccessException ignored) { + super.trim(false, i); + } + } + return true; } - return super.trim(aggressive); } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitAdapter_1_15_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitAdapter_1_15_2.java index 578e023ed..4b5851de0 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitAdapter_1_15_2.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitAdapter_1_15_2.java @@ -187,11 +187,11 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter { /* NMS conversion */ - public static ChunkSection newChunkSection(final int layer, final char[] blocks) { - return newChunkSection(layer, null, blocks); + public static ChunkSection newChunkSection(final int layer, final char[] blocks, boolean fastmode) { + return newChunkSection(layer, null, blocks, fastmode); } - public static ChunkSection newChunkSection(final int layer, final Function get, char[] set) { + public static ChunkSection newChunkSection(final int layer, final Function get, char[] set, boolean fastmode) { if (set == null) { return newChunkSection(layer); } @@ -205,10 +205,10 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter { int air; if (get == null) { air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, - set, ticking_blocks); + set, ticking_blocks, fastmode); } else { air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, - num_palette_buffer, get, set, ticking_blocks); + num_palette_buffer, get, set, ticking_blocks, fastmode); } int num_palette = num_palette_buffer[0]; // BlockStates @@ -251,10 +251,11 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter { fieldPalette.set(dataPaletteBlocks, palette); fieldSize.set(dataPaletteBlocks, bitsPerEntry); setCount(ticking_blocks.size(), 4096 - air, section); - ticking_blocks.forEach((pos, ordinal) -> { - section.setType(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ(), - Block.getByCombinedId(ordinal)); - }); + if (!fastmode) { + ticking_blocks.forEach((pos, ordinal) -> section + .setType(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ(), + Block.getByCombinedId(ordinal))); + } } catch (final IllegalAccessException | NoSuchFieldException e) { throw new RuntimeException(e); } 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 cd4d6bc2a..87c3062a9 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 @@ -3,10 +3,12 @@ package com.boydti.fawe.bukkit.adapter.mc1_15_2; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.beta.implementation.blocks.CharBlocks; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; import com.boydti.fawe.beta.implementation.queue.QueueHandler; import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.mc1_15_2.nbt.LazyCompoundTag_1_15_2; +import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.AdaptedMap; import com.boydti.fawe.object.collection.BitArray; import com.google.common.base.Suppliers; @@ -242,6 +244,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { try { WorldServer nmsWorld = world; Chunk nmsChunk = ensureLoaded(nmsWorld, X, Z); + boolean fastmode = set.isFastMode() && Settings.IMP.QUEUE.NO_TICK_FASTMODE; // Remove existing tiles { @@ -282,7 +285,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { ChunkSection newSection; ChunkSection existingSection = sections[layer]; if (existingSection == null) { - newSection = BukkitAdapter_1_15_2.newChunkSection(layer, setArr); + newSection = BukkitAdapter_1_15_2.newChunkSection(layer, setArr, fastmode); if (BukkitAdapter_1_15_2.setSectionAtomic(sections, null, newSection, layer)) { updateGet(this, nmsChunk, sections, newSection, setArr, layer); continue; @@ -294,7 +297,11 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { } } } + BukkitAdapter_1_15_2.fieldTickingBlockCount.set(existingSection, (short) 0); + + //ensure that the server doesn't try to tick the chunksection while we're editing it. DelegateLock lock = BukkitAdapter_1_15_2.applyLock(existingSection); + synchronized (this) { synchronized (lock) { lock.untilFree(); @@ -310,7 +317,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { } else if (lock.isModified()) { this.reset(layer); } - newSection = BukkitAdapter_1_15_2.newChunkSection(layer, this::load, setArr); + newSection = BukkitAdapter_1_15_2.newChunkSection(layer, this::load, setArr, fastmode); if (!BukkitAdapter_1_15_2.setSectionAtomic(sections, existingSection, newSection, layer)) { System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); continue; @@ -644,7 +651,37 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { if (aggressive) { sections = null; nmsChunk = null; + return super.trim(true); + } else { + for (int i = 0; i < 16; i++) { + if (!hasSection(i) || super.sections[i] == CharBlocks.EMPTY) { + continue; + } + ChunkSection existing = getSections()[i]; + try { + final DataPaletteBlock blocksExisting = existing.getBlocks(); + + final DataPalette palette = (DataPalette) BukkitAdapter_1_15_2.fieldPalette.get(blocksExisting); + int paletteSize; + + if (palette instanceof DataPaletteLinear) { + paletteSize = ((DataPaletteLinear) palette).b(); + } else if (palette instanceof DataPaletteHash) { + paletteSize = ((DataPaletteHash) palette).b(); + } else { + super.trim(false, i); + continue; + } + if (paletteSize == 1) { + //If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks. + continue; + } + super.trim(false, i); + } catch (IllegalAccessException ignored) { + super.trim(false, i); + } + } + return true; } - return super.trim(aggressive); } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java index d451c5625..4252d3f32 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java @@ -55,6 +55,7 @@ import org.bukkit.entity.Item; import org.bukkit.entity.LightningStrike; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; +import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; @@ -649,6 +650,11 @@ public class AsyncWorld extends PassthroughExtent implements World { return TaskManager.IMP.sync(() -> parent.spawn(location, clazz, function)); } + @Override + public @NotNull T spawn(@NotNull Location location, @NotNull Class clazz, @Nullable Consumer function, CreatureSpawnEvent.@NotNull SpawnReason reason) throws IllegalArgumentException { + return null; + } + @Override public FallingBlock spawnFallingBlock(Location location, MaterialData data) throws IllegalArgumentException { return TaskManager.IMP.sync(() -> parent.spawnFallingBlock(location, data)); @@ -1053,6 +1059,21 @@ public class AsyncWorld extends PassthroughExtent implements World { return parent.getViewDistance(); } + @Override + public void setViewDistance(int viewDistance) { + + } + + @Override + public int getNoTickViewDistance() { + return 0; + } + + @Override + public void setNoTickViewDistance(int viewDistance) { + + } + @Override public RayTraceResult rayTrace(Location arg0, Vector arg1, double arg2, FluidCollisionMode arg3, boolean arg4, double arg5, Predicate arg6) { @@ -1174,6 +1195,11 @@ public class AsyncWorld extends PassthroughExtent implements World { return parent.getChunkAtAsync(arg0, arg1, arg2); } + @Override + public @NotNull CompletableFuture getChunkAtAsync(int x, int z, boolean gen, boolean urgent) { + return null; + } + @Override public boolean isDayTime() { return parent.isDayTime(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/CombinedBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/CombinedBlocks.java index 0915fed10..b5ae0014e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/CombinedBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/CombinedBlocks.java @@ -145,4 +145,9 @@ public class CombinedBlocks implements IBlocks { public boolean trim(boolean aggressive) { return false; } + + @Override + public boolean trim(boolean aggressive, int layer) { + return false; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java index 1f91546e7..c042e7a23 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java @@ -41,6 +41,8 @@ public interface IBlocks extends Trimable { .map(layer -> (1 << layer)).sum(); } + boolean trim(boolean aggressive, int layer); + IBlocks reset(); default byte[] toByteArray(boolean full) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java index 65b24872c..168120f46 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java @@ -3,14 +3,12 @@ package com.boydti.fawe.beta; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.extent.OutputExtent; import com.sk89q.worldedit.function.operation.Operation; -import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; -import java.util.Map; + +import javax.annotation.Nullable; import java.util.Set; import java.util.UUID; -import javax.annotation.Nullable; /** * Interface for setting blocks @@ -42,6 +40,13 @@ public interface IChunkSet extends IBlocks, OutputExtent { return getBiomes() != null; } + default boolean isFastMode() { + return false; + } + + //default to avoid tricky child classes. We only need it in a few cases anyway. + default void setFastMode(boolean fastMode){} + @Override IChunkSet reset(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java index a3e74a806..eed76600e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java @@ -82,6 +82,10 @@ public interface IQueueExtent extends Flushable, Trimable, ICh return BlockVector3.at(30000000, FaweCache.IMP.WORLD_MAX_Y, 30000000); } + void setFastMode(boolean fastMode); + + boolean isFastMode(); + /** * Create a new root IChunk object
- Full chunks will be reused, so a more optimized chunk * can be returned in that case
- Don't wrap the chunk, that should be done in {@link @@ -143,6 +147,7 @@ public interface IQueueExtent extends Flushable, Trimable, ICh T chunk = this.getOrCreateChunk(chunkX, chunkZ); // Initialize chunk.init(this, chunkX, chunkZ); + chunk.setFastMode(isFastMode()); T newChunk = filter.applyChunk(chunk, region); if (newChunk != null) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java index a3712a053..4d7a6a21b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java @@ -153,4 +153,9 @@ public class BitSetBlocks implements IChunkSet { public boolean trim(boolean aggressive) { return false; } + + @Override + public boolean trim(boolean aggressive, int layer) { + return false; + } } 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 ba49152ee..81a5c32cc 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 @@ -60,6 +60,17 @@ public abstract class CharBlocks implements IBlocks { return result; } + @Override + public boolean trim(boolean aggressive, int layer) { + boolean result = true; + if (sections[layer] == EMPTY && blocks[layer] != null) { + blocks[layer] = null; + } else { + result = false; + } + return result; + } + @Override public IChunkSet reset() { for (int i = 0; i < 16; i++) { 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 931c676be..f38d1bae4 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 @@ -25,6 +25,13 @@ public abstract class CharGetBlocks extends CharBlocks implements IChunkGet { return true; } + @Override + public boolean trim(boolean aggressive, int layer) { + sections[layer] = EMPTY; + blocks[layer] = null; + return true; + } + @Override public IChunkSet reset() { super.reset(); 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 39947092c..b7eda2724 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 @@ -34,6 +34,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { public BlockVector3ChunkMap tiles; public HashSet entities; public HashSet entityRemoves; + private boolean fastMode = false; private CharSetBlocks() {} @@ -131,6 +132,16 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { entityRemoves.add(uuid); } + @Override + public void setFastMode(boolean fastMode) { + this.fastMode = fastMode; + } + + @Override + public boolean isFastMode() { + return fastMode; + } + @Override public boolean isEmpty() { if (biomes != null) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java index 0c0ab3d44..c6b596f43 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java @@ -85,6 +85,11 @@ public class FallbackChunkGet implements IChunkGet { return true; } + @Override + public boolean trim(boolean aggressive, int layer) { + return true; + } + @Override public > T call(IChunkSet set, Runnable finalize) { for (int layer = 0; layer < 16; layer++) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.kt b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.kt index 77e85f715..a7a5e43de 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.kt +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.kt @@ -50,6 +50,10 @@ object NullChunkGet : IChunkGet { return true } + override fun trim(aggressive: Boolean, layer: Int): Boolean { + return true + } + override fun > call(set: IChunkSet, finalize: Runnable): T? { return null } 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 4442faff4..7449cbec0 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 @@ -42,6 +42,7 @@ public class ChunkHolder> implements IQueueChunk { private IQueueExtent extent; // the parent queue extent which has this chunk private int chunkX; private int chunkZ; + private boolean fastmode; private ChunkHolder() { this.delegate = NULL; @@ -100,6 +101,16 @@ public class ChunkHolder> implements IQueueChunk { return getOrCreateGet().load(layer); } + @Override + public boolean isFastMode() { + return fastmode; + } + + @Override + public void setFastMode(boolean fastmode) { + this.fastmode = fastmode; + } + @Override public CompoundTag getEntity(UUID uuid) { return delegate.get(this).getEntity(uuid); @@ -313,6 +324,7 @@ public class ChunkHolder> implements IQueueChunk { public void filterBlocks(Filter filter, ChunkFilterBlock block, @Nullable Region region, boolean full) { final IChunkGet get = getOrCreateGet(); final IChunkSet set = getOrCreateSet(); + set.setFastMode(fastmode); try { block.filter(this, get, set, filter, region, full); } finally { @@ -344,6 +356,11 @@ public class ChunkHolder> implements IQueueChunk { return false; } + @Override + public boolean trim(boolean aggressive, int layer) { + return this.trim(aggressive); + } + @Override public boolean isEmpty() { return chunkSet == null || chunkSet.isEmpty(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.kt b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.kt index 06a3d6e8c..6a6f33358 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.kt +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.kt @@ -117,5 +117,9 @@ object NullChunk : IQueueChunk { override fun trim(aggressive: Boolean): Boolean { return true } + + override fun trim(aggressive: Boolean, layer: Int): Boolean { + return true + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java index be64c28ea..0bd4346db 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java @@ -4,7 +4,6 @@ import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; -import com.sk89q.worldedit.extent.Extent; public class BatchProcessorHolder implements IBatchProcessorHolder { private IBatchProcessor processor = EmptyBatchProcessor.INSTANCE; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/ParallelQueueExtent.java index c93052360..b68207afe 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/ParallelQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/ParallelQueueExtent.java @@ -44,12 +44,14 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap private final QueueHandler handler; private final BatchProcessorHolder processor; private int changes; + private final boolean fastmode; - public ParallelQueueExtent(QueueHandler handler, World world) { + public ParallelQueueExtent(QueueHandler handler, World world, boolean fastmode) { super(handler.getQueue(world, new BatchProcessorHolder())); this.world = world; this.handler = handler; this.processor = (BatchProcessorHolder) getExtent().getProcessor(); + this.fastmode = fastmode; } @Override @@ -94,6 +96,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap final Filter newFilter = filter.fork(); // Create a chunk that we will reuse/reset for each operation final IQueueExtent queue = getNewQueue(); + queue.setFastMode(fastmode); synchronized (queue) { ChunkFilterBlock block = null; @@ -162,7 +165,8 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap @Override public int replaceBlocks(Region region, Mask mask, Pattern pattern) throws MaxChangedBlocksException { - return this.changes = apply(region, mask.toFilter(pattern), false).getBlocksApplied(); + boolean full = mask.replacesAir(); + return this.changes = apply(region, mask.toFilter(pattern), full).getBlocksApplied(); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java index 62a199e6a..d079d2c81 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java @@ -49,6 +49,8 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen private boolean enabledQueue = true; + private boolean fastmode = false; + /** * Safety check to ensure that the thread being used matches the one being initialized on. - Can * be removed later @@ -80,6 +82,16 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen return cacheSet.get(chunkX, chunkZ); } + @Override + public void setFastMode(boolean fastmode) { + this.fastmode = fastmode; + } + + @Override + public boolean isFastMode() { + return fastmode; + } + /** * Resets the queue. */ diff --git a/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java b/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java index 561f2330d..2dea1937d 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java @@ -309,6 +309,11 @@ public class Settings extends Config { }) public int DISCARD_AFTER_MS = 60000; + @Comment({ + "When using fastmode also do not bother to fix existing ticking blocks" + }) + public boolean NO_TICK_FASTMODE = true; + public static class PROGRESS { @Comment({"Display constant titles about the progress of a user's edit", " - false = disabled", @@ -369,6 +374,18 @@ public class Settings extends Config { "Other experimental features" }) public boolean OTHER = false; + + @Comment({ + "Allow blocks placed by WorldEdit to tick. This could cause the big lags.", + "This has no effect on existing blocks one way or the other." + }) + public boolean ALLOW_TICK_PLACED = false; + + @Comment({ + "Force re-ticking of existing blocks not edited by FAWE.", + "This will increase time taken slightly." + }) + public boolean ALLOW_TICK_EXISTING = true; } public static class WEB { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java index 66f60ac69..e8717012e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java @@ -549,6 +549,11 @@ public class MCAChunk implements IChunk { return isEmpty(); } + @Override + public boolean trim(boolean aggressive, int layer) { + return hasSection(layer); + } + @Override public CompoundTag getEntity(UUID uuid) { return this.entities.get(uuid); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java index 671e4b351..0a1507828 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java @@ -184,7 +184,11 @@ public class CPUOptimizedClipboard extends LinearClipboard { @Override public > boolean setBlock(int index, B block) { - states[index] = block.getOrdinalChar(); + char ordinal = block.getOrdinalChar(); + if (ordinal == 0) { + ordinal = 1; + } + states[index] = ordinal; boolean hasNbt = block instanceof BaseBlock && block.hasNbtData(); if (hasNbt) { setTile(index, block.getNbtData()); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java index 2b1c613fd..a66f9b84c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java @@ -388,6 +388,9 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable try { int index = HEADER_SIZE + (getIndex(x, y, z) << 1); char ordinal = block.getOrdinalChar(); + if (ordinal == 0) { + ordinal = 1; + } byteBuffer.putChar(index, ordinal); boolean hasNbt = block instanceof BaseBlock && block.hasNbtData(); if (hasNbt) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java index 825415310..7a30348e2 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java @@ -263,6 +263,9 @@ public class MemoryOptimizedClipboard extends LinearClipboard { @Override public > boolean setBlock(int index, B block) { int ordinal = block.getOrdinal(); + if (ordinal == 0) { + ordinal = 1; + } setOrdinal(index, ordinal); boolean hasNbt = block instanceof BaseBlock && block.hasNbtData(); if (hasNbt) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java b/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java index 1ca86ccbb..d2a84c00b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java @@ -296,7 +296,7 @@ public class EditSessionBuilder { if (unwrapped instanceof IQueueExtent) { extent = queue = (IQueueExtent) unwrapped; } else if (Settings.IMP.QUEUE.PARALLEL_THREADS > 1 && threaded) { - ParallelQueueExtent parallel = new ParallelQueueExtent(Fawe.get().getQueueHandler(), world); + ParallelQueueExtent parallel = new ParallelQueueExtent(Fawe.get().getQueueHandler(), world, fastmode); queue = parallel.getExtent(); extent = parallel; } else { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index 76baa9082..59817d210 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -72,6 +72,7 @@ import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; import com.sk89q.worldedit.world.World; import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; @@ -366,7 +367,7 @@ public class ClipboardCommands { e.printStackTrace(); } } - player.print(Caption.of("fawe.web.download.link" , urlText)); + player.print(Caption.of("fawe.web.download.link" , urlText).clickEvent(ClickEvent.openUrl(urlText))); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java index 8db4bdec4..e78809bf6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java @@ -134,8 +134,8 @@ public class GenerationCommands { @Arg(desc = "TODO", def = "100") int threshold, @Arg(desc = "BlockVector2", def = "") BlockVector2 dimensions) throws WorldEditException, IOException { TextureUtil tu = Fawe.get().getCachedTextureUtil(randomize, 0, threshold); URL url = new URL(argStr); - if (!url.getHost().equalsIgnoreCase("i.imgur.com") && !url.getHost().equalsIgnoreCase("empcraft.com")) { - throw new IOException("Only i.imgur.com or empcraft.com/ui links are allowed!"); + if (!url.getHost().equalsIgnoreCase("i.imgur.com")) { + throw new IOException("Only i.imgur.com links are allowed!"); } BufferedImage image = MainUtil.readImage(url); if (dimensions != null) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index d5e4f4b49..17f1e22b0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -212,6 +212,7 @@ public class SchematicCommands { } UUID uuid = UUID.fromString(filename.substring(4)); URL webUrl = new URL(Settings.IMP.WEB.URL); + format = ClipboardFormats.findByAlias(formatName); URL url = new URL(webUrl, "uploads/" + uuid + "." + format.getPrimaryFileExtension()); ReadableByteChannel byteChannel = Channels.newChannel(url.openStream()); in = Channels.newInputStream(byteChannel); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java index 659760670..31491b2b8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java @@ -364,6 +364,12 @@ public class DefaultBlockParser extends InputParser { } state = fuzzyBuilder.build(); } + } else { + for (Map.Entry, Object> blockState : blockStates.entrySet()) { + @SuppressWarnings("unchecked") + Property objProp = (Property) blockState.getKey(); + state = state.with(objProp, blockState.getValue()); + } } } // this should be impossible but IntelliJ isn't that smart diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicReader.java index 3b2332fb8..64cdf882e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicReader.java @@ -223,11 +223,13 @@ public class FastSchematicReader extends NBTSchematicReader { int volume = width * height * length; if (palette.length < 128) { for (int index = 0; index < volume; index++) { - linear.setBlock(index, getBlockState(fis.read())); + int ordinal = fis.read(); + linear.setBlock(index, getBlockState(ordinal)); } } else { for (int index = 0; index < volume; index++) { - linear.setBlock(index, getBlockState(fis.readVarInt())); + int ordinal = fis.readVarInt(); + linear.setBlock(index, getBlockState(ordinal)); } } } else { @@ -235,7 +237,8 @@ public class FastSchematicReader extends NBTSchematicReader { for (int y = 0; y < height; y++) { for (int z = 0; z < length; z++) { for (int x = 0; x < width; x++) { - clipboard.setBlock(x, y, z, getBlockState(fis.read())); + int ordinal = fis.read(); + clipboard.setBlock(x, y, z, getBlockState(ordinal)); } } } @@ -243,7 +246,8 @@ public class FastSchematicReader extends NBTSchematicReader { for (int y = 0; y < height; y++) { for (int z = 0; z < length; z++) { for (int x = 0; x < width; x++) { - clipboard.setBlock(x, y, z, getBlockState(fis.readVarInt())); + int ordinal = fis.readVarInt(); + clipboard.setBlock(x, y, z, getBlockState(ordinal)); } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicWriter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicWriter.java index b57a50448..3bbb2c02d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicWriter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicWriter.java @@ -21,7 +21,6 @@ package com.sk89q.worldedit.extent.clipboard.io; import com.boydti.fawe.jnbt.streamer.IntValueReader; import com.boydti.fawe.object.FaweOutputStream; -import com.boydti.fawe.object.clipboard.LinearClipboard; import com.boydti.fawe.util.IOUtil; import com.google.common.collect.Maps; import com.sk89q.jnbt.CompoundTag; @@ -45,7 +44,6 @@ 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.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypesCache; import net.jpountz.lz4.LZ4BlockInputStream; import net.jpountz.lz4.LZ4BlockOutputStream; @@ -181,6 +179,9 @@ public class FastSchematicWriter implements ClipboardWriter { } int ordinal = block.getOrdinal(); + if (ordinal == 0) { + ordinal = 1; + } char value = palette[ordinal]; if (value == Character.MAX_VALUE) { int size = paletteMax++; @@ -338,4 +339,4 @@ public class FastSchematicWriter implements ClipboardWriter { public void close() throws IOException { outputStream.close(); } -} \ No newline at end of file +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java index 550d95adf..e13026ba7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java @@ -189,7 +189,15 @@ public class BlockMask extends ABlockMask { @Override public boolean test(Extent extent, BlockVector3 vector) { - return ordinals[vector.getOrdinal(extent)]; + int test = vector.getOrdinal(extent); + return ordinals[test] || replacesAir() && test == 0; + } + + @Override + public boolean replacesAir() { + return ordinals[BlockTypes.AIR.getDefaultState().getOrdinal()] + || ordinals[BlockTypes.CAVE_AIR.getDefaultState().getOrdinal()] + || ordinals[BlockTypes.VOID_AIR.getDefaultState().getOrdinal()]; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java index b8bc72e8f..b3580631b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java @@ -26,6 +26,8 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; + import java.util.Map; import javax.annotation.Nullable; @@ -65,6 +67,11 @@ public class BlockStateMask extends AbstractExtentMask { .allMatch(entry -> block.getState(entry.getKey()) == entry.getValue()); } + @Override + public boolean replacesAir() { + return test(BlockTypes.AIR.getDefaultState()) || test(BlockTypes.CAVE_AIR.getDefaultState()) || test(BlockTypes.VOID_AIR.getDefaultState()); + } + @Nullable @Override public Mask2D toMask2D() { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java index c33db1738..2d420cd6c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java @@ -43,6 +43,7 @@ import org.jetbrains.annotations.NotNull; public class BlockTypeMask extends AbstractExtentMask { private final boolean[] types; + private boolean hasAir; /** * Create a new block mask. @@ -63,7 +64,9 @@ public class BlockTypeMask extends AbstractExtentMask { public BlockTypeMask(Extent extent, @NotNull BlockType... block) { super(extent); this.types = new boolean[BlockTypes.size()]; - for (BlockType type : block) this.types[type.getInternalId()] = true; + for (BlockType type : block) { + add(type); + } } /** @@ -76,9 +79,6 @@ public class BlockTypeMask extends AbstractExtentMask { for (BlockType type : blocks) { add(type); } - for (BlockType type : blocks) { - this.types[type.getInternalId()] = true; - } } /** @@ -88,6 +88,9 @@ public class BlockTypeMask extends AbstractExtentMask { */ public void add(@NotNull BlockType... block) { for (BlockType type : block) { + if (!hasAir && (type == BlockTypes.AIR || type == BlockTypes.CAVE_AIR || type == BlockTypes.VOID_AIR)) { + hasAir = true; + } this.types[type.getInternalId()] = true; } } @@ -110,6 +113,11 @@ public class BlockTypeMask extends AbstractExtentMask { return test(vector.getBlock(extent).getBlockType()); } + @Override + public boolean replacesAir() { + return hasAir; + } + public boolean test(BlockType block) { return types[block.getInternalId()]; } 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 7267cb13c..8a9c9a4aa 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 @@ -123,4 +123,8 @@ public interface Mask { } }; } + + default boolean replacesAir() { + return false; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java index 5fab17454..d6a3dc7f6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java @@ -88,7 +88,11 @@ public class OffsetMask extends AbstractMask { @Override public boolean test(Extent extent, BlockVector3 pos) { - return getMask().test(extent, pos); + BlockVector3 testPos = pos.add(offset); + if (testPos.getBlockY() < 0 || testPos.getBlockY() > 255) { + return false; + } + return getMask().test(extent, pos.add(offset)); } @Nullable diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockStateMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockStateMask.java index ff022f7c7..f776335ac 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockStateMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockStateMask.java @@ -1,13 +1,12 @@ package com.sk89q.worldedit.function.mask; -import com.boydti.fawe.Fawe; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockStateHolder; public class SingleBlockStateMask extends ABlockMask { private final char ordinal; + private final boolean isAir; public BlockState getBlockState() { return BlockState.getFromOrdinal(ordinal); @@ -15,12 +14,14 @@ public class SingleBlockStateMask extends ABlockMask { public SingleBlockStateMask(Extent extent, BlockState state) { super(extent); + isAir = state.isAir(); this.ordinal = state.getOrdinalChar(); } @Override public boolean test(Extent extent, BlockVector3 vector) { - return ordinal == vector.getOrdinal(extent); + int test = vector.getOrdinal(extent); + return ordinal == test || isAir && test == 0; } @Override @@ -33,6 +34,11 @@ public class SingleBlockStateMask extends ABlockMask { return new InverseSingleBlockStateMask(getExtent(), BlockState.getFromOrdinal(ordinal)); } + @Override + public boolean replacesAir() { + return isAir; + } + @Override public Mask tryCombine(Mask mask) { if (mask instanceof ABlockMask) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java index 01d4aad7c..466f51c7e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java @@ -8,10 +8,12 @@ import com.sk89q.worldedit.world.block.BlockTypesCache; public class SingleBlockTypeMask extends ABlockMask { private final int internalId; + private final boolean isAir; public SingleBlockTypeMask(Extent extent, BlockType type) { super(extent); - this.internalId = type.getInternalId(); + isAir = type == BlockTypes.AIR || type == BlockTypes.CAVE_AIR || type == BlockTypes.VOID_AIR; + this.internalId = type.getInternalId(); } @Override @@ -27,4 +29,9 @@ public class SingleBlockTypeMask extends ABlockMask { public BlockType getBlockType() { return BlockTypes.get(internalId); } + + @Override + public boolean replacesAir() { + return isAir; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypesCache.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypesCache.java index 4fb47affe..897340388 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypesCache.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypesCache.java @@ -1,6 +1,7 @@ package com.sk89q.worldedit.world.block; import com.boydti.fawe.util.MathMan; +import com.google.common.primitives.Booleans; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; @@ -166,12 +167,14 @@ public class BlockTypesCache { public static final BlockType[] values; public static final BlockState[] states; + public static final boolean[] ticking; protected static final Set $NAMESPACES = new LinkedHashSet<>(); static { try { ArrayList stateList = new ArrayList<>(); + ArrayList tickList = new ArrayList<>(); Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS); Registries registries = platform.getRegistries(); @@ -202,7 +205,7 @@ public class BlockTypesCache { if (values[internalId] != null) { throw new IllegalStateException("Invalid duplicate id for " + field.getName()); } - BlockType type = register(defaultState, internalId, stateList); + BlockType type = register(defaultState, internalId, stateList, tickList); // Note: Throws IndexOutOfBoundsError if nothing is registered and blocksMap is empty values[internalId] = type; } @@ -214,7 +217,7 @@ public class BlockTypesCache { String defaultState = entry.getValue(); // Skip already registered ids for (; values[internalId] != null; internalId++); - BlockType type = register(defaultState, internalId, stateList); + BlockType type = register(defaultState, internalId, stateList, tickList); values[internalId] = type; } } @@ -223,7 +226,7 @@ public class BlockTypesCache { } states = stateList.toArray(new BlockState[stateList.size()]); - + ticking = Booleans.toArray(tickList); } catch (Throwable e) { e.printStackTrace(); @@ -231,12 +234,14 @@ public class BlockTypesCache { } } - private static BlockType register(final String id, int internalId, List states) { + private static BlockType register(final String id, int internalId, List states, List tickList) { // Get the enum name (remove namespace if minecraft:) int propStart = id.indexOf('['); String typeName = id.substring(0, propStart == -1 ? id.length() : propStart); String enumName = (typeName.startsWith("minecraft:") ? typeName.substring(10) : typeName).toUpperCase(Locale.ROOT); + int oldsize = states.size(); BlockType existing = new BlockType(id, internalId, states); + tickList.addAll(Collections.nCopies(states.size() - oldsize, existing.getMaterial().isTicksRandomly())); // register states BlockType.REGISTRY.register(typeName, existing); String nameSpace = typeName.substring(0, typeName.indexOf(':')); diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index 819f15ddf..f0abf6b9a 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -72,10 +72,10 @@ "fawe.worldedit.history.command.history.clear": "History cleared", "fawe.worldedit.history.command.redo.error": "Nothing left to redo. (See also `/inspect` and `/frb`)", "fawe.worldedit.history.command.history.other.error": "Unable to find session for {0}.", - "fawe.worldedit.history.command.redo.success": "Redo successful{0}.", + "fawe.worldedit.history.command.redo.success": "Redo successful {0}.", "fawe.worldedit.history.command.undo.error": "Nothing left to undo. (See also `/inspect` and `/frb`)", "fawe.worldedit.history.command.undo.disabled": "Undo disabled, use: //fast", - "fawe.worldedit.history.command.undo.success": "Undo successful{0}.", + "fawe.worldedit.history.command.undo.success": "Undo successful {0}.", "fawe.worldedit.operation.operation": "Operation queued ({0})", @@ -97,7 +97,7 @@ "fawe.worldedit.selection.selection.shift": "Region shifted", "fawe.worldedit.selection.selection.cleared": "Selection cleared", - "fawe.worldedit.anvil.world.is.loaded": "The world shouldn't be in use when executing. Unload the world, or use use -f to override (save first)", + "fawe.worldedit.anvil.world.is.loaded": "The world shouldn't be in use when executing. Unload the world, or use -f to override (save first)", "fawe.worldedit.brush.brush.reset": "Reset your brush. (SHIFT + Click)", "fawe.worldedit.brush.brush.none": "You aren't holding a brush!", @@ -238,7 +238,7 @@ "fawe.error.command.syntax": "Usage: {0}", "fawe.error.no-perm": "You are lacking the permission node: {0}", - "fawe.error.block.not.allowed": "You are not allowed to use", + "fawe.error.block.not.allowed": "You are not allowed to use: {0}", "fawe.error.setting.disable": "Lacking setting: {0}", "fawe.error.brush.not.found": "Available brushes: {0}", "fawe.error.brush.incompatible": "Brush not compatible with this version", @@ -373,7 +373,7 @@ "worldedit.limit.set": "Block change limit set to {0}.", "worldedit.limit.return-to-default": "(Use //limit to go back to the default.)", "worldedit.timeout.too-high": "Your maximum allowable timeout is {0}ms.", - "worldedit.timeout.set": "Timeout time set to {0} ms.", + "worldedit.timeout.set": "Timeout time set to {0}ms.", "worldedit.timeout.return-to-default": " (Use //timeout to go back to the default.)", "worldedit.fast.disabled": "Fast mode disabled.", "worldedit.fast.enabled": "Fast mode enabled. Lighting in the affected chunks may be wrong and/or you may need to rejoin to see changes.",