merge 1.16

This commit is contained in:
Aurora
2020-07-01 14:46:25 +02:00
86 changed files with 2816 additions and 741 deletions

View File

@ -305,7 +305,7 @@ public class Fawe {
br.close();
this.version = FaweVersion.tryParse(versionString, commitString, dateString);
Settings.IMP.DATE = new Date(100 + version.year, version.month, version.day).toGMTString();
Settings.IMP.BUILD = "https://ci.athion.net/job/FastAsyncWorldEdit-1.15/" + version.build;
Settings.IMP.BUILD = "https://ci.athion.net/job/FastAsyncWorldEdit-1.16/" + version.build;
Settings.IMP.COMMIT = "https://github.com/IntellectualSites/FastAsyncWorldEdit/commit/" + Integer.toHexString(version.hash);
} catch (Throwable ignore) {}
try {

View File

@ -1,7 +1,6 @@
package com.boydti.fawe;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.Trimable;
@ -9,6 +8,7 @@ import com.boydti.fawe.beta.implementation.queue.Pool;
import com.boydti.fawe.beta.implementation.queue.QueuePool;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.collection.BitArray;
import com.boydti.fawe.object.collection.BitArrayUnstretched;
import com.boydti.fawe.object.collection.CleanableThreadLocal;
import com.boydti.fawe.object.collection.VariableThreadLocal;
import com.boydti.fawe.object.exception.FaweBlockBagException;
@ -299,6 +299,102 @@ public enum FaweCache implements Trimable {
}
}
/**
* Convert raw int array to unstretched palette (1.16)
* @param layerOffset
* @param blocks
* @return palette
*/
public Palette toPaletteUnstretched(int layerOffset, char[] blocks) {
return toPaletteUnstretched(layerOffset, null, blocks);
}
/**
* Convert raw int array to unstretched palette (1.16)
* @param layerOffset
* @param blocks
* @return palette
*/
public Palette toPaletteUnstretched(int layerOffset, int[] blocks) {
return toPaletteUnstretched(layerOffset, blocks, null);
}
private Palette toPaletteUnstretched(int layerOffset, int[] blocksInts, char[] blocksChars) {
int[] blockToPalette = BLOCK_TO_PALETTE.get();
int[] paletteToBlock = PALETTE_TO_BLOCK.get();
long[] blockStates = BLOCK_STATES.get();
int[] blocksCopy = SECTION_BLOCKS.get();
try {
int num_palette = 0;
int blockIndexStart = layerOffset << 12;
int blockIndexEnd = blockIndexStart + 4096;
if (blocksChars != null) {
for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) {
int ordinal = blocksChars[i];
int palette = blockToPalette[ordinal];
if (palette == Integer.MAX_VALUE) {
blockToPalette[ordinal] = palette = num_palette;
paletteToBlock[num_palette] = ordinal;
num_palette++;
}
blocksCopy[j] = palette;
}
} else if (blocksInts != null) {
for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) {
int ordinal = blocksInts[i];
int palette = blockToPalette[ordinal];
if (palette == Integer.MAX_VALUE) {
// BlockState state = BlockTypesCache.states[ordinal];
blockToPalette[ordinal] = palette = num_palette;
paletteToBlock[num_palette] = ordinal;
num_palette++;
}
blocksCopy[j] = palette;
}
} else {
throw new IllegalArgumentException();
}
for (int i = 0; i < num_palette; i++) {
blockToPalette[paletteToBlock[i]] = Integer.MAX_VALUE;
}
// BlockStates
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
if (Settings.IMP.PROTOCOL_SUPPORT_FIX || num_palette != 1) {
bitsPerEntry = Math.max(bitsPerEntry, 4); // Protocol support breaks <4 bits per entry
} else {
bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries
}
int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntry);
int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong);
if (num_palette == 1) {
// Set a value, because minecraft needs it for some reason
blockStates[0] = 0;
blockBitArrayEnd = 1;
} else {
BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntry, blockStates);
bitArray.fromRaw(blocksCopy);
}
// Construct palette
Palette palette = PALETTE_CACHE.get();
palette.bitsPerEntry = bitsPerEntry;
palette.paletteToBlockLength = num_palette;
palette.paletteToBlock = paletteToBlock;
palette.blockStatesLength = blockBitArrayEnd;
palette.blockStates = blockStates;
return palette;
} catch (Throwable e) {
e.printStackTrace();
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
throw e;
}
}
/*
* Vector cache
*/

View File

