It started on work with commands then I got carried away.

This commit is contained in:
MattBDev
2019-07-25 14:44:10 -04:00
parent 01c371df9c
commit ff5860113d
184 changed files with 1694 additions and 2580 deletions

View File

@ -43,6 +43,7 @@ import javax.annotation.Nullable;
* A base class for {@link Extent}s that merely passes extents onto another.
*/
public class AbstractDelegateExtent implements Extent, LightingExtent {
private final Extent extent;
/**
@ -60,14 +61,19 @@ public class AbstractDelegateExtent implements Extent, LightingExtent {
*
* @return the extent
*/
public final Extent getExtent() {
public Extent getExtent() {
return extent;
}
@Override
public BlockState getBlock(BlockVector3 position) {
return getBlock(position.getX(),position.getY(),position.getZ());
}
/*
Queue based methods
TODO NOT IMPLEMENTED: IQueueExtent and such need to implement these
*/
Queue based methods
TODO NOT IMPLEMENTED: IQueueExtent and such need to implement these
*/
public boolean isQueueEnabled() {
return extent.isQueueEnabled();
}
@ -78,7 +84,8 @@ public class AbstractDelegateExtent implements Extent, LightingExtent {
if (!(extent instanceof ForgetfulExtentBuffer)) { // placeholder
extent.disableQueue();
}
} catch (FaweException ignored) {}
} catch (FaweException ignored) {
}
if (extent instanceof AbstractDelegateExtent) {
Extent next = ((AbstractDelegateExtent) extent).getExtent();
new ExtentTraverser(this).setNext(next);
@ -108,8 +115,7 @@ public class AbstractDelegateExtent implements Extent, LightingExtent {
} else {
history.setChangeSet(changeSet);
}
}
else if (extent instanceof AbstractDelegateExtent) {
} else if (extent instanceof AbstractDelegateExtent) {
((AbstractDelegateExtent) extent).setChangeSet(changeSet);
} else if (changeSet != null) {
new ExtentTraverser<>(this).setNext(new HistoryExtent(extent, changeSet));
@ -124,21 +130,12 @@ public class AbstractDelegateExtent implements Extent, LightingExtent {
/*
Bounds
*/
@Override
public BlockVector3 getMinimumPoint() {
return extent.getMinimumPoint();
}
@Override
public BlockVector3 getMaximumPoint() {
return extent.getMaximumPoint();
}
@Override
public int getMaxY() {
return extent.getMaxY();
}
/*
Input + Output
*/
@ -147,7 +144,6 @@ public class AbstractDelegateExtent implements Extent, LightingExtent {
public BlockState getBlock(int x, int y, int z) {
return extent.getBlock(x, y, z);
}
@Override
public BaseBlock getFullBlock(int x, int y, int z) {
return extent.getFullBlock(x, y, z);
@ -164,10 +160,12 @@ public class AbstractDelegateExtent implements Extent, LightingExtent {
}
@Override
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T block) throws WorldEditException {
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T block)
throws WorldEditException {
return extent.setBlock(x, y, z, block);
}
/*
Light
*/
@ -184,7 +182,6 @@ public class AbstractDelegateExtent implements Extent, LightingExtent {
}
return getBrightness(x, y, z);
}
public int getOpacity(int x, int y, int z) {
if (extent instanceof LightingExtent) {
return ((LightingExtent) extent).getOpacity(x, y, z);
@ -217,15 +214,21 @@ public class AbstractDelegateExtent implements Extent, LightingExtent {
}
}
/*
Generic
*/
@Override
public String toString() {
return super.toString() + ":" + extent.toString();
}
@Override
public BlockVector3 getMinimumPoint() {
return extent.getMinimumPoint();
}
@Override
public BlockVector3 getMaximumPoint() {
return extent.getMaximumPoint();
}
protected Operation commitBefore() {
return null;
}
@ -234,7 +237,9 @@ public class AbstractDelegateExtent implements Extent, LightingExtent {
public @Nullable Operation commit() {
Operation ours = commitBefore();
Operation other = null;
if (extent != this) other = extent.commit();
if (extent != this) {
other = extent.commit();
}
if (ours != null && other != null) {
return new OperationQueue(ours, other);
} else if (ours != null) {

View File

@ -45,8 +45,6 @@ import javax.annotation.Nullable;
*/
public class NullExtent implements Extent {
public static final NullExtent INSTANCE = new NullExtent();
@Override
public BlockVector3 getMinimumPoint() {
return BlockVector3.ZERO;
@ -73,6 +71,7 @@ public class NullExtent implements Extent {
return null;
}
@Override
public BlockState getBlock(BlockVector3 position) {
return BlockTypes.AIR.getDefaultState();
}

View File

@ -19,9 +19,11 @@
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.AbstractDelegateExtent;
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;
@ -29,16 +31,14 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Optional;
/**
* Buffers changes to an {@link Extent} and allows retrieval of the changed blocks,
* without modifying the underlying extent.
*/
public class ExtentBuffer extends AbstractDelegateExtent {
public class ExtentBuffer extends AbstractBufferingExtent {
private final Map<BlockVector3, BaseBlock> buffer = Maps.newHashMap();
private final Mask mask;
@ -66,6 +66,14 @@ public class ExtentBuffer extends AbstractDelegateExtent {
this.mask = mask;
}
@Override
protected Optional<BaseBlock> getBufferedBlock(BlockVector3 position) {
if (mask.test(position)) {
return Optional.of(buffer.computeIfAbsent(position, (pos -> getExtent().getFullBlock(pos))));
}
return Optional.empty();
}
@Override
public BlockState getBlock(BlockVector3 position) {
if (mask.test(position)) {

View File

@ -61,7 +61,7 @@ public interface Clipboard extends Extent {
/**
* Returns true if the clipboard has biome data. This can be checked since {@link Extent#getBiome(BlockVector2)}
* strongly suggests returning {@link com.sk89q.worldedit.world.biome.BiomeTypes#OCEAN} instead of {@code null}
* strongly suggests returning {@link com.sk89q.worldedit.world.biome.BiomeTypes.OCEAN} instead of {@code null}
* if biomes aren't present. However, it might not be desired to set areas to ocean if the clipboard is defaulting
* to ocean, instead of having biomes explicitly set.
*

View File

@ -72,13 +72,27 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
@Override
public ClipboardWriter getWriter(OutputStream outputStream) throws IOException {
throw new IOException("This format does not support saving, use `.schem` as format");
throw new IOException("This format does not support saving");
}
@Override
public boolean isFormat(File file) {
String name = file.getName().toLowerCase();
return name.endsWith(".schematic") || name.endsWith(".mcedit") || name.endsWith(".mce");
try (NBTInputStream str = new NBTInputStream(new GZIPInputStream(new FileInputStream(file)))) {
NamedTag rootTag = str.readNamedTag();
if (!rootTag.getName().equals("Schematic")) {
return false;
}
CompoundTag schematicTag = (CompoundTag) rootTag.getTag();
// Check
Map<String, Tag> schematic = schematicTag.getValue();
if (!schematic.containsKey("Materials")) {
return false;
}
} catch (Exception e) {
return false;
}
return true;
}
},
SPONGE_SCHEMATIC("sponge", "schem") {
@ -113,8 +127,23 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
@Override
public boolean isFormat(File file) {
String name = file.getName().toLowerCase();
return name.endsWith(".schem") || name.endsWith(".sponge");
try (NBTInputStream str = new NBTInputStream(new GZIPInputStream(new FileInputStream(file)))) {
NamedTag rootTag = str.readNamedTag();
if (!rootTag.getName().equals("Schematic")) {
return false;
}
CompoundTag schematicTag = (CompoundTag) rootTag.getTag();
// Check
Map<String, Tag> schematic = schematicTag.getValue();
if (!schematic.containsKey("Version")) {
return false;
}
} catch (Exception e) {
return false;
}
return true;
}
},
@ -149,6 +178,8 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
try (NBTInputStream str = new NBTInputStream(new GZIPInputStream(new FileInputStream(file)))) {
NamedTag rootTag = str.readNamedTag();
CompoundTag structureTag = (CompoundTag) rootTag.getTag();
// Check
Map<String, Tag> structure = structureTag.getValue();
if (!structure.containsKey("DataVersion")) {
return false;

View File

@ -69,7 +69,7 @@ public class ClipboardFormats {
checkNotNull(format);
for (String key : format.getAliases()) {
String lowKey = key.toLowerCase(Locale.ROOT);
String lowKey = key.toLowerCase(Locale.ENGLISH);
ClipboardFormat old = aliasMap.put(lowKey, format);
if (old != null) {
aliasMap.put(lowKey, old);
@ -77,7 +77,7 @@ public class ClipboardFormats {
}
}
for (String ext : format.getFileExtensions()) {
String lowExt = ext.toLowerCase(Locale.ROOT);
String lowExt = ext.toLowerCase(Locale.ENGLISH);
fileExtensionMap.put(lowExt, format);
}
registeredFormats.add(format);
@ -99,7 +99,7 @@ public class ClipboardFormats {
@Nullable
public static ClipboardFormat findByAlias(String alias) {
checkNotNull(alias);
return aliasMap.get(alias.toLowerCase(Locale.ROOT).trim());
return aliasMap.get(alias.toLowerCase(Locale.ENGLISH).trim());
}
/**
@ -157,7 +157,7 @@ public class ClipboardFormats {
* It is not in SchematicCommands because it may rely on internal register calls.
*/
public static String[] getFileExtensionArray() {
return fileExtensionMap.keySet().toArray(new String[0]);
return fileExtensionMap.keySet().toArray(new String[fileExtensionMap.keySet().size()]);
}
private ClipboardFormats() {
@ -259,7 +259,7 @@ public class ClipboardFormats {
File[] files = dir.listFiles(pathname -> {
String input = pathname.getName();
String extension = input.substring(input.lastIndexOf('.') + 1);
return (extensions.contains(extension.toLowerCase()));
return (extensions.contains(extension.toLowerCase(Locale.ENGLISH)));
});
LazyClipboardHolder[] clipboards = new LazyClipboardHolder[files.length];
for (int i = 0; i < files.length; i++) {

View File

@ -19,10 +19,18 @@
package com.sk89q.worldedit.extent.clipboard.io;
import com.sk89q.jnbt.DoubleTag;
import com.sk89q.jnbt.FloatTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.util.Location;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Writes {@code Clipboard}s.
@ -39,4 +47,18 @@ public interface ClipboardWriter extends Closeable {
*/
void write(Clipboard clipboard) throws IOException;
default Tag writeVector(Vector3 vector) {
List<DoubleTag> list = new ArrayList<>();
list.add(new DoubleTag(vector.getX()));
list.add(new DoubleTag(vector.getY()));
list.add(new DoubleTag(vector.getZ()));
return new ListTag(DoubleTag.class, list);
}
default Tag writeRotation(Location location) {
List<FloatTag> list = new ArrayList<>();
list.add(new FloatTag(location.getYaw()));
list.add(new FloatTag(location.getPitch()));
return new ListTag(FloatTag.class, list);
}
}

View File

@ -19,9 +19,12 @@
package com.sk89q.worldedit.extent.clipboard.io;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.jnbt.NBTStreamer;
import com.boydti.fawe.jnbt.NBTStreamer.LazyReader;
import com.boydti.fawe.object.FaweInputStream;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.clipboard.CPUOptimizedClipboard;
@ -31,21 +34,15 @@ import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.boydti.fawe.object.io.FastByteArraysInputStream;
import com.boydti.fawe.util.IOUtil;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NamedTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extension.platform.Platform;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.BlockVector2;
@ -60,18 +57,16 @@ import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.entity.EntityTypes;
import com.sk89q.worldedit.world.storage.NBTConversions;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.BiConsumer;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Reads schematic files using the Sponge Schematic Specification.
@ -174,24 +169,18 @@ public class SpongeSchematicReader extends NBTSchematicReader {
/// readBiomes
streamer.addReader("Schematic.BlockData.#", new NBTStreamer.LazyReader() {
@Override
public void accept(Integer arrayLen, DataInputStream dis) {
try (FaweOutputStream blocks = new FaweOutputStream(new LZ4BlockOutputStream(blocksOut))) {
IOUtil.copy(dis, blocks, arrayLen);
} catch (IOException e) {
e.printStackTrace();
}
streamer.addReader("Schematic.BlockData.#", (LazyReader) (arrayLen, dis) -> {
try (FaweOutputStream blocks = new FaweOutputStream(new LZ4BlockOutputStream(blocksOut))) {
IOUtil.copy(dis, blocks, arrayLen);
} catch (IOException e) {
e.printStackTrace();
}
});
streamer.addReader("Schematic.Biomes.#", new NBTStreamer.LazyReader() {
@Override
public void accept(Integer arrayLen, DataInputStream dis) {
try (FaweOutputStream biomes = new FaweOutputStream(new LZ4BlockOutputStream(biomesOut))) {
IOUtil.copy(dis, biomes, arrayLen);
} catch (IOException e) {
e.printStackTrace();
}
streamer.addReader("Schematic.Biomes.#", (LazyReader) (arrayLen, dis) -> {
try (FaweOutputStream biomes = new FaweOutputStream(new LZ4BlockOutputStream(biomesOut))) {
IOUtil.copy(dis, biomes, arrayLen);
} catch (IOException e) {
e.printStackTrace();
}
});
streamer.addReader("Schematic.TileEntities.#", (BiConsumer<Integer, CompoundTag>) (index, value) -> {
@ -291,6 +280,17 @@ public class SpongeSchematicReader extends NBTSchematicReader {
return clipboard;
}
private Clipboard readVersion2(BlockArrayClipboard version1, CompoundTag schematicTag) throws IOException {
Map<String, Tag> schematic = schematicTag.getValue();
if (schematic.containsKey("BiomeData")) {
readBiomes(version1, schematic);
}
if (schematic.containsKey("Entities")) {
readEntities(version1, schematic);
}
return version1;
}
private void readBiomes(BlockArrayClipboard clipboard, Map<String, Tag> schematic) throws IOException {
ByteArrayTag dataTag = requireTag(schematic, "BiomeData", ByteArrayTag.class);
IntTag maxTag = requireTag(schematic, "BiomePaletteMax", IntTag.class);
@ -349,6 +349,36 @@ public class SpongeSchematicReader extends NBTSchematicReader {
}
}
private void readEntities(BlockArrayClipboard clipboard, Map<String, Tag> schematic) throws IOException {
List<Tag> entList = requireTag(schematic, "Entities", ListTag.class).getValue();
if (entList.isEmpty()) {
return;
}
for (Tag et : entList) {
if (!(et instanceof CompoundTag)) {
continue;
}
CompoundTag entityTag = (CompoundTag) et;
Map<String, Tag> tags = entityTag.getValue();
String id = requireTag(tags, "Id", StringTag.class).getValue();
entityTag = entityTag.createBuilder().putString("id", id).remove("Id").build();
if (fixer != null) {
entityTag = fixer.fixUp(DataFixer.FixTypes.ENTITY, entityTag, dataVersion);
}
EntityType entityType = EntityTypes.get(id);
if (entityType != null) {
Location location = NBTConversions.toLocation(clipboard,
requireTag(tags, "Pos", ListTag.class),
requireTag(tags, "Rotation", ListTag.class));
BaseEntity state = new BaseEntity(entityType, entityTag);
clipboard.createEntity(location, state);
} else {
log.warn("Unknown entity when pasting schematic: " + id);
}
}
}
@Override
public void close() throws IOException {
inputStream.close();

View File

@ -19,13 +19,19 @@
package com.sk89q.worldedit.extent.clipboard.io;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.jnbt.NBTStreamer;
import com.boydti.fawe.object.clipboard.FaweClipboard;
import com.boydti.fawe.util.IOUtil;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Maps;
import com.sk89q.jnbt.*;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntArrayTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.NBTConstants;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
@ -34,18 +40,13 @@ import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
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.Location;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
@ -53,13 +54,13 @@ import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
/**
* Writes schematic files using the Sponge schematic format.
@ -345,21 +346,6 @@ public class SpongeSchematicWriter implements ClipboardWriter {
schematic.writeNamedTag("Entities", new ListTag(CompoundTag.class, entities));
}
private static Tag writeVector(Vector3 vector) {
List<DoubleTag> list = new ArrayList<>();
list.add(new DoubleTag(vector.getX()));
list.add(new DoubleTag(vector.getY()));
list.add(new DoubleTag(vector.getZ()));
return new ListTag(DoubleTag.class, list);
}
private static Tag writeRotation(Location location) {
List<FloatTag> list = new ArrayList<>();
list.add(new FloatTag(location.getYaw()));
list.add(new FloatTag(location.getPitch()));
return new ListTag(FloatTag.class, list);
}
@Override
public void close() throws IOException {
outputStream.close();

View File

@ -28,14 +28,10 @@ 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.block.BlockTypes;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.annotation.Nullable;
/**
* Applies a {@link BlockBag} to operations.
@ -44,7 +40,7 @@ public class BlockBagExtent extends AbstractDelegateExtent {
private final boolean mine;
private int[] missingBlocks = new int[BlockTypes.size()];
private final BlockBag blockBag;
private BlockBag blockBag;
/**
* Create a new instance.
@ -52,13 +48,12 @@ public class BlockBagExtent extends AbstractDelegateExtent {
* @param extent the extent
* @param blockBag the block bag
*/
public BlockBagExtent(Extent extent, @Nonnull BlockBag blockBag) {
public BlockBagExtent(Extent extent, @Nullable BlockBag blockBag) {
this(extent, blockBag, false);
}
public BlockBagExtent(Extent extent, @Nonnull BlockBag blockBag, boolean mine) {
public BlockBagExtent(Extent extent, @Nullable BlockBag blockBag, boolean mine) {
super(extent);
checkNotNull(blockBag);
this.blockBag = blockBag;
this.mine = mine;
}
@ -72,6 +67,14 @@ public class BlockBagExtent extends AbstractDelegateExtent {
return blockBag;
}
/**
* Set the block bag.
*
* @param blockBag a block bag, which may be null if none is used
*/
public void setBlockBag(@Nullable BlockBag blockBag) {
this.blockBag = blockBag;
}
/**
* Gets the list of missing blocks and clears the list for the next
* operation.

View File

@ -19,22 +19,24 @@
package com.sk89q.worldedit.extent.reorder;
import com.google.common.collect.Table;
import com.google.common.collect.TreeBasedTable;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.AbstractBufferingExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.RunContext;
import com.sk89q.worldedit.function.operation.SetLocatedBlocks;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.collection.LocatedBlockList;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
* A special extent that batches changes into Minecraft chunks. This helps
@ -42,17 +44,19 @@ import java.util.TreeMap;
* loaded repeatedly, however it does take more memory due to caching the
* blocks.
*/
public class ChunkBatchingExtent extends AbstractDelegateExtent {
public class ChunkBatchingExtent extends AbstractBufferingExtent {
/**
* Comparator optimized for sorting chunks by the region file they reside
* in. This allows for file caches to be used while loading the chunk.
*/
private static final Comparator<BlockVector2> REGION_OPTIMIZED_SORT =
Comparator.comparing((BlockVector2 vec) -> vec.divide(32), BlockVector2.COMPARING_GRID_ARRANGEMENT)
Comparator.comparing((BlockVector2 vec) -> vec.shr(5), BlockVector2.COMPARING_GRID_ARRANGEMENT)
.thenComparing(BlockVector2.COMPARING_GRID_ARRANGEMENT);
private final SortedMap<BlockVector2, LocatedBlockList> batches = new TreeMap<>(REGION_OPTIMIZED_SORT);
private final Table<BlockVector2, BlockVector3, BaseBlock> batches =
TreeBasedTable.create(REGION_OPTIMIZED_SORT, BlockVector3.sortByCoordsYzx());
private final Set<BlockVector3> containedBlocks = new HashSet<>();
private boolean enabled;
public ChunkBatchingExtent(Extent extent) {
@ -76,16 +80,32 @@ public class ChunkBatchingExtent extends AbstractDelegateExtent {
return enabled;
}
private BlockVector2 getChunkPos(BlockVector3 location) {
return location.shr(4).toBlockVector2();
}
private BlockVector3 getInChunkPos(BlockVector3 location) {
return BlockVector3.at(location.getX() & 15, location.getY(), location.getZ() & 15);
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 location, B block) throws WorldEditException {
if (!enabled) {
return getExtent().setBlock(location, block);
return setDelegateBlock(location, block);
}
BlockVector2 chunkPos = BlockVector2.at(location.getBlockX() >> 4, location.getBlockZ() >> 4);
batches.computeIfAbsent(chunkPos, k -> new LocatedBlockList()).add(location, block);
BlockVector2 chunkPos = getChunkPos(location);
BlockVector3 inChunkPos = getInChunkPos(location);
batches.put(chunkPos, inChunkPos, block.toBaseBlock());
containedBlocks.add(location);
return true;
}
@Override
protected Optional<BaseBlock> getBufferedBlock(BlockVector3 position) {
if (!containedBlocks.contains(position)) {
return Optional.empty();
}
return Optional.of(batches.get(getChunkPos(position), getInChunkPos(position)));
}
@Override
protected Operation commitBefore() {
if (!commitRequired()) {
@ -94,17 +114,22 @@ public class ChunkBatchingExtent extends AbstractDelegateExtent {
return new Operation() {
// we get modified between create/resume -- only create this on resume to prevent CME
private Iterator<LocatedBlockList> batchIterator;
private Iterator<Map.Entry<BlockVector2, Map<BlockVector3, BaseBlock>>> batchIterator;
@Override
public Operation resume(RunContext run) throws WorldEditException {
if (batchIterator == null) {
batchIterator = batches.values().iterator();
batchIterator = batches.rowMap().entrySet().iterator();
}
if (!batchIterator.hasNext()) {
return null;
}
new SetLocatedBlocks(getExtent(), batchIterator.next()).resume(run);
Map.Entry<BlockVector2, Map<BlockVector3, BaseBlock>> next = batchIterator.next();
BlockVector3 chunkOffset = next.getKey().toBlockVector3().shl(4);
for (Map.Entry<BlockVector3, BaseBlock> block : next.getValue().entrySet()) {
getExtent().setBlock(block.getKey().add(chunkOffset), block.getValue());
containedBlocks.remove(block.getKey());
}
batchIterator.remove();
return this;
}

View File

@ -20,7 +20,7 @@
package com.sk89q.worldedit.extent.reorder;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.AbstractBufferingExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.OperationQueue;
@ -33,16 +33,19 @@ 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.block.BlockTypes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
/**
* Re-orders blocks into several stages.
*/
public class MultiStageReorder extends AbstractDelegateExtent implements ReorderingExtent {
public class MultiStageReorder extends AbstractBufferingExtent implements ReorderingExtent {
private static final Map<BlockType, PlacementPriority> priorityMap = new HashMap<>();
@ -139,6 +142,7 @@ public class MultiStageReorder extends AbstractDelegateExtent implements Reorder
priorityMap.put(BlockTypes.MOVING_PISTON, PlacementPriority.FINAL);
}
private final Set<BlockVector3> containedBlocks = new HashSet<>();
private Map<PlacementPriority, LocatedBlockList> stages = new HashMap<>();
private boolean enabled;
@ -212,7 +216,7 @@ public class MultiStageReorder extends AbstractDelegateExtent implements Reorder
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 location, B block) throws WorldEditException {
if (!enabled) {
return super.setBlock(location, block);
return setDelegateBlock(location, block);
}
BlockState existing = getBlock(location);
@ -240,9 +244,21 @@ public class MultiStageReorder extends AbstractDelegateExtent implements Reorder
}
stages.get(priority).add(location, block);
containedBlocks.add(location);
return !existing.equalsFuzzy(block);
}
@Override
protected Optional<BaseBlock> getBufferedBlock(BlockVector3 position) {
if (!containedBlocks.contains(position)) {
return Optional.empty();
}
return stages.values().stream()
.map(blocks -> blocks.get(position))
.filter(Objects::nonNull)
.findAny();
}
@Override
public Operation commitBefore() {
if (!commitRequired()) {