This commit is contained in:
IronApollo 2020-05-13 17:45:17 -04:00
commit 34d40cb856
49 changed files with 471 additions and 96 deletions

View File

@ -12,6 +12,7 @@ FAWE is a fork of WorldEdit that has huge speed and memory improvements and cons
* [Discord](https://discord.gg/KxkjDVg) * [Discord](https://discord.gg/KxkjDVg)
* [Wiki](https://github.com/IntellectualSites/FastAsyncWorldEdit-1.13/wiki) * [Wiki](https://github.com/IntellectualSites/FastAsyncWorldEdit-1.13/wiki)
* [Report Issue](https://github.com/IntellectualSites/FastAsyncWorldEdit-1.13/issues) * [Report Issue](https://github.com/IntellectualSites/FastAsyncWorldEdit-1.13/issues)
* [Crowdin](https://intellectualsites.crowdin.com/fastasyncworldedit)
## Downloads ## Downloads
### 1.13+ ### 1.13+

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

29
gradlew vendored
View File

@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
else else
eval `echo args$i`="\"$arg\"" eval `echo args$i`="\"$arg\""
fi fi
i=$((i+1)) i=`expr $i + 1`
done done
case $i in case $i in
(0) set -- ;; 0) set -- ;;
(1) set -- "$args0" ;; 1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;; 2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;; 3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;; 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
@ -175,14 +175,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " " echo " "
} }
APP_ARGS=$(save "$@") APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules # 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" 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" "$@" exec "$JAVACMD" "$@"

3
gradlew.bat vendored
View File

@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% 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. @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" set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

View File

@ -1,18 +1,23 @@
package com.boydti.fawe.bukkit.adapter; package com.boydti.fawe.bukkit.adapter;
import com.boydti.fawe.config.Settings;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockID; import com.sk89q.worldedit.world.block.BlockID;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
public class NMSAdapter { public class NMSAdapter {
public static int createPalette(int[] blockToPalette, int[] paletteToBlock, int[] blocksCopy, public static int createPalette(int[] blockToPalette, int[] paletteToBlock, int[] blocksCopy,
int[] num_palette_buffer, char[] set, Map<BlockVector3, Integer> ticking_blocks) { int[] num_palette_buffer, char[] set, Map<BlockVector3, Integer> ticking_blocks, boolean fastmode) {
int air = 0; int air = 0;
int num_palette = 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++) { for (int i = 0; i < 4096; i++) {
char ordinal = set[i]; char ordinal = set[i];
switch (ordinal) { switch (ordinal) {
@ -24,11 +29,22 @@ public class NMSAdapter {
air++; air++;
break; break;
default: default:
BlockState state = BlockState.getFromOrdinal(ordinal); if (!fastmode && !tick_placed) {
if (state.getMaterial().isTicksRandomly()) { boolean ticking;
ticking_blocks.put(BlockVector3.at(i & 15, (i >> 8) & 15, (i >> 4) & 15), if (ordinal != lastOrdinal) {
WorldEditPlugin.getInstance().getBukkitImplAdapter() ticking = BlockTypesCache.ticking[ordinal];
.getInternalBlockStateId(state).orElse(0)); 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]; int palette = blockToPalette[ordinal];
@ -45,10 +61,14 @@ public class NMSAdapter {
public static int createPalette(int layer, int[] blockToPalette, int[] paletteToBlock, public static int createPalette(int layer, int[] blockToPalette, int[] paletteToBlock,
int[] blocksCopy, int[] num_palette_buffer, Function<Integer, char[]> get, char[] set, int[] blocksCopy, int[] num_palette_buffer, Function<Integer, char[]> get, char[] set,
Map<BlockVector3, Integer> ticking_blocks) { Map<BlockVector3, Integer> ticking_blocks, boolean fastmode) {
int air = 0; int air = 0;
int num_palette = 0; int num_palette = 0;
char[] getArr = null; 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++) { for (int i = 0; i < 4096; i++) {
char ordinal = set[i]; char ordinal = set[i];
switch (ordinal) { switch (ordinal) {
@ -65,6 +85,24 @@ public class NMSAdapter {
case BlockID.VOID_AIR: case BlockID.VOID_AIR:
air++; air++;
break; 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; set[i] = ordinal;
break; break;
@ -75,11 +113,21 @@ public class NMSAdapter {
air++; air++;
break; break;
} }
BlockState state = BlockState.getFromOrdinal(ordinal); if (!fastmode && tick_placed) {
if (state.getMaterial().isTicksRandomly()) { boolean ticking;
ticking_blocks.put(BlockVector3.at(i & 15, (i >> 8) & 15, (i >> 4) & 15), if (ordinal != lastOrdinal) {
WorldEditPlugin.getInstance().getBukkitImplAdapter() ticking = BlockTypesCache.ticking[ordinal];
.getInternalBlockStateId(state).orElse(0)); 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]; int palette = blockToPalette[ordinal];
if (palette == Integer.MAX_VALUE) { if (palette == Integer.MAX_VALUE) {

View File

@ -199,11 +199,11 @@ public final class BukkitAdapter_1_14 extends NMSAdapter {
/* /*
NMS conversion NMS conversion
*/ */
public static ChunkSection newChunkSection(final int layer, final char[] blocks) { public static ChunkSection newChunkSection(final int layer, final char[] blocks, boolean fastmode) {
return newChunkSection(layer, null, blocks); return newChunkSection(layer, null, blocks, fastmode);
} }
public static ChunkSection newChunkSection(final int layer, final Function<Integer, char[]> get, char[] set) { public static ChunkSection newChunkSection(final int layer, final Function<Integer, char[]> get, char[] set, boolean fastmode) {
if (set == null) { if (set == null) {
return newChunkSection(layer); return newChunkSection(layer);
} }
@ -216,9 +216,9 @@ public final class BukkitAdapter_1_14 extends NMSAdapter {
Map<BlockVector3, Integer> ticking_blocks = new HashMap<>(); Map<BlockVector3, Integer> ticking_blocks = new HashMap<>();
int air; int air;
if (get == null) { 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 { } 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]; int num_palette = num_palette_buffer[0];
// BlockStates // BlockStates

View File

@ -5,10 +5,12 @@ import static org.slf4j.LoggerFactory.getLogger;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IChunkSet; 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.blocks.CharGetBlocks;
import com.boydti.fawe.beta.implementation.queue.QueueHandler; import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.DelegateLock;
import com.boydti.fawe.bukkit.adapter.mc1_14.nbt.LazyCompoundTag_1_14; 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.AdaptedMap;
import com.boydti.fawe.object.collection.BitArray; import com.boydti.fawe.object.collection.BitArray;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
@ -227,6 +229,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
try { try {
WorldServer nmsWorld = world; WorldServer nmsWorld = world;
Chunk nmsChunk = ensureLoaded(nmsWorld, X, Z); Chunk nmsChunk = ensureLoaded(nmsWorld, X, Z);
boolean fastmode = set.isFastMode() && Settings.IMP.QUEUE.NO_TICK_FASTMODE;
// Remove existing tiles // Remove existing tiles
{ {
@ -262,7 +265,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
ChunkSection newSection; ChunkSection newSection;
ChunkSection existingSection = sections[layer]; ChunkSection existingSection = sections[layer];
if (existingSection == null) { 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)) { if (BukkitAdapter_1_14.setSectionAtomic(sections, null, newSection, layer)) {
updateGet(this, nmsChunk, sections, newSection, setArr, layer); updateGet(this, nmsChunk, sections, newSection, setArr, layer);
continue; 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); DelegateLock lock = BukkitAdapter_1_14.applyLock(existingSection);
synchronized (this) { synchronized (this) {
synchronized (lock) { synchronized (lock) {
@ -290,7 +297,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
} else if (lock.isModified()) { } else if (lock.isModified()) {
this.reset(layer); 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)) { if (!BukkitAdapter_1_14.setSectionAtomic(sections, existingSection, newSection, layer)) {
System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer);
continue; continue;
@ -620,7 +627,37 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
if (aggressive) { if (aggressive) {
sections = null; sections = null;
nmsChunk = 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<IBlockData> blocksExisting = existing.getBlocks();
final DataPalette<IBlockData> palette = (DataPalette<IBlockData>) BukkitAdapter_1_14.fieldPalette.get(blocksExisting);
int paletteSize;
if (palette instanceof DataPaletteLinear) {
paletteSize = ((DataPaletteLinear<IBlockData>) palette).b();
} else if (palette instanceof DataPaletteHash) {
paletteSize = ((DataPaletteHash<IBlockData>) 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);
} }
} }

View File

@ -186,11 +186,11 @@ public final class BukkitAdapter_1_15 extends NMSAdapter {
/* /*
NMS conversion NMS conversion
*/ */
public static ChunkSection newChunkSection(final int layer, final char[] blocks) { public static ChunkSection newChunkSection(final int layer, final char[] blocks, boolean fastmode) {
return newChunkSection(layer, null, blocks); return newChunkSection(layer, null, blocks, fastmode);
} }
public static ChunkSection newChunkSection(final int layer, final Function<Integer, char[]> get, char[] set) { public static ChunkSection newChunkSection(final int layer, final Function<Integer, char[]> get, char[] set, boolean fastmode) {
if (set == null) { if (set == null) {
return newChunkSection(layer); return newChunkSection(layer);
} }
@ -203,9 +203,9 @@ public final class BukkitAdapter_1_15 extends NMSAdapter {
Map<BlockVector3, Integer> ticking_blocks = new HashMap<>(); Map<BlockVector3, Integer> ticking_blocks = new HashMap<>();
int air; int air;
if (get == null) { 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 { } 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]; int num_palette = num_palette_buffer[0];
// BlockStates // BlockStates

View File

@ -5,10 +5,12 @@ import static org.slf4j.LoggerFactory.getLogger;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IChunkSet; 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.blocks.CharGetBlocks;
import com.boydti.fawe.beta.implementation.queue.QueueHandler; import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.DelegateLock;
import com.boydti.fawe.bukkit.adapter.mc1_15.nbt.LazyCompoundTag_1_15; 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.AdaptedMap;
import com.boydti.fawe.object.collection.BitArray; import com.boydti.fawe.object.collection.BitArray;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
@ -235,6 +237,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
try { try {
WorldServer nmsWorld = world; WorldServer nmsWorld = world;
Chunk nmsChunk = ensureLoaded(nmsWorld, X, Z); Chunk nmsChunk = ensureLoaded(nmsWorld, X, Z);
boolean fastmode = set.isFastMode() && Settings.IMP.QUEUE.NO_TICK_FASTMODE;
// Remove existing tiles // Remove existing tiles
{ {
@ -270,7 +273,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
ChunkSection newSection; ChunkSection newSection;
ChunkSection existingSection = sections[layer]; ChunkSection existingSection = sections[layer];
if (existingSection == null) { 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)) { if (BukkitAdapter_1_15.setSectionAtomic(sections, null, newSection, layer)) {
updateGet(this, nmsChunk, sections, newSection, setArr, layer); updateGet(this, nmsChunk, sections, newSection, setArr, layer);
continue; 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); DelegateLock lock = BukkitAdapter_1_15.applyLock(existingSection);
synchronized (this) { synchronized (this) {
synchronized (lock) { synchronized (lock) {
@ -298,7 +305,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
} else if (lock.isModified()) { } else if (lock.isModified()) {
this.reset(layer); 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)) { if (!BukkitAdapter_1_15.setSectionAtomic(sections, existingSection, newSection, layer)) {
System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer);
continue; continue;
@ -640,7 +647,37 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
if (aggressive) { if (aggressive) {
sections = null; sections = null;
nmsChunk = 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<IBlockData> blocksExisting = existing.getBlocks();
final DataPalette<IBlockData> palette = (DataPalette<IBlockData>) BukkitAdapter_1_15.fieldPalette.get(blocksExisting);
int paletteSize;
if (palette instanceof DataPaletteLinear) {
paletteSize = ((DataPaletteLinear<IBlockData>) palette).b();
} else if (palette instanceof DataPaletteHash) {
paletteSize = ((DataPaletteHash<IBlockData>) 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);
} }
} }

View File

@ -187,11 +187,11 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
/* /*
NMS conversion NMS conversion
*/ */
public static ChunkSection newChunkSection(final int layer, final char[] blocks) { public static ChunkSection newChunkSection(final int layer, final char[] blocks, boolean fastmode) {
return newChunkSection(layer, null, blocks); return newChunkSection(layer, null, blocks, fastmode);
} }
public static ChunkSection newChunkSection(final int layer, final Function<Integer, char[]> get, char[] set) { public static ChunkSection newChunkSection(final int layer, final Function<Integer, char[]> get, char[] set, boolean fastmode) {
if (set == null) { if (set == null) {
return newChunkSection(layer); return newChunkSection(layer);
} }
@ -205,10 +205,10 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
int air; int air;
if (get == null) { if (get == null) {
air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer,
set, ticking_blocks); set, ticking_blocks, fastmode);
} else { } else {
air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, 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]; int num_palette = num_palette_buffer[0];
// BlockStates // BlockStates
@ -251,10 +251,11 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
fieldPalette.set(dataPaletteBlocks, palette); fieldPalette.set(dataPaletteBlocks, palette);
fieldSize.set(dataPaletteBlocks, bitsPerEntry); fieldSize.set(dataPaletteBlocks, bitsPerEntry);
setCount(ticking_blocks.size(), 4096 - air, section); setCount(ticking_blocks.size(), 4096 - air, section);
ticking_blocks.forEach((pos, ordinal) -> { if (!fastmode) {
section.setType(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ(), ticking_blocks.forEach((pos, ordinal) -> section
Block.getByCombinedId(ordinal)); .setType(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ(),
}); Block.getByCombinedId(ordinal)));
}
} catch (final IllegalAccessException | NoSuchFieldException e) { } catch (final IllegalAccessException | NoSuchFieldException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@ -3,10 +3,12 @@ package com.boydti.fawe.bukkit.adapter.mc1_15_2;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IChunkSet; 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.blocks.CharGetBlocks;
import com.boydti.fawe.beta.implementation.queue.QueueHandler; import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.bukkit.adapter.DelegateLock; 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.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.AdaptedMap;
import com.boydti.fawe.object.collection.BitArray; import com.boydti.fawe.object.collection.BitArray;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
@ -242,6 +244,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
try { try {
WorldServer nmsWorld = world; WorldServer nmsWorld = world;
Chunk nmsChunk = ensureLoaded(nmsWorld, X, Z); Chunk nmsChunk = ensureLoaded(nmsWorld, X, Z);
boolean fastmode = set.isFastMode() && Settings.IMP.QUEUE.NO_TICK_FASTMODE;
// Remove existing tiles // Remove existing tiles
{ {
@ -282,7 +285,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
ChunkSection newSection; ChunkSection newSection;
ChunkSection existingSection = sections[layer]; ChunkSection existingSection = sections[layer];
if (existingSection == null) { 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)) { if (BukkitAdapter_1_15_2.setSectionAtomic(sections, null, newSection, layer)) {
updateGet(this, nmsChunk, sections, newSection, setArr, layer); updateGet(this, nmsChunk, sections, newSection, setArr, layer);
continue; 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); DelegateLock lock = BukkitAdapter_1_15_2.applyLock(existingSection);
synchronized (this) { synchronized (this) {
synchronized (lock) { synchronized (lock) {
lock.untilFree(); lock.untilFree();
@ -310,7 +317,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
} else if (lock.isModified()) { } else if (lock.isModified()) {
this.reset(layer); 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)) { if (!BukkitAdapter_1_15_2.setSectionAtomic(sections, existingSection, newSection, layer)) {
System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer);
continue; continue;
@ -644,7 +651,37 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
if (aggressive) { if (aggressive) {
sections = null; sections = null;
nmsChunk = 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<IBlockData> blocksExisting = existing.getBlocks();
final DataPalette<IBlockData> palette = (DataPalette<IBlockData>) BukkitAdapter_1_15_2.fieldPalette.get(blocksExisting);
int paletteSize;
if (palette instanceof DataPaletteLinear) {
paletteSize = ((DataPaletteLinear<IBlockData>) palette).b();
} else if (palette instanceof DataPaletteHash) {
paletteSize = ((DataPaletteHash<IBlockData>) 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);
} }
} }

View File

@ -55,6 +55,7 @@ import org.bukkit.entity.Item;
import org.bukkit.entity.LightningStrike; import org.bukkit.entity.LightningStrike;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack; 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)); return TaskManager.IMP.sync(() -> parent.spawn(location, clazz, function));
} }
@Override
public <T extends Entity> @NotNull T spawn(@NotNull Location location, @NotNull Class<T> clazz, @Nullable Consumer<T> function, CreatureSpawnEvent.@NotNull SpawnReason reason) throws IllegalArgumentException {
return null;
}
@Override @Override
public FallingBlock spawnFallingBlock(Location location, MaterialData data) throws IllegalArgumentException { public FallingBlock spawnFallingBlock(Location location, MaterialData data) throws IllegalArgumentException {
return TaskManager.IMP.sync(() -> parent.spawnFallingBlock(location, data)); return TaskManager.IMP.sync(() -> parent.spawnFallingBlock(location, data));
@ -1053,6 +1059,21 @@ public class AsyncWorld extends PassthroughExtent implements World {
return parent.getViewDistance(); return parent.getViewDistance();
} }
@Override
public void setViewDistance(int viewDistance) {
}
@Override
public int getNoTickViewDistance() {
return 0;
}
@Override
public void setNoTickViewDistance(int viewDistance) {
}
@Override @Override
public RayTraceResult rayTrace(Location arg0, Vector arg1, double arg2, FluidCollisionMode arg3, boolean arg4, public RayTraceResult rayTrace(Location arg0, Vector arg1, double arg2, FluidCollisionMode arg3, boolean arg4,
double arg5, Predicate<Entity> arg6) { double arg5, Predicate<Entity> arg6) {
@ -1174,6 +1195,11 @@ public class AsyncWorld extends PassthroughExtent implements World {
return parent.getChunkAtAsync(arg0, arg1, arg2); return parent.getChunkAtAsync(arg0, arg1, arg2);
} }
@Override
public @NotNull CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent) {
return null;
}
@Override @Override
public boolean isDayTime() { public boolean isDayTime() {
return parent.isDayTime(); return parent.isDayTime();

View File

@ -145,4 +145,9 @@ public class CombinedBlocks implements IBlocks {
public boolean trim(boolean aggressive) { public boolean trim(boolean aggressive) {
return false; return false;
} }
@Override
public boolean trim(boolean aggressive, int layer) {
return false;
}
} }

View File

@ -41,6 +41,8 @@ public interface IBlocks extends Trimable {
.map(layer -> (1 << layer)).sum(); .map(layer -> (1 << layer)).sum();
} }
boolean trim(boolean aggressive, int layer);
IBlocks reset(); IBlocks reset();
default byte[] toByteArray(boolean full) { default byte[] toByteArray(boolean full) {

View File

@ -3,14 +3,12 @@ package com.boydti.fawe.beta;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.extent.OutputExtent; import com.sk89q.worldedit.extent.OutputExtent;
import com.sk89q.worldedit.function.operation.Operation; 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.biome.BiomeType;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.Map;
import javax.annotation.Nullable;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nullable;
/** /**
* Interface for setting blocks * Interface for setting blocks
@ -42,6 +40,13 @@ public interface IChunkSet extends IBlocks, OutputExtent {
return getBiomes() != null; 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 @Override
IChunkSet reset(); IChunkSet reset();

View File

@ -82,6 +82,10 @@ public interface IQueueExtent<T extends IChunk> extends Flushable, Trimable, ICh
return BlockVector3.at(30000000, FaweCache.IMP.WORLD_MAX_Y, 30000000); return BlockVector3.at(30000000, FaweCache.IMP.WORLD_MAX_Y, 30000000);
} }
void setFastMode(boolean fastMode);
boolean isFastMode();
/** /**
* Create a new root IChunk object<br> - Full chunks will be reused, so a more optimized chunk * Create a new root IChunk object<br> - Full chunks will be reused, so a more optimized chunk
* can be returned in that case<br> - Don't wrap the chunk, that should be done in {@link * can be returned in that case<br> - Don't wrap the chunk, that should be done in {@link
@ -143,6 +147,7 @@ public interface IQueueExtent<T extends IChunk> extends Flushable, Trimable, ICh
T chunk = this.getOrCreateChunk(chunkX, chunkZ); T chunk = this.getOrCreateChunk(chunkX, chunkZ);
// Initialize // Initialize
chunk.init(this, chunkX, chunkZ); chunk.init(this, chunkX, chunkZ);
chunk.setFastMode(isFastMode());
T newChunk = filter.applyChunk(chunk, region); T newChunk = filter.applyChunk(chunk, region);
if (newChunk != null) { if (newChunk != null) {

View File

@ -153,4 +153,9 @@ public class BitSetBlocks implements IChunkSet {
public boolean trim(boolean aggressive) { public boolean trim(boolean aggressive) {
return false; return false;
} }
@Override
public boolean trim(boolean aggressive, int layer) {
return false;
}
} }

View File

@ -60,6 +60,17 @@ public abstract class CharBlocks implements IBlocks {
return result; 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 @Override
public IChunkSet reset() { public IChunkSet reset() {
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {

View File

@ -25,6 +25,13 @@ public abstract class CharGetBlocks extends CharBlocks implements IChunkGet {
return true; return true;
} }
@Override
public boolean trim(boolean aggressive, int layer) {
sections[layer] = EMPTY;
blocks[layer] = null;
return true;
}
@Override @Override
public IChunkSet reset() { public IChunkSet reset() {
super.reset(); super.reset();

View File

@ -34,6 +34,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
public BlockVector3ChunkMap<CompoundTag> tiles; public BlockVector3ChunkMap<CompoundTag> tiles;
public HashSet<CompoundTag> entities; public HashSet<CompoundTag> entities;
public HashSet<UUID> entityRemoves; public HashSet<UUID> entityRemoves;
private boolean fastMode = false;
private CharSetBlocks() {} private CharSetBlocks() {}
@ -131,6 +132,16 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
entityRemoves.add(uuid); entityRemoves.add(uuid);
} }
@Override
public void setFastMode(boolean fastMode) {
this.fastMode = fastMode;
}
@Override
public boolean isFastMode() {
return fastMode;
}
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
if (biomes != null) { if (biomes != null) {

View File

@ -85,6 +85,11 @@ public class FallbackChunkGet implements IChunkGet {
return true; return true;
} }
@Override
public boolean trim(boolean aggressive, int layer) {
return true;
}
@Override @Override
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) { public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
for (int layer = 0; layer < 16; layer++) { for (int layer = 0; layer < 16; layer++) {

View File

@ -50,6 +50,10 @@ object NullChunkGet : IChunkGet {
return true return true
} }
override fun trim(aggressive: Boolean, layer: Int): Boolean {
return true
}
override fun <T : Future<T>> call(set: IChunkSet, finalize: Runnable): T? { override fun <T : Future<T>> call(set: IChunkSet, finalize: Runnable): T? {
return null return null
} }

View File

@ -42,6 +42,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
private IQueueExtent<? extends IChunk> extent; // the parent queue extent which has this chunk private IQueueExtent<? extends IChunk> extent; // the parent queue extent which has this chunk
private int chunkX; private int chunkX;
private int chunkZ; private int chunkZ;
private boolean fastmode;
private ChunkHolder() { private ChunkHolder() {
this.delegate = NULL; this.delegate = NULL;
@ -100,6 +101,16 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
return getOrCreateGet().load(layer); return getOrCreateGet().load(layer);
} }
@Override
public boolean isFastMode() {
return fastmode;
}
@Override
public void setFastMode(boolean fastmode) {
this.fastmode = fastmode;
}
@Override @Override
public CompoundTag getEntity(UUID uuid) { public CompoundTag getEntity(UUID uuid) {
return delegate.get(this).getEntity(uuid); return delegate.get(this).getEntity(uuid);
@ -313,6 +324,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
public 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 IChunkGet get = getOrCreateGet();
final IChunkSet set = getOrCreateSet(); final IChunkSet set = getOrCreateSet();
set.setFastMode(fastmode);
try { try {
block.filter(this, get, set, filter, region, full); block.filter(this, get, set, filter, region, full);
} finally { } finally {
@ -344,6 +356,11 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
return false; return false;
} }
@Override
public boolean trim(boolean aggressive, int layer) {
return this.trim(aggressive);
}
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
return chunkSet == null || chunkSet.isEmpty(); return chunkSet == null || chunkSet.isEmpty();

View File

@ -117,5 +117,9 @@ object NullChunk : IQueueChunk<Nothing> {
override fun trim(aggressive: Boolean): Boolean { override fun trim(aggressive: Boolean): Boolean {
return true return true
} }
override fun trim(aggressive: Boolean, layer: Int): Boolean {
return true
}
} }

View File

@ -4,7 +4,6 @@ import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.IChunkSet;
import com.sk89q.worldedit.extent.Extent;
public class BatchProcessorHolder implements IBatchProcessorHolder { public class BatchProcessorHolder implements IBatchProcessorHolder {
private IBatchProcessor processor = EmptyBatchProcessor.INSTANCE; private IBatchProcessor processor = EmptyBatchProcessor.INSTANCE;

View File

@ -44,12 +44,14 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
private final QueueHandler handler; private final QueueHandler handler;
private final BatchProcessorHolder processor; private final BatchProcessorHolder processor;
private int changes; 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())); super(handler.getQueue(world, new BatchProcessorHolder()));
this.world = world; this.world = world;
this.handler = handler; this.handler = handler;
this.processor = (BatchProcessorHolder) getExtent().getProcessor(); this.processor = (BatchProcessorHolder) getExtent().getProcessor();
this.fastmode = fastmode;
} }
@Override @Override
@ -94,6 +96,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
final Filter newFilter = filter.fork(); final Filter newFilter = filter.fork();
// Create a chunk that we will reuse/reset for each operation // Create a chunk that we will reuse/reset for each operation
final IQueueExtent<IQueueChunk> queue = getNewQueue(); final IQueueExtent<IQueueChunk> queue = getNewQueue();
queue.setFastMode(fastmode);
synchronized (queue) { synchronized (queue) {
ChunkFilterBlock block = null; ChunkFilterBlock block = null;
@ -162,7 +165,8 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
@Override @Override
public int replaceBlocks(Region region, Mask mask, Pattern pattern) public int replaceBlocks(Region region, Mask mask, Pattern pattern)
throws MaxChangedBlocksException { 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 @Override

View File

@ -49,6 +49,8 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
private boolean enabledQueue = true; private boolean enabledQueue = true;
private boolean fastmode = false;
/** /**
* Safety check to ensure that the thread being used matches the one being initialized on. - Can * Safety check to ensure that the thread being used matches the one being initialized on. - Can
* be removed later * be removed later
@ -80,6 +82,16 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
return cacheSet.get(chunkX, chunkZ); return cacheSet.get(chunkX, chunkZ);
} }
@Override
public void setFastMode(boolean fastmode) {
this.fastmode = fastmode;
}
@Override
public boolean isFastMode() {
return fastmode;
}
/** /**
* Resets the queue. * Resets the queue.
*/ */

View File

@ -309,6 +309,11 @@ public class Settings extends Config {
}) })
public int DISCARD_AFTER_MS = 60000; 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 { public static class PROGRESS {
@Comment({"Display constant titles about the progress of a user's edit", @Comment({"Display constant titles about the progress of a user's edit",
" - false = disabled", " - false = disabled",
@ -369,6 +374,18 @@ public class Settings extends Config {
"Other experimental features" "Other experimental features"
}) })
public boolean OTHER = false; 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 { public static class WEB {

View File

@ -549,6 +549,11 @@ public class MCAChunk implements IChunk {
return isEmpty(); return isEmpty();
} }
@Override
public boolean trim(boolean aggressive, int layer) {
return hasSection(layer);
}
@Override @Override
public CompoundTag getEntity(UUID uuid) { public CompoundTag getEntity(UUID uuid) {
return this.entities.get(uuid); return this.entities.get(uuid);

View File

@ -184,7 +184,11 @@ public class CPUOptimizedClipboard extends LinearClipboard {
@Override @Override
public <B extends BlockStateHolder<B>> boolean setBlock(int index, B block) { public <B extends BlockStateHolder<B>> 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(); boolean hasNbt = block instanceof BaseBlock && block.hasNbtData();
if (hasNbt) { if (hasNbt) {
setTile(index, block.getNbtData()); setTile(index, block.getNbtData());

View File

@ -388,6 +388,9 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable
try { try {
int index = HEADER_SIZE + (getIndex(x, y, z) << 1); int index = HEADER_SIZE + (getIndex(x, y, z) << 1);
char ordinal = block.getOrdinalChar(); char ordinal = block.getOrdinalChar();
if (ordinal == 0) {
ordinal = 1;
}
byteBuffer.putChar(index, ordinal); byteBuffer.putChar(index, ordinal);
boolean hasNbt = block instanceof BaseBlock && block.hasNbtData(); boolean hasNbt = block instanceof BaseBlock && block.hasNbtData();
if (hasNbt) { if (hasNbt) {

View File

@ -263,6 +263,9 @@ public class MemoryOptimizedClipboard extends LinearClipboard {
@Override @Override
public <B extends BlockStateHolder<B>> boolean setBlock(int index, B block) { public <B extends BlockStateHolder<B>> boolean setBlock(int index, B block) {
int ordinal = block.getOrdinal(); int ordinal = block.getOrdinal();
if (ordinal == 0) {
ordinal = 1;
}
setOrdinal(index, ordinal); setOrdinal(index, ordinal);
boolean hasNbt = block instanceof BaseBlock && block.hasNbtData(); boolean hasNbt = block instanceof BaseBlock && block.hasNbtData();
if (hasNbt) { if (hasNbt) {

View File

@ -296,7 +296,7 @@ public class EditSessionBuilder {
if (unwrapped instanceof IQueueExtent) { if (unwrapped instanceof IQueueExtent) {
extent = queue = (IQueueExtent) unwrapped; extent = queue = (IQueueExtent) unwrapped;
} else if (Settings.IMP.QUEUE.PARALLEL_THREADS > 1 && threaded) { } 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(); queue = parallel.getExtent();
extent = parallel; extent = parallel;
} else { } else {

View File

@ -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.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.Command;
import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.CommandContainer;
@ -366,7 +367,7 @@ public class ClipboardCommands {
e.printStackTrace(); e.printStackTrace();
} }
} }
player.print(Caption.of("fawe.web.download.link" , urlText)); player.print(Caption.of("fawe.web.download.link" , urlText).clickEvent(ClickEvent.openUrl(urlText)));
} }
} }

View File

@ -134,8 +134,8 @@ public class GenerationCommands {
@Arg(desc = "TODO", def = "100") int threshold, @Arg(desc = "BlockVector2", def = "") BlockVector2 dimensions) throws WorldEditException, IOException { @Arg(desc = "TODO", def = "100") int threshold, @Arg(desc = "BlockVector2", def = "") BlockVector2 dimensions) throws WorldEditException, IOException {
TextureUtil tu = Fawe.get().getCachedTextureUtil(randomize, 0, threshold); TextureUtil tu = Fawe.get().getCachedTextureUtil(randomize, 0, threshold);
URL url = new URL(argStr); URL url = new URL(argStr);
if (!url.getHost().equalsIgnoreCase("i.imgur.com") && !url.getHost().equalsIgnoreCase("empcraft.com")) { if (!url.getHost().equalsIgnoreCase("i.imgur.com")) {
throw new IOException("Only i.imgur.com or empcraft.com/ui links are allowed!"); throw new IOException("Only i.imgur.com links are allowed!");
} }
BufferedImage image = MainUtil.readImage(url); BufferedImage image = MainUtil.readImage(url);
if (dimensions != null) { if (dimensions != null) {

View File

@ -212,6 +212,7 @@ public class SchematicCommands {
} }
UUID uuid = UUID.fromString(filename.substring(4)); UUID uuid = UUID.fromString(filename.substring(4));
URL webUrl = new URL(Settings.IMP.WEB.URL); URL webUrl = new URL(Settings.IMP.WEB.URL);
format = ClipboardFormats.findByAlias(formatName);
URL url = new URL(webUrl, "uploads/" + uuid + "." + format.getPrimaryFileExtension()); URL url = new URL(webUrl, "uploads/" + uuid + "." + format.getPrimaryFileExtension());
ReadableByteChannel byteChannel = Channels.newChannel(url.openStream()); ReadableByteChannel byteChannel = Channels.newChannel(url.openStream());
in = Channels.newInputStream(byteChannel); in = Channels.newInputStream(byteChannel);

View File

@ -364,6 +364,12 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
} }
state = fuzzyBuilder.build(); state = fuzzyBuilder.build();
} }
} else {
for (Map.Entry<Property<?>, Object> blockState : blockStates.entrySet()) {
@SuppressWarnings("unchecked")
Property<Object> objProp = (Property<Object>) blockState.getKey();
state = state.with(objProp, blockState.getValue());
}
} }
} }
// this should be impossible but IntelliJ isn't that smart // this should be impossible but IntelliJ isn't that smart

View File

@ -223,11 +223,13 @@ public class FastSchematicReader extends NBTSchematicReader {
int volume = width * height * length; int volume = width * height * length;
if (palette.length < 128) { if (palette.length < 128) {
for (int index = 0; index < volume; index++) { for (int index = 0; index < volume; index++) {
linear.setBlock(index, getBlockState(fis.read())); int ordinal = fis.read();
linear.setBlock(index, getBlockState(ordinal));
} }
} else { } else {
for (int index = 0; index < volume; index++) { for (int index = 0; index < volume; index++) {
linear.setBlock(index, getBlockState(fis.readVarInt())); int ordinal = fis.readVarInt();
linear.setBlock(index, getBlockState(ordinal));
} }
} }
} else { } else {
@ -235,7 +237,8 @@ public class FastSchematicReader extends NBTSchematicReader {
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int z = 0; z < length; z++) { for (int z = 0; z < length; z++) {
for (int x = 0; x < width; x++) { 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 y = 0; y < height; y++) {
for (int z = 0; z < length; z++) { for (int z = 0; z < length; z++) {
for (int x = 0; x < width; x++) { 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));
} }
} }
} }

View File

@ -21,7 +21,6 @@ package com.sk89q.worldedit.extent.clipboard.io;
import com.boydti.fawe.jnbt.streamer.IntValueReader; import com.boydti.fawe.jnbt.streamer.IntValueReader;
import com.boydti.fawe.object.FaweOutputStream; import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.clipboard.LinearClipboard;
import com.boydti.fawe.util.IOUtil; import com.boydti.fawe.util.IOUtil;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.sk89q.jnbt.CompoundTag; 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.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.block.BlockTypesCache;
import net.jpountz.lz4.LZ4BlockInputStream; import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream; import net.jpountz.lz4.LZ4BlockOutputStream;
@ -181,6 +179,9 @@ public class FastSchematicWriter implements ClipboardWriter {
} }
int ordinal = block.getOrdinal(); int ordinal = block.getOrdinal();
if (ordinal == 0) {
ordinal = 1;
}
char value = palette[ordinal]; char value = palette[ordinal];
if (value == Character.MAX_VALUE) { if (value == Character.MAX_VALUE) {
int size = paletteMax++; int size = paletteMax++;
@ -338,4 +339,4 @@ public class FastSchematicWriter implements ClipboardWriter {
public void close() throws IOException { public void close() throws IOException {
outputStream.close(); outputStream.close();
} }
} }

View File

@ -189,7 +189,15 @@ public class BlockMask extends ABlockMask {
@Override @Override
public boolean test(Extent extent, BlockVector3 vector) { 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 @Override

View File

@ -26,6 +26,8 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -65,6 +67,11 @@ public class BlockStateMask extends AbstractExtentMask {
.allMatch(entry -> block.getState(entry.getKey()) == entry.getValue()); .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 @Nullable
@Override @Override
public Mask2D toMask2D() { public Mask2D toMask2D() {

View File

@ -43,6 +43,7 @@ import org.jetbrains.annotations.NotNull;
public class BlockTypeMask extends AbstractExtentMask { public class BlockTypeMask extends AbstractExtentMask {
private final boolean[] types; private final boolean[] types;
private boolean hasAir;
/** /**
* Create a new block mask. * Create a new block mask.
@ -63,7 +64,9 @@ public class BlockTypeMask extends AbstractExtentMask {
public BlockTypeMask(Extent extent, @NotNull BlockType... block) { public BlockTypeMask(Extent extent, @NotNull BlockType... block) {
super(extent); super(extent);
this.types = new boolean[BlockTypes.size()]; 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) { for (BlockType type : blocks) {
add(type); 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) { public void add(@NotNull BlockType... block) {
for (BlockType type : 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; this.types[type.getInternalId()] = true;
} }
} }
@ -110,6 +113,11 @@ public class BlockTypeMask extends AbstractExtentMask {
return test(vector.getBlock(extent).getBlockType()); return test(vector.getBlock(extent).getBlockType());
} }
@Override
public boolean replacesAir() {
return hasAir;
}
public boolean test(BlockType block) { public boolean test(BlockType block) {
return types[block.getInternalId()]; return types[block.getInternalId()];
} }

View File

@ -123,4 +123,8 @@ public interface Mask {
} }
}; };
} }
default boolean replacesAir() {
return false;
}
} }

View File

@ -88,7 +88,11 @@ public class OffsetMask extends AbstractMask {
@Override @Override
public boolean test(Extent extent, BlockVector3 pos) { 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 @Nullable

View File

@ -1,13 +1,12 @@
package com.sk89q.worldedit.function.mask; package com.sk89q.worldedit.function.mask;
import com.boydti.fawe.Fawe;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
public class SingleBlockStateMask extends ABlockMask { public class SingleBlockStateMask extends ABlockMask {
private final char ordinal; private final char ordinal;
private final boolean isAir;
public BlockState getBlockState() { public BlockState getBlockState() {
return BlockState.getFromOrdinal(ordinal); return BlockState.getFromOrdinal(ordinal);
@ -15,12 +14,14 @@ public class SingleBlockStateMask extends ABlockMask {
public SingleBlockStateMask(Extent extent, BlockState state) { public SingleBlockStateMask(Extent extent, BlockState state) {
super(extent); super(extent);
isAir = state.isAir();
this.ordinal = state.getOrdinalChar(); this.ordinal = state.getOrdinalChar();
} }
@Override @Override
public boolean test(Extent extent, BlockVector3 vector) { public boolean test(Extent extent, BlockVector3 vector) {
return ordinal == vector.getOrdinal(extent); int test = vector.getOrdinal(extent);
return ordinal == test || isAir && test == 0;
} }
@Override @Override
@ -33,6 +34,11 @@ public class SingleBlockStateMask extends ABlockMask {
return new InverseSingleBlockStateMask(getExtent(), BlockState.getFromOrdinal(ordinal)); return new InverseSingleBlockStateMask(getExtent(), BlockState.getFromOrdinal(ordinal));
} }
@Override
public boolean replacesAir() {
return isAir;
}
@Override @Override
public Mask tryCombine(Mask mask) { public Mask tryCombine(Mask mask) {
if (mask instanceof ABlockMask) { if (mask instanceof ABlockMask) {

View File

@ -8,10 +8,12 @@ import com.sk89q.worldedit.world.block.BlockTypesCache;
public class SingleBlockTypeMask extends ABlockMask { public class SingleBlockTypeMask extends ABlockMask {
private final int internalId; private final int internalId;
private final boolean isAir;
public SingleBlockTypeMask(Extent extent, BlockType type) { public SingleBlockTypeMask(Extent extent, BlockType type) {
super(extent); super(extent);
this.internalId = type.getInternalId(); isAir = type == BlockTypes.AIR || type == BlockTypes.CAVE_AIR || type == BlockTypes.VOID_AIR;
this.internalId = type.getInternalId();
} }
@Override @Override
@ -27,4 +29,9 @@ public class SingleBlockTypeMask extends ABlockMask {
public BlockType getBlockType() { public BlockType getBlockType() {
return BlockTypes.get(internalId); return BlockTypes.get(internalId);
} }
@Override
public boolean replacesAir() {
return isAir;
}
} }

View File

@ -1,6 +1,7 @@
package com.sk89q.worldedit.world.block; package com.sk89q.worldedit.world.block;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.google.common.primitives.Booleans;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extension.platform.Platform;
@ -166,12 +167,14 @@ public class BlockTypesCache {
public static final BlockType[] values; public static final BlockType[] values;
public static final BlockState[] states; public static final BlockState[] states;
public static final boolean[] ticking;
protected static final Set<String> $NAMESPACES = new LinkedHashSet<>(); protected static final Set<String> $NAMESPACES = new LinkedHashSet<>();
static { static {
try { try {
ArrayList<BlockState> stateList = new ArrayList<>(); ArrayList<BlockState> stateList = new ArrayList<>();
ArrayList<Boolean> tickList = new ArrayList<>();
Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS); Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS);
Registries registries = platform.getRegistries(); Registries registries = platform.getRegistries();
@ -202,7 +205,7 @@ public class BlockTypesCache {
if (values[internalId] != null) { if (values[internalId] != null) {
throw new IllegalStateException("Invalid duplicate id for " + field.getName()); 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 // Note: Throws IndexOutOfBoundsError if nothing is registered and blocksMap is empty
values[internalId] = type; values[internalId] = type;
} }
@ -214,7 +217,7 @@ public class BlockTypesCache {
String defaultState = entry.getValue(); String defaultState = entry.getValue();
// Skip already registered ids // Skip already registered ids
for (; values[internalId] != null; internalId++); for (; values[internalId] != null; internalId++);
BlockType type = register(defaultState, internalId, stateList); BlockType type = register(defaultState, internalId, stateList, tickList);
values[internalId] = type; values[internalId] = type;
} }
} }
@ -223,7 +226,7 @@ public class BlockTypesCache {
} }
states = stateList.toArray(new BlockState[stateList.size()]); states = stateList.toArray(new BlockState[stateList.size()]);
ticking = Booleans.toArray(tickList);
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
@ -231,12 +234,14 @@ public class BlockTypesCache {
} }
} }
private static BlockType register(final String id, int internalId, List<BlockState> states) { private static BlockType register(final String id, int internalId, List<BlockState> states, List<Boolean> tickList) {
// Get the enum name (remove namespace if minecraft:) // Get the enum name (remove namespace if minecraft:)
int propStart = id.indexOf('['); int propStart = id.indexOf('[');
String typeName = id.substring(0, propStart == -1 ? id.length() : propStart); String typeName = id.substring(0, propStart == -1 ? id.length() : propStart);
String enumName = (typeName.startsWith("minecraft:") ? typeName.substring(10) : typeName).toUpperCase(Locale.ROOT); String enumName = (typeName.startsWith("minecraft:") ? typeName.substring(10) : typeName).toUpperCase(Locale.ROOT);
int oldsize = states.size();
BlockType existing = new BlockType(id, internalId, states); BlockType existing = new BlockType(id, internalId, states);
tickList.addAll(Collections.nCopies(states.size() - oldsize, existing.getMaterial().isTicksRandomly()));
// register states // register states
BlockType.REGISTRY.register(typeName, existing); BlockType.REGISTRY.register(typeName, existing);
String nameSpace = typeName.substring(0, typeName.indexOf(':')); String nameSpace = typeName.substring(0, typeName.indexOf(':'));

View File

@ -72,10 +72,10 @@
"fawe.worldedit.history.command.history.clear": "History cleared", "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.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.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.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.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})", "fawe.worldedit.operation.operation": "Operation queued ({0})",
@ -97,7 +97,7 @@
"fawe.worldedit.selection.selection.shift": "Region shifted", "fawe.worldedit.selection.selection.shift": "Region shifted",
"fawe.worldedit.selection.selection.cleared": "Selection cleared", "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.reset": "Reset your brush. (SHIFT + Click)",
"fawe.worldedit.brush.brush.none": "You aren't holding a brush!", "fawe.worldedit.brush.brush.none": "You aren't holding a brush!",
@ -238,7 +238,7 @@
"fawe.error.command.syntax": "Usage: {0}", "fawe.error.command.syntax": "Usage: {0}",
"fawe.error.no-perm": "You are lacking the permission node: {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.setting.disable": "Lacking setting: {0}",
"fawe.error.brush.not.found": "Available brushes: {0}", "fawe.error.brush.not.found": "Available brushes: {0}",
"fawe.error.brush.incompatible": "Brush not compatible with this version", "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.set": "Block change limit set to {0}.",
"worldedit.limit.return-to-default": "(Use //limit to go back to the default.)", "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.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.timeout.return-to-default": " (Use //timeout to go back to the default.)",
"worldedit.fast.disabled": "Fast mode disabled.", "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.", "worldedit.fast.enabled": "Fast mode enabled. Lighting in the affected chunks may be wrong and/or you may need to rejoin to see changes.",