@ -32,9 +32,9 @@ public class FaweVersion {
@Override public String toString() {
if (hash == 0 && build == 0) {
return "FastAsyncWorldEdit-1.15-NoVer-SNAPSHOT";
return "FastAsyncWorldEdit-1.16-NoVer-SNAPSHOT";
} else {
return "FastAsyncWorldEdit-1.15" + build;
return "FastAsyncWorldEdit-1.16" + build;
}
}

View File

@ -41,4 +41,8 @@ public interface IFawe {
Preloader getPreloader();
default boolean isChunksStretched() {
return true;
}
}

View File

@ -45,11 +45,11 @@ public interface IBlocks extends Trimable {
IBlocks reset();
default byte[] toByteArray(boolean full) {
return toByteArray(null, getBitMask(), full);
default byte[] toByteArray(boolean full, boolean stretched) {
return toByteArray(null, getBitMask(), full, stretched);
}
default byte[] toByteArray(byte[] buffer, int bitMask, boolean full) {
default byte[] toByteArray(byte[] buffer, int bitMask, boolean full, boolean stretched) {
if (buffer == null) {
buffer = new byte[1024];
}
@ -81,7 +81,12 @@ public interface IBlocks extends Trimable {
}
sectionWriter.writeShort(nonEmpty); // non empty
FaweCache.Palette palette = FaweCache.IMP.toPalette(0, ids);
FaweCache.Palette palette;
if (stretched) {
palette = FaweCache.IMP.toPalette(0, ids);
} else {
palette = FaweCache.IMP.toPaletteUnstretched(0, ids);
}
sectionWriter.writeByte(palette.bitsPerEntry); // bits per block
sectionWriter.writeVarInt(palette.paletteToBlockLength);

View File

@ -181,6 +181,18 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
}
@Override public void setFullBright(int layer) {
if (light == null) {
light = new char[16][];
}
if (light[layer] == null) {
light[layer] = new char[4096];
}
if (skyLight == null) {
skyLight = new char[16][];
}
if (skyLight[layer] == null) {
skyLight[layer] = new char[4096];
}
Arrays.fill(light[layer], (char) 15);
Arrays.fill(skyLight[layer], (char) 15);
}

View File

@ -1,10 +1,12 @@
package com.boydti.fawe.beta.implementation.packet;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IBlocks;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.WorldEdit;
import java.util.HashMap;
import java.util.function.Function;
@ -64,7 +66,7 @@ public class ChunkPacket implements Function<byte[], byte[]>, Supplier<byte[]> {
if (sectionBytes == null) {
IBlocks tmpChunk = getChunk();
byte[] buf = FaweCache.IMP.BYTE_BUFFER_8192.get();
sectionBytes = tmpChunk.toByteArray(buf, tmpChunk.getBitMask(), this.full);
sectionBytes = tmpChunk.toByteArray(buf, tmpChunk.getBitMask(), this.full, Fawe.imp().isChunksStretched());
}
tmp = sectionBytes;
}
@ -72,6 +74,7 @@ public class ChunkPacket implements Function<byte[], byte[]>, Supplier<byte[]> {
return tmp;
}
public Object getNativePacket() {
return nativePacket;
}

View File

@ -13,12 +13,16 @@ import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.world.AbstractWorld;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import javax.annotation.Nullable;
import java.io.File;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
@ -35,6 +39,11 @@ public class MCAWorld extends AbstractWorld {
return path.getName();
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException {
return false;
}
@Override
public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException {
@ -46,6 +55,11 @@ public class MCAWorld extends AbstractWorld {
return false;
}
@Override
public Set<SideEffect> applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException {
return SideEffectSet.none().getSideEffectsToApply();
}
@Override
public boolean clearContainerBlockContents(BlockVector3 position) {
return false;

View File

@ -31,7 +31,8 @@ public class CopyPastaBrush implements Brush, ResettableTool {
private final LocalSession session;
private final Player player;
public boolean autoRotate, randomRotate;
public boolean autoRotate;
public boolean randomRotate;
public CopyPastaBrush(Player player, LocalSession session, boolean randomRotate, boolean autoRotate) {
session.setClipboard(null);

View File

@ -29,10 +29,7 @@ import com.boydti.fawe.util.TextureUtil;
import com.boydti.fawe.util.image.Drawable;
import com.boydti.fawe.util.image.ImageViewer;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
@ -48,9 +45,7 @@ import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.Identifiable;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.util.*;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
@ -68,6 +63,7 @@ import java.lang.reflect.Field;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Supplier;
@ -750,12 +746,24 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr
return BlockVector3.at(getWidth() - 1, 255, getLength() - 1);
}
@Override
public Set<SideEffect> applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet)
throws WorldEditException{
return SideEffectSet.none().getSideEffectsToApply();
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block)
throws WorldEditException {
return setBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ(), block);
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects)
throws WorldEditException {
return setBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ(), block);
}
private boolean setBlock(int x, int y, int z, char combined) {
int index = z * getWidth() + x;
if (index < 0 || index >= getArea()) {

View File

@ -0,0 +1,115 @@
package com.boydti.fawe.object.collection;
import com.boydti.fawe.util.MathMan;
public final class BitArrayUnstretched {
private final long[] data;
private final int bitsPerEntry;
private final int maxSeqLocIndex;
private final int emptyBitCount;
private final long mask;
private final int longLen;
public BitArrayUnstretched(int bitsPerEntry, long[] buffer) {
this.bitsPerEntry = bitsPerEntry;
this.mask = (1L << bitsPerEntry) - 1L;
this.emptyBitCount = 64 % bitsPerEntry;
this.maxSeqLocIndex = 64 - (bitsPerEntry + emptyBitCount);
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntry);
this.longLen = MathMan.ceilZero((float) 4096 / blocksPerLong);
if (buffer.length < longLen) {
this.data = new long[longLen];
} else {
this.data = buffer;
}
}
public long[] getData() {
return data;
}
public final void set(int index, int value) {
if (longLen == 0) return;
int bitIndexStart = index * bitsPerEntry + MathMan.floorZero((double) index / longLen) * emptyBitCount;
int longIndexStart = bitIndexStart >> 6;
int localBitIndexStart = bitIndexStart & 63;
this.data[longIndexStart] = this.data[longIndexStart] & ~(mask << localBitIndexStart) | (long) value << localBitIndexStart;
}
public final int get(int index) {
if (longLen == 0) return 0;
int bitIndexStart = index * bitsPerEntry + MathMan.floorZero((double) index / longLen) * emptyBitCount;
int longIndexStart = bitIndexStart >> 6;
int localBitIndexStart = bitIndexStart & 63;
return (int)(this.data[longIndexStart] >>> localBitIndexStart & mask);
}
public int getLength() {
return longLen;
}
public final void fromRaw(int[] arr) {
final long[] data = this.data;
final int bitsPerEntry = this.bitsPerEntry;
final int maxSeqLocIndex = this.maxSeqLocIndex;
int localStart = 0;
int arrI = 0;
long l = 0;
for (int i = 0; i < longLen; i++) {
int lastVal;
for (; localStart <= maxSeqLocIndex && arrI < 4096; localStart += bitsPerEntry) {
lastVal = arr[arrI++];
l |= ((long) lastVal << localStart);
}
localStart = 0;
data[i] = l;
l = 0;
}
}
public final int[] toRaw() {
return toRaw(new int[4096]);
}
public final int[] toRaw(int[] buffer) {
final long[] data = this.data;
final int bitsPerEntry = this.bitsPerEntry;
final int maxSeqLocIndex = this.maxSeqLocIndex;
int localStart = 0;
int arrI = 0;
for (int i = 0; i < longLen; i++) {
long l = data[i];
char lastVal;
for (; localStart <= maxSeqLocIndex && arrI < 4096; localStart += bitsPerEntry) {
lastVal = (char) (l >>> localStart & this.mask);
buffer[arrI++] = lastVal;
}
localStart = 0;
}
return buffer;
}
public final char[] toRaw(char[] buffer) {
final long[] data = this.data;
final int bitsPerEntry = this.bitsPerEntry;
final int maxSeqLocIndex = this.maxSeqLocIndex;
int localStart = 0;
int arrI = 0;
for (int i = 0; i < longLen; i++) {
long l = data[i];
char lastVal;
for (; localStart <= maxSeqLocIndex && arrI < 4096; localStart += bitsPerEntry) {
lastVal = (char) (l >>> localStart & this.mask);
buffer[arrI++] = lastVal;
}
localStart = 0;
}
return buffer;
}
}

View File

@ -24,9 +24,7 @@ import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.util.*;
import com.sk89q.worldedit.world.AbstractWorld;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BiomeType;
@ -38,6 +36,7 @@ import com.sk89q.worldedit.world.weather.WeatherType;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Set;
public class WorldWrapper extends AbstractWorld {
@ -88,6 +87,12 @@ public class WorldWrapper extends AbstractWorld {
return parent.useItem(position, item, face);
}
@Override
public Set<SideEffect> applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet)
throws WorldEditException{
return parent.applySideEffects(position, previousType, sideEffectSet);
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException {
return parent.setBlock(position, block, notifyAndLight);
@ -99,6 +104,11 @@ public class WorldWrapper extends AbstractWorld {
return parent.setBlock(x, y, z, block);
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException {
return parent.setBlock(position, block, sideEffects);
}
@Override
public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException {
return parent.setTile(x, y, z, tile);

View File

@ -20,6 +20,7 @@
package com.sk89q.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
@ -331,4 +332,37 @@ public final class StringUtil {
return parsableBlocks;
}
/**
* Splits a string respecting enclosing quotes.
*
* @param input the input to split.
* @param delimiter the delimiter to split on.
* @param open the opening quote character.
* @param close the closing quote character.
* @return a list of split strings.
*/
public static List<String> split(String input, char delimiter, char open, char close) {
if (input.indexOf(open) == -1 && input.indexOf(close) == -1) {
return Arrays.asList(input.split(String.valueOf(delimiter)));
}
int level = 0;
int begin = 0;
List<String> split = new ArrayList<>();
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (c == delimiter && level == 0) {
split.add(input.substring(begin, i));
begin = i + 1;
} else if (c == open) {
level++;
} else if (c == close) {
level--;
}
}
if (begin < input.length()) {
split.add(input.substring(begin));
}
return split;
}
}

View File

@ -119,7 +119,7 @@ import com.sk89q.worldedit.regions.shape.RegionShape;
import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment;
import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.util.eventbus.EventBus;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
@ -575,6 +575,20 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
disableHistory(enabled);
}
/**
* Set which block updates should occur.
*
* @param sideEffectSet side effects to enable
*/
public void setSideEffectApplier(SideEffectSet sideEffectSet) {
//Do nothing; TODO: SideEffects currently not fully implemented in FAWE.
}
public SideEffectSet getSideEffectApplier() {
//Do nothing; TODO: SideEffects currently not fully implemented in FAWE.
return SideEffectSet.defaults();
}
/**
* Disable history (or re-enable)
*

View File

@ -72,6 +72,7 @@ import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.util.HandSide;
import com.sk89q.worldedit.util.Identifiable;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
@ -145,7 +146,7 @@ public class LocalSession implements TextureHolder {
private transient Snapshot snapshotExperimental;
private transient boolean hasCUISupport = false;
private transient int cuiVersion = -1;
private transient boolean fastMode = false;
private transient SideEffectSet sideEffectSet = SideEffectSet.defaults();
private transient Mask mask;
private transient Mask sourceMask;
private transient TextureUtil texture;
@ -1494,7 +1495,7 @@ public class LocalSession implements TextureHolder {
builder.blockBag(blockBag);
}
builder.command(command);
builder.fastmode(fastMode);
builder.fastmode(!this.sideEffectSet.doesApplyAny());
editSession = builder.build();
@ -1513,7 +1514,7 @@ public class LocalSession implements TextureHolder {
}
private void prepareEditingExtents(EditSession editSession, Actor actor) {
editSession.setFastMode(fastMode);
editSession.setSideEffectApplier(sideEffectSet);
editSession.setReorderMode(reorderMode);
if (editSession.getSurvivalExtent() != null) {
editSession.getSurvivalExtent().setStripNbt(!actor.hasPermission("worldedit.setnbt"));
@ -1521,13 +1522,32 @@ public class LocalSession implements TextureHolder {
editSession.setTickingWatchdog(tickingWatchdog);
}
/**
* Gets the side effect applier of this session.
*
* @return the side effect applier
*/
public SideEffectSet getSideEffectSet() {
return this.sideEffectSet;
}
/**
* Sets the side effect applier for this session
*
* @param sideEffectSet the side effect applier
*/
public void setSideEffectSet(SideEffectSet sideEffectSet) {
this.sideEffectSet = sideEffectSet;
}
/**
* Checks if the session has fast mode enabled.
*
* @return true if fast mode is enabled
*/
@Deprecated
public boolean hasFastMode() {
return fastMode;
return !this.sideEffectSet.doesApplyAny();
}
/**
@ -1535,8 +1555,9 @@ public class LocalSession implements TextureHolder {
*
* @param fastMode true if fast mode is enabled
*/
@Deprecated
public void setFastMode(boolean fastMode) {
this.fastMode = fastMode;
this.sideEffectSet = fastMode ? SideEffectSet.none() : SideEffectSet.defaults();
}
/**

View File

@ -49,10 +49,13 @@ import java.util.ArrayList;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.formatting.component.PaginationBox;
import com.sk89q.worldedit.util.formatting.component.SideEffectBox;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.item.ItemType;
import java.io.FileNotFoundException;
@ -142,24 +145,62 @@ public class GeneralCommands {
@Command(
name = "/fast",
desc = "Toggle fast mode"
desc = "Toggle fast mode side effects"
)
@CommandPermissions("worldedit.fast")
public void fast(Actor actor, LocalSession session,
@Arg(desc = "The new fast mode state", def = "")
Boolean fastMode) {
boolean hasFastMode = session.hasFastMode();
if (fastMode != null && fastMode == hasFastMode) {
actor.printError(TranslatableComponent.of(fastMode ? "worldedit.fast.enabled.already" : "worldedit.fast.disabled.already"));
return;
@Arg(desc = "The side effect", def = "")
SideEffect sideEffect,
@Arg(desc = "The new side effect state", def = "")
SideEffect.State newState,
@Switch(name = 'h', desc = "Show the info box")
boolean showInfoBox) throws WorldEditException {
if (sideEffect != null) {
SideEffect.State currentState = session.getSideEffectSet().getState(sideEffect);
if (newState != null && newState == currentState) {
if (!showInfoBox) {
actor.printError(TranslatableComponent.of(
"worldedit.fast.sideeffect.already-set",
TranslatableComponent.of(sideEffect.getDisplayName()),
TranslatableComponent.of(newState.getDisplayName())
));
}
return;
}
if (newState != null) {
session.setSideEffectSet(session.getSideEffectSet().with(sideEffect, newState));
if (!showInfoBox) {
actor.printInfo(TranslatableComponent.of(
"worldedit.fast.sideeffect.set",
TranslatableComponent.of(sideEffect.getDisplayName()),
TranslatableComponent.of(newState.getDisplayName())
));
}
} else {
actor.printInfo(TranslatableComponent.of(
"worldedit.fast.sideeffect.get",
TranslatableComponent.of(sideEffect.getDisplayName()),
TranslatableComponent.of(currentState.getDisplayName())
));
}
} else if (newState != null) {
SideEffectSet applier = session.getSideEffectSet();
for (SideEffect sideEffectEntry : SideEffect.values()) {
applier = applier.with(sideEffectEntry, newState);
}
session.setSideEffectSet(applier);
if (!showInfoBox) {
actor.printInfo(TranslatableComponent.of(
"worldedit.fast.sideeffect.set-all",
TranslatableComponent.of(newState.getDisplayName())
));
}
}
if (hasFastMode) {
session.setFastMode(false);
actor.printInfo(TranslatableComponent.of("worldedit.fast.disabled"));
} else {
session.setFastMode(true);
actor.printInfo(TranslatableComponent.of("worldedit.fast.enabled"));
if (sideEffect == null || showInfoBox) {
SideEffectBox sideEffectBox = new SideEffectBox(session.getSideEffectSet());
actor.print(sideEffectBox.create(1));
}
}

View File

@ -23,6 +23,7 @@ import com.boydti.fawe.object.brush.scroll.Scroll;
import com.google.common.collect.ImmutableSet;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.command.util.HookMode;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.TreeGenerator;
import org.enginehub.piston.CommandManager;
import org.enginehub.piston.converter.ArgumentConverter;
@ -30,6 +31,7 @@ import org.enginehub.piston.converter.MultiKeyConverter;
import org.enginehub.piston.inject.Key;
import javax.annotation.Nullable;
import java.util.EnumSet;
import java.util.Locale;
import java.util.Set;
@ -48,6 +50,11 @@ public final class EnumConverter {
full(EditSession.ReorderMode.class,
r -> ImmutableSet.of(r.getDisplayName()),
null));
commandManager.registerConverter(Key.of(SideEffect.State.class),
MultiKeyConverter.from(
EnumSet.of(SideEffect.State.OFF, SideEffect.State.ON),
r -> ImmutableSet.of(r.name().toLowerCase(Locale.US)),
null));
commandManager.registerConverter(Key.of(HookMode.class),
basic(HookMode.class));
commandManager.registerConverter(Key.of(Scroll.Action.class),

View File

@ -0,0 +1,75 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.command.argument;
import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.util.EntityRemover;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import org.enginehub.piston.CommandManager;
import org.enginehub.piston.converter.ArgumentConverter;
import org.enginehub.piston.converter.ConversionResult;
import org.enginehub.piston.converter.FailedConversion;
import org.enginehub.piston.converter.SuccessfulConversion;
import org.enginehub.piston.inject.InjectedValueAccess;
import org.enginehub.piston.inject.Key;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
public class SideEffectConverter implements ArgumentConverter<SideEffect> {
public static void register(CommandManager commandManager) {
commandManager.registerConverter(Key.of(SideEffect.class), new SideEffectConverter());
}
private final TextComponent choices = TextComponent.of("any side effect");
private SideEffectConverter() {
}
private Collection<SideEffect> getSideEffects() {
return WorldEdit.getInstance().getPlatformManager().getSupportedSideEffects();
}
@Override
public Component describeAcceptableArguments() {
return choices;
}
@Override
public List<String> getSuggestions(String input, InjectedValueAccess context) {
return limitByPrefix(getSideEffects().stream().map(sideEffect -> sideEffect.name().toLowerCase(Locale.US)), input);
}
@Override
public ConversionResult<SideEffect> convert(String argument, InjectedValueAccess context) {
try {
return SuccessfulConversion.fromSingle(SideEffect.valueOf(argument.toUpperCase(Locale.US)));
} catch (Exception e) {
return FailedConversion.from(e);
}
}
}

View File

@ -58,8 +58,8 @@ public class BlockFactory extends AbstractFactory<BaseBlock> {
*/
public Set<BaseBlock> parseFromListInput(String input, ParserContext context) throws InputParseException {
Set<BaseBlock> blocks = new HashSet<>();
String[] splits = input.split(",");
for (String token : StringUtil.parseListInQuotes(splits, ',', '[', ']', true)) {
// String[] splits = input.split(",");
for (String token : StringUtil.split(input, ',', '[', ']')) {
blocks.add(parseFromInput(token, context));
}
return blocks;

View File

@ -24,6 +24,7 @@ import com.sk89q.worldedit.extension.factory.parser.pattern.BlockCategoryPattern
import com.sk89q.worldedit.extension.factory.parser.pattern.ClipboardPatternParser;
import com.sk89q.worldedit.extension.factory.parser.pattern.RandomPatternParser;
import com.sk89q.worldedit.extension.factory.parser.pattern.RandomStatePatternParser;
import com.sk89q.worldedit.extension.factory.parser.pattern.SimplexPatternParser;
import com.sk89q.worldedit.extension.factory.parser.pattern.SingleBlockPatternParser;
import com.sk89q.worldedit.extension.factory.parser.pattern.TypeOrStateApplyingPatternParser;
import com.sk89q.worldedit.function.pattern.Pattern;
@ -54,6 +55,9 @@ public final class PatternFactory extends AbstractFactory<Pattern> {
register(new TypeOrStateApplyingPatternParser(worldEdit));
register(new RandomStatePatternParser(worldEdit));
register(new BlockCategoryPatternParser(worldEdit));
// FAWE
register(new SimplexPatternParser(worldEdit));
}
}

View File

@ -0,0 +1,118 @@
package com.sk89q.worldedit.extension.factory.parser;
import com.sk89q.util.StringUtil;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.internal.registry.InputParser;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
import java.util.stream.Stream;
/**
* A rich parser allows parsing of patterns and masks with extra arguments,
* e.g. #simplex[scale][pattern].
*
* @param <E> the parse result.
*/
public abstract class RichParser<E> extends InputParser<E> {
private final String prefix;
private final String required;
/**
* Create a new rich parser with a defined prefix for the result, e.g. {@code #simplex}.
*
* @param worldEdit the worldedit instance.
* @param prefix the prefix of this parser result.
*/
protected RichParser(WorldEdit worldEdit, String prefix) {
super(worldEdit);
this.prefix = prefix;
this.required = prefix + "[";
}
@Override
public Stream<String> getSuggestions(String input) {
// we don't even want to start suggesting if it's not meant to be this parser result
if (input.length() > this.required.length() && !input.startsWith(this.required)) {
return Stream.empty();
}
// suggest until the first [ as long as it isn't fully typed
if (input.length() < this.required.length()) {
return Stream.of(this.required).filter(s -> s.startsWith(input));
}
// we know that it is at least "<required>"
String[] strings = extractArguments(input.substring(this.prefix.length()), false);
StringJoiner joiner = new StringJoiner(",");
for (int i = 0; i < strings.length - 1; i++) {
joiner.add("[" + strings[i] + "]");
}
String previous = this.prefix + joiner;
return getSuggestions(strings[strings.length - 1], strings.length - 1).map(s -> previous + "[" + s + "]");
}
@Override
public E parseFromInput(String input, ParserContext context) throws InputParseException {
if (!input.startsWith(this.prefix)) return null;
if (input.length() < this.prefix.length()) return null;
String[] arguments = extractArguments(input.substring(prefix.length()), true);
return parseFromInput(arguments, context);
}
/**
* Returns a stream of suggestions for the argument at the given index.
*
* @param argumentInput the already provided input for the argument at the given index.
* @param index the index of the argument to get suggestions for.
* @return a stream of suggestions matching the given input for the argument at the given index.
*/
protected abstract Stream<String> getSuggestions(String argumentInput, int index);
/**
* Parses the already split arguments.
*
* @param arguments the array of arguments that were split (can be empty).
* @param context the context of this parsing process.
* @return the resulting parsed type.
* @throws InputParseException if the input couldn't be parsed correctly.
*/
protected abstract E parseFromInput(@NotNull String[] arguments, ParserContext context) throws InputParseException;
/**
* Extracts arguments enclosed by {@code []} into an array.
* Example: {@code [Hello][World]} results in a list containing {@code Hello} and {@code World}.
*
* @param input the input to extract arguments from.
* @param requireClosing whether or not the extraction requires valid bracketing.
* @return an array of extracted arguments.
* @throws InputParseException if {@code requireClosing == true} and the count of [ != the count of ]
*/
protected String[] extractArguments(String input, boolean requireClosing) throws InputParseException {
int open = 0; // the "level"
int openIndex = 0;
int i = 0;
List<String> arguments = new ArrayList<>();
for (; i < input.length(); i++) {
if (input.charAt(i) == '[') {
if (open++ == 0) {
openIndex = i;
}
}
if (input.charAt(i) == ']') {
if (--open == 0) {
arguments.add(input.substring(openIndex + 1, i));
}
}
}
if (!requireClosing && open > 0) {
arguments.add(input.substring(openIndex + 1));
}
if (requireClosing && open != 0) {
throw new InputParseException("Invalid bracketing, are you missing a '[' or ']'?");
}
return arguments.toArray(new String[0]);
}
}

View File

@ -38,8 +38,9 @@ public class RandomPatternParser extends InputParser<Pattern> {
@Override
public Stream<String> getSuggestions(String input) {
String[] splits = input.split(",", -1);
List<String> patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true);
List<String> patterns = StringUtil.split(input, ',', '[', ']');
/*String[] splits = input.split(",", -1);
List<String> patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true);*/
if (patterns.size() == 1) {
return Stream.empty();
}
@ -63,8 +64,9 @@ public class RandomPatternParser extends InputParser<Pattern> {
public Pattern parseFromInput(String input, ParserContext context) throws InputParseException {
RandomPattern randomPattern = new RandomPattern();
String[] splits = input.split(",", -1);
List<String> patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true);
List<String> patterns = StringUtil.split(input, ',', '[', ']');
/*String[] splits = input.split(",", -1);
List<String> patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true);*/
if (patterns.size() == 1) {
return null; // let a 'single'-pattern parser handle it
}
@ -74,7 +76,7 @@ public class RandomPatternParser extends InputParser<Pattern> {
// Parse special percentage syntax
if (token.matches("[0-9]+(\\.[0-9]*)?%.*")) {
String[] p = token.split("%");
String[] p = token.split("%", 2);
if (p.length < 2) {
throw new InputParseException("Missing the type after the % symbol for '" + input + "'");

View File

@ -0,0 +1,75 @@
package com.sk89q.worldedit.extension.factory.parser.pattern;
import com.boydti.fawe.object.random.SimplexRandom;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.factory.parser.RichParser;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.pattern.RandomPattern;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import org.jetbrains.annotations.NotNull;
import java.util.stream.Stream;
public class SimplexPatternParser extends RichParser<Pattern> {
private static final String SIMPLEX_PREFIX = "#simplex";
public SimplexPatternParser(WorldEdit worldEdit) {
super(worldEdit, SIMPLEX_PREFIX);
}
@Override
protected Stream<String> getSuggestions(String argumentInput, int index) {
if (index == 0) {
if (argumentInput.isEmpty()) {
return Stream.of("1", "2", "3", "4", "5", "6", "7", "8", "9");
}
// if already a valid number, suggest more digits
if (isDouble(argumentInput)) {
Stream<String> numbers = Stream.of("", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
if (argumentInput.indexOf('.') == -1) {
numbers = Stream.concat(numbers, Stream.of("."));
}
return numbers.map(s -> argumentInput + s);
}
// no valid input anymore
return Stream.empty();
}
if (index == 1) {
return worldEdit.getPatternFactory().getSuggestions(argumentInput).stream();
}
return Stream.empty();
}
@Override
protected Pattern parseFromInput(@NotNull String[] arguments, ParserContext context) {
if (arguments.length != 2) {
throw new InputParseException("Simplex requires a scale and a pattern, e.g. #simplex[5][dirt,stone]");
}
double scale = Double.parseDouble(arguments[0]);
scale = 1d / Math.max(1, scale);
Pattern inner = worldEdit.getPatternFactory().parseFromInput(arguments[1], context);
if (inner instanceof RandomPattern) {
return new RandomPattern(new SimplexRandom(scale), (RandomPattern) inner);
} else if (inner instanceof BlockStateHolder) {
return inner; // single blocks won't have any impact on how simplex behaves
} else {
throw new InputParseException("Pattern " + inner.getClass().getSimpleName() + " cannot be used with #simplex");
}
}
private static boolean isDouble(String input) {
boolean point = false;
for (char c : input.toCharArray()) {
if (!Character.isDigit(c)) {
if (c == '.' && !point) {
point = true;
} else {
return false;
}
}
}
return true;
}
}

View File

@ -21,14 +21,17 @@ package com.sk89q.worldedit.extension.platform;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.world.DataFixer;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.registry.Registries;
import org.enginehub.piston.CommandManager;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Represents a platform that WorldEdit has been implemented for.
@ -174,4 +177,5 @@ public interface Platform {
*/
Map<Capability, Preference> getCapabilities();
Set<SideEffect> getSupportedSideEffects();
}

View File

@ -94,6 +94,7 @@ import com.sk89q.worldedit.command.argument.ExpressionConverter;
import com.sk89q.worldedit.command.argument.FactoryConverter;
import com.sk89q.worldedit.command.argument.RegionFactoryConverter;
import com.sk89q.worldedit.command.argument.RegistryConverter;
import com.sk89q.worldedit.command.argument.SideEffectConverter;
import com.sk89q.worldedit.command.argument.VectorConverter;
import com.sk89q.worldedit.command.argument.WorldConverter;
import com.sk89q.worldedit.command.argument.ZonedDateTimeConverter;
@ -255,6 +256,7 @@ public final class PlatformCommandManager {
RegionFactoryConverter.register(commandManager);
WorldConverter.register(commandManager);
ExpressionConverter.register(commandManager);
SideEffectConverter.register(commandManager);
registerBindings(new ConsumeBindings(worldEdit, this));
registerBindings(new PrimitiveBindings(worldEdit));

View File

@ -48,9 +48,11 @@ import com.sk89q.worldedit.event.platform.PlayerInputEvent;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.eventbus.Subscribe;
import com.sk89q.worldedit.world.World;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.List;
@ -304,6 +306,10 @@ public class PlatformManager {
return queryCapability(Capability.CONFIGURATION).getConfiguration();
}
public Collection<SideEffect> getSupportedSideEffects() {
return queryCapability(Capability.WORLD_EDITING).getSupportedSideEffects();
}
@Subscribe
public void handlePlatformReady(PlatformReadyEvent event) {
choosePreferred();

View File

@ -21,17 +21,16 @@ package com.sk89q.worldedit.extent.buffer;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Maps;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.AbstractBufferingExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.collection.BlockMap;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.Map;
import java.util.Optional;
/**
* Buffers changes to an {@link Extent} and allows retrieval of the changed blocks,
@ -39,7 +38,7 @@ import java.util.Optional;
*/
public class ExtentBuffer extends AbstractBufferingExtent {
private final Map<BlockVector3, BaseBlock> buffer = Maps.newHashMap();
private final Map<BlockVector3, BaseBlock> buffer = BlockMap.create();
private final Mask mask;
/**

View File

@ -19,7 +19,6 @@
package com.sk89q.worldedit.extent.reorder;
import com.google.common.collect.ImmutableSortedSet;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.AbstractBufferingExtent;
import com.sk89q.worldedit.extent.Extent;

View File

@ -27,67 +27,38 @@ import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.RunContext;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.collection.BlockMap;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Implements "fast mode" which may skip physics, lighting, etc.
* An extent that sets blocks in the world, with a {@link SideEffectSet}.
*/
public class FastModeExtent extends AbstractDelegateExtent {
public class SideEffectExtent extends AbstractDelegateExtent {
private final World world;
private final Set<BlockVector3> positions = new HashSet<>();
private final Map<BlockVector3, BlockState> positions = BlockMap.create();
private final Set<BlockVector2> dirtyChunks = new HashSet<>();
private boolean enabled = true;
private SideEffectSet sideEffectSet = SideEffectSet.defaults();
private boolean postEditSimulation;
/**
* Create a new instance with fast mode enabled.
*
* @param world the world
*/
public FastModeExtent(World world) {
this(world, true);
}
/**
* Create a new instance.
*
* @param world the world
* @param enabled true to enable fast mode
*/
public FastModeExtent(World world, boolean enabled) {
public SideEffectExtent(World world) {
super(world);
checkNotNull(world);
this.world = world;
this.enabled = enabled;
if (enabled) {
this.postEditSimulation = true;
}
}
/**
* Return whether fast mode is enabled.
*
* @return true if fast mode is enabled
*/
public boolean isEnabled() {
return enabled;
}
/**
* Set fast mode enable status.
*
* @param enabled true to enable fast mode
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isPostEditSimulationEnabled() {
@ -98,26 +69,28 @@ public class FastModeExtent extends AbstractDelegateExtent {
this.postEditSimulation = enabled;
}
public SideEffectSet getSideEffectSet() {
return this.sideEffectSet;
}
public void setSideEffectSet(SideEffectSet sideEffectSet) {
this.sideEffectSet = sideEffectSet;
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 location, B block) throws WorldEditException {
if (enabled || postEditSimulation) {
if (sideEffectSet.getState(SideEffect.LIGHTING) == SideEffect.State.DELAYED) {
dirtyChunks.add(BlockVector2.at(location.getBlockX() >> 4, location.getBlockZ() >> 4));
if (world.setBlock(location, block, false)) {
if (!enabled && postEditSimulation) {
positions.add(location);
}
return true;
}
return false;
} else {
return world.setBlock(location, block, true);
}
if (postEditSimulation) {
positions.put(location, world.getBlock(location));
}
return world.setBlock(location, block, postEditSimulation ? SideEffectSet.none() : sideEffectSet);
}
public boolean commitRequired() {
return enabled || postEditSimulation;
return postEditSimulation || !dirtyChunks.isEmpty();
}
@Override
@ -132,11 +105,11 @@ public class FastModeExtent extends AbstractDelegateExtent {
world.fixAfterFastMode(dirtyChunks);
}
if (!enabled && postEditSimulation) {
Iterator<BlockVector3> positionIterator = positions.iterator();
if (postEditSimulation) {
Iterator<Map.Entry<BlockVector3, BlockState>> positionIterator = positions.entrySet().iterator();
while (run.shouldContinue() && positionIterator.hasNext()) {
BlockVector3 position = positionIterator.next();
world.notifyAndLightBlock(position, BlockTypes.AIR.getDefaultState());
Map.Entry<BlockVector3, BlockState> position = positionIterator.next();
world.applySideEffects(position.getKey(), position.getValue(), sideEffectSet);
positionIterator.remove();
}
@ -151,5 +124,4 @@ public class FastModeExtent extends AbstractDelegateExtent {
}
};
}
}

View File

@ -52,6 +52,19 @@ public class RandomPattern extends AbstractPattern {
this.random = random;
}
/**
* Create a random pattern from an existing one but with a different random.
*
* @param random the new random to use.
* @param parent the existing random pattern.
*/
public RandomPattern(SimpleRandom random, RandomPattern parent) {
this.random = random;
this.weights = parent.weights;
this.collection = RandomCollection.of(weights, random);
this.patterns = parent.patterns;
}
/**
* Add a pattern to the weight list of patterns.
*

View File

@ -0,0 +1,184 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.internal.wna;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Natively access and perform operations on the world.
*
* @param <NC> the native chunk type
* @param <NBS> the native block state type
* @param <NP> the native position type
*/
public interface WorldNativeAccess<NC, NBS, NP> {
default <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException {
checkNotNull(position);
checkNotNull(block);
setCurrentSideEffectSet(sideEffects);
int x = position.getBlockX();
int y = position.getBlockY();
int z = position.getBlockZ();
// First set the block
NC chunk = getChunk(x >> 4, z >> 4);
NP pos = getPosition(x, y, z);
NBS old = getBlockState(chunk, pos);
NBS newState = toNative(block.toImmutableState());
// change block prior to placing if it should be fixed
if (sideEffects.shouldApply(SideEffect.VALIDATION)) {
newState = getValidBlockForPosition(newState, pos);
}
NBS successState = setBlockState(chunk, pos, newState);
boolean successful = successState != null;
// Create the TileEntity
if (successful || old == newState) {
if (block instanceof BaseBlock) {
BaseBlock baseBlock = (BaseBlock) block;
CompoundTag tag = baseBlock.getNbtData();
if (tag != null) {
tag = tag.createBuilder()
.putString("id", baseBlock.getNbtId())
.putInt("x", position.getX())
.putInt("y", position.getY())
.putInt("z", position.getZ())
.build();
// update if TE changed as well
successful = updateTileEntity(pos, tag);
}
}
}
if (successful) {
if (sideEffects.getState(SideEffect.LIGHTING) == SideEffect.State.ON) {
updateLightingForBlock(pos);
}
markAndNotifyBlock(pos, chunk, old, newState, sideEffects);
}
return successful;
}
default void applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) {
setCurrentSideEffectSet(sideEffectSet);
NP pos = getPosition(position.getX(), position.getY(), position.getZ());
NC chunk = getChunk(position.getX() >> 4, position.getZ() >> 4);
NBS oldData = toNative(previousType);
NBS newData = getBlockState(chunk, pos);
if (sideEffectSet.getState(SideEffect.LIGHTING) == SideEffect.State.ON) {
updateLightingForBlock(pos);
}
markAndNotifyBlock(pos, chunk, oldData, newData, sideEffectSet);
}
// state-keeping functions for WNA
// may be thread-unsafe, as this is single-threaded code
/**
* Receive the current side-effect set from the high level call.
*
* This allows the implementation to branch on the side-effects internally.
*
* @param sideEffectSet the set of side-effects
*/
default void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
}
// access functions
NC getChunk(int x, int z);
NBS toNative(BlockState state);
NBS getBlockState(NC chunk, NP position);
@Nullable
NBS setBlockState(NC chunk, NP position, NBS state);
NBS getValidBlockForPosition(NBS block, NP position);
NP getPosition(int x, int y, int z);
void updateLightingForBlock(NP position);
boolean updateTileEntity(NP position, CompoundTag tag);
void notifyBlockUpdate(NP position, NBS oldState, NBS newState);
boolean isChunkTicking(NC chunk);
void markBlockChanged(NP position);
void notifyNeighbors(NP pos, NBS oldState, NBS newState);
void updateNeighbors(NP pos, NBS oldState, NBS newState, int recursionLimit);
void onBlockStateChange(NP pos, NBS oldState, NBS newState);
/**
* This is a heavily modified function stripped from MC to apply worldedit-modifications.
*
* See Forge's World.markAndNotifyBlock
*/
default void markAndNotifyBlock(NP pos, NC chunk, NBS oldState, NBS newState, SideEffectSet sideEffectSet) {
NBS blockState1 = getBlockState(chunk, pos);
if (blockState1 != newState) {
return;
}
// Remove redundant branches
if (isChunkTicking(chunk)) {
if (sideEffectSet.shouldApply(SideEffect.ENTITY_AI)) {
notifyBlockUpdate(pos, oldState, newState);
} else {
// If we want to skip entity AI, just mark the block for sending
markBlockChanged(pos);
}
}
if (sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) {
notifyNeighbors(pos, oldState, newState);
}
// Make connection updates optional
if (sideEffectSet.shouldApply(SideEffect.VALIDATION)) {
updateNeighbors(pos, oldState, newState, 512);
}
onBlockStateChange(pos, oldState, newState);
}
}

View File

@ -0,0 +1,25 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* "WNA", or WorldEdit Native Access.
*
* Contains internal helper functions for sharing code between platforms.
*/
package com.sk89q.worldedit.internal.wna;

View File

@ -0,0 +1,68 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util;
import java.util.Locale;
public enum SideEffect {
LIGHTING(State.ON),
NEIGHBORS(State.ON),
VALIDATION(State.ON),
ENTITY_AI(State.OFF),
EVENTS(State.OFF);
private final String displayName;
private final String description;
private final State defaultValue;
SideEffect(State defaultValue) {
this.displayName = "worldedit.sideeffect." + this.name().toLowerCase(Locale.US);
this.description = "worldedit.sideeffect." + this.name().toLowerCase(Locale.US) + ".description";
this.defaultValue = defaultValue;
}
public String getDisplayName() {
return this.displayName;
}
public String getDescription() {
return this.description;
}
public State getDefaultValue() {
return this.defaultValue;
}
public enum State {
OFF,
ON,
DELAYED;
private final String displayName;
State() {
this.displayName = "worldedit.sideeffect.state." + this.name().toLowerCase(Locale.US);
}
public String getDisplayName() {
return this.displayName;
}
}
}

View File

@ -0,0 +1,94 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
public class SideEffectSet {
private static final SideEffectSet DEFAULT = new SideEffectSet(
Arrays.stream(SideEffect.values()).collect(Collectors.toMap(Function.identity(), SideEffect::getDefaultValue))
);
private static final SideEffectSet NONE = new SideEffectSet();
private final Map<SideEffect, SideEffect.State> sideEffects;
private final Set<SideEffect> appliedSideEffects;
private final boolean appliesAny;
private SideEffectSet() {
this(ImmutableMap.of());
}
public SideEffectSet(Map<SideEffect, SideEffect.State> sideEffects) {
this.sideEffects = Maps.immutableEnumMap(sideEffects);
appliedSideEffects = sideEffects.entrySet()
.stream()
.filter(entry -> entry.getValue() != SideEffect.State.OFF)
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
appliesAny = !appliedSideEffects.isEmpty();
}
public SideEffectSet with(SideEffect sideEffect, SideEffect.State state) {
Map<SideEffect, SideEffect.State> entries = this.sideEffects.isEmpty() ? Maps.newEnumMap(SideEffect.class) : new EnumMap<>(this.sideEffects);
entries.put(sideEffect, state);
return new SideEffectSet(entries);
}
public boolean doesApplyAny() {
return this.appliesAny;
}
public SideEffect.State getState(SideEffect effect) {
return sideEffects.getOrDefault(effect, effect.getDefaultValue());
}
/**
* Gets whether this side effect is not off.
*
* This returns whether it is either delayed or on.
*
* @param effect The side effect
* @return Whether it should apply
*/
public boolean shouldApply(SideEffect effect) {
return getState(effect) != SideEffect.State.OFF;
}
public Set<SideEffect> getSideEffectsToApply() {
return this.appliedSideEffects;
}
public static SideEffectSet defaults() {
return DEFAULT;
}
public static SideEffectSet none() {
return NONE;
}
}

View File

@ -0,0 +1,88 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.formatting.component;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
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.util.formatting.text.event.HoverEvent;
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
public class SideEffectBox extends PaginationBox {
private static List<SideEffect> sideEffects;
private SideEffectSet sideEffectSet;
private static List<SideEffect> getSideEffects() {
if (sideEffects == null) {
sideEffects = WorldEdit.getInstance().getPlatformManager().getSupportedSideEffects()
.stream()
.sorted(Comparator.comparing(Enum::name))
.collect(Collectors.toList());
}
return sideEffects;
}
public SideEffectBox(SideEffectSet sideEffectSet) {
super("Side Effects");
this.sideEffectSet = sideEffectSet;
}
private static final SideEffect.State[] SHOWN_VALUES = {SideEffect.State.OFF, SideEffect.State.ON};
@Override
public Component getComponent(int number) {
SideEffect effect = getSideEffects().get(number);
SideEffect.State state = this.sideEffectSet.getState(effect);
TextComponent.Builder builder = TextComponent.builder();
builder = builder.append(TranslatableComponent.of(effect.getDisplayName(), TextColor.YELLOW)
.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TranslatableComponent.of(effect.getDescription()))));
for (SideEffect.State uiState : SHOWN_VALUES) {
builder = builder.append(TextComponent.space());
builder = builder.append(TranslatableComponent.of(uiState.getDisplayName(), uiState == state ? TextColor.WHITE : TextColor.GRAY)
.clickEvent(ClickEvent.runCommand("//fast -h " + effect.name().toLowerCase(Locale.US) + " " + uiState.name().toLowerCase(Locale.US)))
.hoverEvent(HoverEvent.showText(uiState == state
? TranslatableComponent.of("worldedit.sideeffect.box.current")
: TranslatableComponent.of("worldedit.sideeffect.box.change-to", TranslatableComponent.of(uiState.getDisplayName()))
))
);
}
return builder.build();
}
@Override
public int getComponentsSize() {
return getSideEffects().size();
}
}

View File

@ -32,6 +32,7 @@ import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.weather.WeatherType;
import com.sk89q.worldedit.world.block.BlockType;
@ -57,7 +58,7 @@ public abstract class AbstractWorld implements World {
@Override
public final <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 pt, B block) throws WorldEditException {
return setBlock(pt, block, true);
return setBlock(pt, block, SideEffectSet.defaults());
}
@Override

View File

@ -23,6 +23,7 @@ import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
import com.boydti.fawe.beta.implementation.blocks.NullChunkGet;
import com.sk89q.jnbt.CompoundTag;
import com.google.common.collect.ImmutableSet;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.WorldEditException;
@ -35,6 +36,8 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.TreeGenerator.TreeType;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
@ -48,6 +51,7 @@ import com.sk89q.worldedit.world.weather.WeatherTypes;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* A null implementation of {@link World} that drops all changes and
@ -71,13 +75,14 @@ public class NullWorld extends AbstractWorld {
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException {
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException {
return false;
}
@Override
public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException {
return false;
public Set<SideEffect> applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet)
throws WorldEditException {
return ImmutableSet.of();
}
@Override

View File

@ -37,15 +37,19 @@ import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.registry.Keyed;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.weather.WeatherType;
import javax.annotation.Nullable;
import java.nio.file.Path;
import java.util.Locale;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Represents a world (dimension).
@ -112,10 +116,28 @@ public interface World extends Extent, Keyed, IChunkCache<IChunkGet> {
* @param notifyAndLight true to to notify and light
* @return true if the block was successfully set (return value may not be accurate)
*/
@Deprecated
default <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException {
return setBlock(position, block);
return setBlock(position, block, notifyAndLight ? SideEffectSet.defaults() : SideEffectSet.none());
}
/**
* Similar to {@link Extent#setBlock(BlockVector3, BlockStateHolder)} but a
* {@code sideEffects} parameter indicates which side effects should be applied
* to the block. This includes block updates, lighting, and others. See {@link SideEffect}
* for a full list.
*
* <p>Not all implementations support all side effects. Use
* {@link Platform#getSupportedSideEffects()} for a list of supported side effects.
* Non-supported side effects will be ignored.</p>
*
* @param position position of the block
* @param block block to set
* @param sideEffects which side effects to perform
* @return true if the block was successfully set (return value may not be accurate)
*/
<B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException;
/**
* Notifies the simulation that the block at the given location has
* been changed and it must be re-lighted (and issue other events).
@ -124,7 +146,20 @@ public interface World extends Extent, Keyed, IChunkCache<IChunkGet> {
* @param previousType the type of the previous block that was there
* @return true if the block was successfully notified
*/
boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException;
@Deprecated
default boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException {
return !applySideEffects(position, previousType, SideEffectSet.defaults()).isEmpty();
}
/**
* Applies a set of side effects on the given block.
*
* @param position position of the block
* @param previousType the type of the previous block that was there
* @param sideEffectSet which side effects to perform
* @return a set of side effects that were applied
*/
Set<SideEffect> applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException;
/**
* Get the light level at the given block.

View File

@ -210,6 +210,10 @@
"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.disabled.already": "Fast mode already disabled.",
"worldedit.fast.enabled.already": "Fast mode already enabled.",
"worldedit.fast.sideeffect.set": "Side effect \"{0}\" set to {1}",
"worldedit.fast.sideeffect.get": "Side effect \"{0}\" is set to {1}",
"worldedit.fast.sideeffect.already-set": "Side effect \"{0}\" is already {1}",
"worldedit.fast.sideeffect.set-all": "All side effects set to {0}",
"worldedit.reorder.current": "The reorder mode is {0}",
"worldedit.reorder.set": "The reorder mode is now {0}",
"worldedit.gmask.disabled": "Global mask disabled.",
@ -494,6 +498,22 @@
"worldedit.selection.sphere.explain.secondary": "Radius set to {0}.",
"worldedit.selection.sphere.explain.secondary-defined": "Radius set to {0} ({1}).",
"worldedit.sideeffect.lighting": "Lighting",
"worldedit.sideeffect.lighting.description": "Updates block lighting",
"worldedit.sideeffect.neighbors": "Neighbors",
"worldedit.sideeffect.neighbors.description": "Notifies nearby blocks of changes",
"worldedit.sideeffect.validation": "Validation",
"worldedit.sideeffect.validation.description": "Validates and fixes inconsistent world state, such as disconnected blocks",
"worldedit.sideeffect.entity_ai": "Entity AI",
"worldedit.sideeffect.entity_ai.description": "Updates Entity AI paths for the block changes",
"worldedit.sideeffect.events": "Mod/Plugin Events",
"worldedit.sideeffect.events.description": "Tells other mods/plugins about these changes when applicable",
"worldedit.sideeffect.state.on": "On",
"worldedit.sideeffect.state.delayed": "Delayed",
"worldedit.sideeffect.state.off": "Off",
"worldedit.sideeffect.box.current": "Current",
"worldedit.sideeffect.box.change-to": "Click to set to {0}",
"worldedit.help.command-not-found": "The command '{0}' could not be found.",
"worldedit.help.no-subcommands": "'{0}' has no sub-commands. (Maybe '{1}' is for a parameter?)",
"worldedit.help.subcommand-not-found": "The sub-command '{0}' under '{1}' could not be found.",