mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-06-11 20:13:55 +00:00
Merge branch 'commanding-pipeline' of https://github.com/IntellectualSites/FastAsyncWorldEdit-1.13 into commanding-pipeline
Conflicts: worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java worldedit-core/src/main/java/com/boydti/fawe/beta/filters/DistrFilter.java worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkCache.java worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ParallelQueueExtent.java worldedit-core/src/main/java/com/boydti/fawe/command/CFICommands.java worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/MCAWriter.java worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
package com.boydti.fawe;
|
||||
|
||||
import com.boydti.fawe.beta.implementation.QueueHandler;
|
||||
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.object.brush.visualization.VisualQueue;
|
||||
@ -16,7 +16,6 @@ import com.boydti.fawe.util.TextureUtil;
|
||||
import com.boydti.fawe.util.WEManager;
|
||||
import com.github.luben.zstd.util.Native;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extension.factory.DefaultTransformParser;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.session.request.Request;
|
||||
@ -30,12 +29,9 @@ import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryMXBean;
|
||||
import java.lang.management.MemoryPoolMXBean;
|
||||
import java.lang.management.MemoryUsage;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.management.InstanceAlreadyExistsException;
|
||||
|
@ -7,7 +7,6 @@ import com.boydti.fawe.object.RegionWrapper;
|
||||
import com.boydti.fawe.object.changeset.DiskStorageHistory;
|
||||
import com.boydti.fawe.object.exception.FaweException;
|
||||
import com.boydti.fawe.object.extent.LightingExtent;
|
||||
import com.boydti.fawe.object.schematic.Schematic;
|
||||
import com.boydti.fawe.regions.FaweMaskManager;
|
||||
import com.boydti.fawe.util.EditSessionBuilder;
|
||||
import com.boydti.fawe.util.MainUtil;
|
||||
@ -192,7 +191,7 @@ public class FaweAPI {
|
||||
* @see ClipboardFormat
|
||||
* @see Schematic
|
||||
*/
|
||||
public static Schematic load(File file) throws IOException {
|
||||
public static Clipboard load(File file) throws IOException {
|
||||
return ClipboardFormats.findByFile(file).load(file);
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ import com.sk89q.worldedit.math.MutableBlockVector3;
|
||||
import com.sk89q.worldedit.math.MutableVector3;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
@ -38,10 +39,14 @@ import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@ -101,19 +106,25 @@ public enum FaweCache implements Trimable {
|
||||
|
||||
@Override
|
||||
public synchronized boolean trim(boolean aggressive) {
|
||||
BLOCK_TO_PALETTE.clean();
|
||||
PALETTE_TO_BLOCK.clean();
|
||||
BLOCK_STATES.clean();
|
||||
SECTION_BLOCKS.clean();
|
||||
PALETTE_CACHE.clean();
|
||||
PALETTE_TO_BLOCK_CHAR.clean();
|
||||
INDEX_STORE.clean();
|
||||
if (aggressive) {
|
||||
CleanableThreadLocal.cleanAll();
|
||||
} else {
|
||||
CHUNK_FLAG.clean();
|
||||
BYTE_BUFFER_8192.clean();
|
||||
BLOCK_TO_PALETTE.clean();
|
||||
PALETTE_TO_BLOCK.clean();
|
||||
BLOCK_STATES.clean();
|
||||
SECTION_BLOCKS.clean();
|
||||
PALETTE_CACHE.clean();
|
||||
PALETTE_TO_BLOCK_CHAR.clean();
|
||||
INDEX_STORE.clean();
|
||||
|
||||
MUTABLE_VECTOR3.clean();
|
||||
MUTABLE_BLOCKVECTOR3.clean();
|
||||
SECTION_BITS_TO_CHAR.clean();
|
||||
for (Map.Entry<Class, CleanableThreadLocal> entry : REGISTERED_SINGLETONS.entrySet()) {
|
||||
entry.getValue().clean();
|
||||
MUTABLE_VECTOR3.clean();
|
||||
MUTABLE_BLOCKVECTOR3.clean();
|
||||
SECTION_BITS_TO_CHAR.clean();
|
||||
for (Map.Entry<Class, CleanableThreadLocal> entry : REGISTERED_SINGLETONS.entrySet()) {
|
||||
entry.getValue().clean();
|
||||
}
|
||||
}
|
||||
for (Map.Entry<Class, Pool> entry : REGISTERED_POOLS.entrySet()) {
|
||||
Pool pool = entry.getValue();
|
||||
@ -197,13 +208,19 @@ public enum FaweCache implements Trimable {
|
||||
public static final FaweException LOW_MEMORY = new FaweException(BBC.WORLDEDIT_CANCEL_REASON_LOW_MEMORY);
|
||||
public static final FaweException MAX_ENTITIES = new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_ENTITIES);
|
||||
public static final FaweException MAX_TILES = new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_TILES);
|
||||
public static final FaweException MAX_ITERATIONS = new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_ITERATIONS);
|
||||
|
||||
/*
|
||||
thread cache
|
||||
*/
|
||||
public final CleanableThreadLocal<AtomicBoolean> CHUNK_FLAG = new CleanableThreadLocal<>(AtomicBoolean::new); // resets to false
|
||||
|
||||
public final CleanableThreadLocal<long[]> LONG_BUFFER_1024 = new CleanableThreadLocal<>(() -> new long[1024]);
|
||||
|
||||
public final CleanableThreadLocal<byte[]> BYTE_BUFFER_8192 = new CleanableThreadLocal<>(() -> new byte[8192]);
|
||||
|
||||
public final CleanableThreadLocal<int[]> BLOCK_TO_PALETTE = new CleanableThreadLocal<>(() -> {
|
||||
int[] result = new int[BlockTypes.states.length];
|
||||
int[] result = new int[BlockTypesCache.states.length];
|
||||
Arrays.fill(result, Integer.MAX_VALUE);
|
||||
return result;
|
||||
});
|
||||
@ -228,6 +245,8 @@ public enum FaweCache implements Trimable {
|
||||
* Holds data for a palette used in a chunk section
|
||||
*/
|
||||
public final class Palette {
|
||||
public int bitsPerEntry;
|
||||
|
||||
public int paletteToBlockLength;
|
||||
/**
|
||||
* Reusable buffer array, MUST check paletteToBlockLength for actual length
|
||||
@ -278,7 +297,7 @@ public enum FaweCache implements Trimable {
|
||||
int ordinal = blocksChars[i];
|
||||
int palette = blockToPalette[ordinal];
|
||||
if (palette == Integer.MAX_VALUE) {
|
||||
// BlockState state = BlockTypes.states[ordinal];
|
||||
// BlockState state = BlockTypesCache.states[ordinal];
|
||||
blockToPalette[ordinal] = palette = num_palette;
|
||||
paletteToBlock[num_palette] = ordinal;
|
||||
num_palette++;
|
||||
@ -290,7 +309,7 @@ public enum FaweCache implements Trimable {
|
||||
int ordinal = blocksInts[i];
|
||||
int palette = blockToPalette[ordinal];
|
||||
if (palette == Integer.MAX_VALUE) {
|
||||
BlockState state = BlockTypes.states[ordinal];
|
||||
BlockState state = BlockTypesCache.states[ordinal];
|
||||
blockToPalette[ordinal] = palette = num_palette;
|
||||
paletteToBlock[num_palette] = ordinal;
|
||||
num_palette++;
|
||||
@ -319,6 +338,7 @@ public enum FaweCache implements Trimable {
|
||||
|
||||
// Construct palette
|
||||
Palette palette = PALETTE_CACHE.get();
|
||||
palette.bitsPerEntry = bitsPerEntry;
|
||||
palette.paletteToBlockLength = num_palette;
|
||||
palette.paletteToBlock = paletteToBlock;
|
||||
|
||||
@ -499,6 +519,31 @@ public enum FaweCache implements Trimable {
|
||||
return new ThreadPoolExecutor(nThreads, nThreads,
|
||||
0L, TimeUnit.MILLISECONDS, queue
|
||||
, Executors.defaultThreadFactory(),
|
||||
new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
new ThreadPoolExecutor.CallerRunsPolicy()) {
|
||||
protected void afterExecute(Runnable r, Throwable t) {
|
||||
try {
|
||||
super.afterExecute(r, t);
|
||||
if (t == null && r instanceof Future<?>) {
|
||||
try {
|
||||
Future<?> future = (Future<?>) r;
|
||||
if (future.isDone()) {
|
||||
future.get();
|
||||
}
|
||||
} catch (CancellationException ce) {
|
||||
t = ce;
|
||||
} catch (ExecutionException ee) {
|
||||
t = ee.getCause();
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
if (t != null) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
package com.boydti.fawe;
|
||||
|
||||
import com.boydti.fawe.beta.implementation.QueueHandler;
|
||||
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
|
||||
import com.boydti.fawe.beta.implementation.cache.preloader.Preloader;
|
||||
import com.boydti.fawe.object.FaweCommand;
|
||||
import com.boydti.fawe.regions.FaweMaskManager;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.boydti.fawe.util.image.ImageViewer;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import java.io.File;
|
||||
@ -54,4 +54,6 @@ public interface IFawe {
|
||||
|
||||
QueueHandler getQueueHandler();
|
||||
|
||||
Preloader getPreloader();
|
||||
|
||||
}
|
||||
|
@ -1,61 +0,0 @@
|
||||
package com.boydti.fawe.beta;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class ChunkFuture implements Future<Void> {
|
||||
|
||||
private final IChunk chunk;
|
||||
private volatile boolean cancelled;
|
||||
private volatile boolean done;
|
||||
|
||||
public ChunkFuture(IChunk chunk) {
|
||||
this.chunk = chunk;
|
||||
}
|
||||
|
||||
public IChunk getChunk() {
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
cancelled = true;
|
||||
return !done;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return done;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void get() throws InterruptedException, ExecutionException {
|
||||
synchronized (chunk) {
|
||||
if (!done) {
|
||||
this.wait();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void get(long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
synchronized (chunk) {
|
||||
if (!done) {
|
||||
this.wait(unit.toMillis(timeout));
|
||||
if (!done) {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.boydti.fawe.beta;
|
||||
|
||||
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import javax.annotation.Nullable;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.boydti.fawe.beta;
|
||||
|
||||
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
|
||||
|
||||
public interface FilterBlockMask {
|
||||
|
||||
boolean applyBlock(FilterBlock block);
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.boydti.fawe.beta;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.beta.implementation.QueueHandler;
|
||||
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.sk89q.worldedit.util.Direction;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
|
@ -1,8 +1,8 @@
|
||||
package com.boydti.fawe.beta;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.beta.implementation.EmptyBatchProcessor;
|
||||
import com.boydti.fawe.beta.implementation.MultiBatchProcessor;
|
||||
import com.boydti.fawe.beta.implementation.processors.EmptyBatchProcessor;
|
||||
import com.boydti.fawe.beta.implementation.processors.MultiBatchProcessor;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
@ -46,7 +46,7 @@ public interface IBatchProcessor {
|
||||
if (set.hasSection(layer)) {
|
||||
if (layer == minLayer) {
|
||||
char[] arr = set.getArray(layer);
|
||||
int index = (minY & 15) << 12;
|
||||
int index = (minY & 15) << 8;
|
||||
for (int i = 0; i < index; i++) arr[i] = 0;
|
||||
set.setBlocks(layer, arr);
|
||||
} else {
|
||||
@ -59,7 +59,7 @@ public interface IBatchProcessor {
|
||||
if (set.hasSection(layer)) {
|
||||
if (layer == minLayer) {
|
||||
char[] arr = set.getArray(layer);
|
||||
int index = ((maxY + 1) & 15) << 12;
|
||||
int index = ((maxY + 1) & 15) << 8;
|
||||
for (int i = index; i < arr.length; i++) arr[i] = 0;
|
||||
set.setBlocks(layer, arr);
|
||||
} else {
|
||||
|
@ -1,10 +1,20 @@
|
||||
package com.boydti.fawe.beta;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.object.FaweOutputStream;
|
||||
import com.boydti.fawe.object.collection.BitArray4096;
|
||||
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.extension.platform.Capability;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockID;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.registry.BlockRegistry;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@ -19,5 +29,115 @@ public interface IBlocks extends Trimable {
|
||||
|
||||
BlockState getBlock(int x, int y, int z);
|
||||
|
||||
Map<BlockVector3, CompoundTag> getTiles();
|
||||
|
||||
Set<CompoundTag> getEntities();
|
||||
|
||||
BiomeType getBiomeType(int x, int z);
|
||||
|
||||
default int getBitMask() {
|
||||
int mask = 0;
|
||||
for (int layer = 0; layer < FaweCache.IMP.CHUNK_LAYERS; layer++) {
|
||||
if (hasSection(layer)) {
|
||||
mask += (1 << layer);
|
||||
}
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
IBlocks reset();
|
||||
|
||||
default byte[] toByteArray(boolean writeBiomes) {
|
||||
return toByteArray(null, writeBiomes);
|
||||
}
|
||||
|
||||
default byte[] toByteArray(byte[] buffer, boolean writeBiomes) {
|
||||
if (buffer == null) {
|
||||
buffer = new byte[1024];
|
||||
}
|
||||
|
||||
BlockRegistry registry = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry();
|
||||
FastByteArrayOutputStream sectionByteArray = new FastByteArrayOutputStream(buffer);
|
||||
FaweOutputStream sectionWriter = new FaweOutputStream(sectionByteArray);
|
||||
try {
|
||||
for (int layer = 0; layer < FaweCache.IMP.CHUNK_LAYERS; layer++) {
|
||||
if (!this.hasSection(layer)) continue;
|
||||
|
||||
char[] ids = this.getArray(layer);
|
||||
|
||||
int nonEmpty = 0; // TODO optimize into same loop as toPalette
|
||||
for (char id : ids) {
|
||||
if (id != 0) nonEmpty++;
|
||||
}
|
||||
|
||||
sectionWriter.writeShort(nonEmpty); // non empty
|
||||
|
||||
sectionWriter.writeByte(14); // globalPaletteBitsPerBlock
|
||||
|
||||
if (true) {
|
||||
BitArray4096 bits = new BitArray4096(14); // globalPaletteBitsPerBlock
|
||||
bits.setAt(0, 0);
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
int ordinal = ids[i];
|
||||
BlockState state = BlockState.getFromOrdinal(ordinal);
|
||||
if (!state.getMaterial().isAir()) {
|
||||
int mcId = registry.getInternalBlockStateId(state).getAsInt();
|
||||
bits.setAt(i, mcId);
|
||||
}
|
||||
}
|
||||
sectionWriter.write(bits.getData());
|
||||
} else {
|
||||
|
||||
FaweCache.Palette palette = FaweCache.IMP.toPalette(0, ids);
|
||||
|
||||
sectionWriter.writeByte(palette.bitsPerEntry); // bits per block
|
||||
sectionWriter.writeVarInt(palette.paletteToBlockLength);
|
||||
for (int i = 0; i < palette.paletteToBlockLength; i++) {
|
||||
int ordinal = palette.paletteToBlock[i];
|
||||
switch (ordinal) {
|
||||
case BlockID.CAVE_AIR:
|
||||
case BlockID.VOID_AIR:
|
||||
case BlockID.AIR:
|
||||
case BlockID.__RESERVED__:
|
||||
sectionWriter.writeVarInt(0);
|
||||
break;
|
||||
default:
|
||||
BlockState state = BlockState.getFromOrdinal(ordinal);
|
||||
int mcId = registry.getInternalBlockStateId(state).getAsInt();
|
||||
sectionWriter.writeVarInt(mcId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sectionWriter.writeVarInt(palette.blockStatesLength);
|
||||
for (int i = 0; i < palette.blockStatesLength; i++) {
|
||||
sectionWriter.writeLong(palette.blockStates[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if (writeBiomes) {
|
||||
// for (int x = 0; x < 16; x++) {
|
||||
// for (int z = 0; z < 16; z++) {
|
||||
// BiomeType biome = this.getBiomeType(x, z);
|
||||
// if (biome == null) {
|
||||
// if (writeBiomes) {
|
||||
// break;
|
||||
// } else {
|
||||
// biome = BiomeTypes.FOREST;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
if (writeBiomes) {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
// TODO biomes
|
||||
sectionWriter.writeInt(0);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return sectionByteArray.toByteArray();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.boydti.fawe.beta;
|
||||
|
||||
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
@ -27,12 +28,6 @@ public interface IChunk<T extends Future<T>> extends Trimable, Callable<T>, IChu
|
||||
*/
|
||||
void init(IQueueExtent extent, int x, int z);
|
||||
|
||||
/**
|
||||
* Get the queue
|
||||
* @return
|
||||
*/
|
||||
IQueueExtent getQueue();
|
||||
|
||||
/**
|
||||
* Get chunkX
|
||||
* @return
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta.implementation;
|
||||
package com.boydti.fawe.beta;
|
||||
|
||||
import com.boydti.fawe.beta.Trimable;
|
||||
|
@ -28,8 +28,10 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent {
|
||||
|
||||
CompoundTag getTag(int x, int y, int z);
|
||||
|
||||
@Override
|
||||
Map<BlockVector3, CompoundTag> getTiles();
|
||||
|
||||
@Override
|
||||
Set<CompoundTag> getEntities();
|
||||
|
||||
@Override
|
||||
|
@ -38,8 +38,13 @@ public interface IChunkSet extends IBlocks, OutputExtent {
|
||||
|
||||
BiomeType[] getBiomes();
|
||||
|
||||
@Override
|
||||
BiomeType getBiomeType(int x, int z);
|
||||
|
||||
@Override
|
||||
Map<BlockVector3, CompoundTag> getTiles();
|
||||
|
||||
@Override
|
||||
Set<CompoundTag> getEntities();
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.boydti.fawe.beta;
|
||||
|
||||
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
@ -38,11 +39,6 @@ public interface IDelegateChunk<U extends IChunk> extends IChunk {
|
||||
return getParent().call(set, finalize);
|
||||
}
|
||||
|
||||
@Override
|
||||
default IQueueExtent getQueue() {
|
||||
return getParent().getQueue();
|
||||
}
|
||||
|
||||
@Override
|
||||
default CompoundTag getTag(int x, int y, int z) {
|
||||
return getParent().getTag(x, y, z);
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.boydti.fawe.beta;
|
||||
|
||||
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
@ -1,13 +1,11 @@
|
||||
package com.boydti.fawe.beta;
|
||||
|
||||
import com.boydti.fawe.beta.implementation.IChunkCache;
|
||||
import com.boydti.fawe.beta.implementation.MultiBatchProcessor;
|
||||
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.entity.Entity;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
|
||||
import com.sk89q.worldedit.function.generator.GenBase;
|
||||
@ -45,16 +43,6 @@ public interface IDelegateQueueExtent extends IQueueExtent {
|
||||
return getParent().isQueueEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void clearBlockUpdates(Player... players) {
|
||||
getParent().clearBlockUpdates(players);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void sendBlockUpdates(Player... players) {
|
||||
getParent().sendBlockUpdates(players);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void enableQueue() {
|
||||
getParent().enableQueue();
|
||||
|
@ -1,15 +1,15 @@
|
||||
package com.boydti.fawe.beta;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.beta.implementation.IBatchProcessorHolder;
|
||||
import com.boydti.fawe.beta.implementation.IChunkCache;
|
||||
import com.boydti.fawe.beta.implementation.MultiBatchProcessor;
|
||||
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
|
||||
import com.boydti.fawe.beta.implementation.processors.IBatchProcessorHolder;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.function.operation.Operation;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
@ -17,7 +17,7 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.Flushable;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
@ -31,22 +31,6 @@ public interface IQueueExtent extends Flushable, Trimable, Extent, IBatchProcess
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear any block updates
|
||||
* @param players
|
||||
*/
|
||||
default void clearBlockUpdates(Player... players) {
|
||||
throw new UnsupportedOperationException("TODO NOT IMPLEMENTED");
|
||||
}
|
||||
|
||||
/**
|
||||
* Send all the chunks as block updates
|
||||
* @param players
|
||||
*/
|
||||
default void sendBlockUpdates(Player... players) {
|
||||
throw new UnsupportedOperationException("TODO NOT IMPLEMENTED");
|
||||
}
|
||||
|
||||
/**
|
||||
* Must ensure that it is enqueued with QueueHandler
|
||||
*/
|
||||
@ -204,4 +188,35 @@ public interface IQueueExtent extends Flushable, Trimable, Extent, IBatchProcess
|
||||
* @return <tt>true</tt> if this queue contains no elements
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
default ChunkFilterBlock apply(ChunkFilterBlock block, Filter filter, Region region, int chunkX, int chunkZ) {
|
||||
if (!filter.appliesChunk(chunkX, chunkZ)) {
|
||||
return block;
|
||||
}
|
||||
IChunk chunk = this.getOrCreateChunk(chunkX, chunkZ);
|
||||
// Initialize
|
||||
chunk.init(this, chunkX, chunkZ);
|
||||
|
||||
IChunk newChunk = filter.applyChunk(chunk, region);
|
||||
if (newChunk != null) {
|
||||
chunk = newChunk;
|
||||
if (block == null) {
|
||||
block = this.initFilterBlock();
|
||||
}
|
||||
chunk.filterBlocks(filter, block, region);
|
||||
}
|
||||
this.submit(chunk);
|
||||
return block;
|
||||
}
|
||||
|
||||
@Override
|
||||
default <T extends Filter> T apply(Region region, T filter) {
|
||||
final Set<BlockVector2> chunks = region.getChunks();
|
||||
ChunkFilterBlock block = null;
|
||||
for (BlockVector2 chunk : chunks) {
|
||||
block = apply(block, filter, region, chunk.getX(), chunk.getZ());
|
||||
}
|
||||
flush();
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta.implementation;
|
||||
package com.boydti.fawe.beta;
|
||||
|
||||
import com.boydti.fawe.beta.IQueueExtent;
|
||||
|
@ -1,105 +0,0 @@
|
||||
package com.boydti.fawe.beta;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
|
||||
public class NorthVector extends BlockVector3 {
|
||||
|
||||
private final BlockVector3 parent;
|
||||
|
||||
public NorthVector(BlockVector3 parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public BlockVector3 south(BlockVector3 orDefault) {
|
||||
// return parent;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public int getX() {
|
||||
return parent.getX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY() {
|
||||
return parent.getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getZ() {
|
||||
return parent.getZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setOrdinal(Extent orDefault, int ordinal) {
|
||||
return orDefault.setBlock(this, BlockState.getFromOrdinal(ordinal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBlock(Extent orDefault, BlockState state) {
|
||||
return orDefault.setBlock(this, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setFullBlock(Extent orDefault, BaseBlock block) {
|
||||
return orDefault.setBlock(this, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBiome(Extent orDefault, BiomeType biome) {
|
||||
return orDefault.setBiome(getX(), getY(), getZ(), biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrdinal(Extent orDefault) {
|
||||
return getBlock(orDefault).getOrdinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getOrdinalChar(Extent orDefault) {
|
||||
return (char) getOrdinal(orDefault);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(Extent orDefault) {
|
||||
return orDefault.getBlock(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(Extent orDefault) {
|
||||
return orDefault.getFullBlock(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getNbtData(Extent orDefault) {
|
||||
return orDefault.getFullBlock(getX(), getY(), getZ()).getNbtData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getOrdinalBelow(Extent orDefault) {
|
||||
return getStateRelative(orDefault, 0, -1, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateAbove(Extent orDefault) {
|
||||
return getStateRelative(orDefault, 0, 1, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateRelativeY(Extent orDefault, int y) {
|
||||
return getStateRelative(orDefault, 0, y, 0);
|
||||
}
|
||||
|
||||
public BlockState getStateRelative(Extent orDefault, int x, int y, int z) {
|
||||
return getFullBlockRelative(orDefault, x, y, z).toBlockState();
|
||||
}
|
||||
|
||||
public BaseBlock getFullBlockRelative(Extent orDefault, int x, int y, int z) {
|
||||
return orDefault.getFullBlock(x + getX(), y + getY(), z + getZ());
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package com.boydti.fawe.beta.implementation;
|
||||
|
||||
import com.boydti.fawe.beta.IBatchProcessor;
|
||||
|
||||
public class BatchProcessorHolder implements IBatchProcessorHolder {
|
||||
private IBatchProcessor processor = EmptyBatchProcessor.INSTANCE;
|
||||
|
||||
@Override
|
||||
public IBatchProcessor getProcessor() {
|
||||
return processor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProcessor(IBatchProcessor set) {
|
||||
this.processor = set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "{" + getProcessor() + "}";
|
||||
}
|
||||
}
|
@ -115,6 +115,11 @@ public class BitSetBlocks implements IChunkSet {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int z) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||
return null;
|
||||
|
@ -7,11 +7,12 @@ import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class CharBlocks implements IBlocks {
|
||||
public abstract class CharBlocks implements IBlocks {
|
||||
|
||||
public static final Section FULL = new Section() {
|
||||
@Override
|
||||
@ -96,7 +97,7 @@ public class CharBlocks implements IBlocks {
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
return BlockTypes.states[get(x, y, z)];
|
||||
return BlockTypesCache.states[get(x, y, z)];
|
||||
}
|
||||
|
||||
public char get(int x, int y, int z) {
|
||||
|
@ -5,17 +5,18 @@ import com.boydti.fawe.beta.IChunkSet;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
|
||||
public abstract class CharGetBlocks extends CharBlocks implements IChunkGet {
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
||||
return BlockTypes.states[get(x, y, z)].toBaseBlock();
|
||||
return BlockTypesCache.states[get(x, y, z)].toBaseBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
return BlockTypes.states[get(x, y, z)];
|
||||
return BlockTypesCache.states[get(x, y, z)];
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -13,6 +13,7 @@ import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@ -43,6 +44,11 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
|
||||
return biomes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int z) {
|
||||
return biomes[(z << 4) | x];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||
return tiles == null ? Collections.emptyMap() : tiles;
|
||||
@ -69,7 +75,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
return BlockTypes.states[get(x, y, z)];
|
||||
return BlockTypesCache.states[get(x, y, z)];
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta.implementation;
|
||||
package com.boydti.fawe.beta.implementation.blocks;
|
||||
|
||||
import com.boydti.fawe.beta.IChunkSet;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta.implementation;
|
||||
package com.boydti.fawe.beta.implementation.blocks;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.beta.IBlocks;
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta.implementation;
|
||||
package com.boydti.fawe.beta.implementation.blocks;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.beta.IBlocks;
|
@ -1,10 +1,12 @@
|
||||
package com.boydti.fawe.beta.implementation;
|
||||
package com.boydti.fawe.beta.implementation.cache;
|
||||
|
||||
import com.boydti.fawe.beta.IChunkCache;
|
||||
import com.boydti.fawe.beta.Trimable;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class ChunkCache<T extends Trimable> implements IChunkCache<T> {
|
||||
@ -12,11 +14,18 @@ public class ChunkCache<T extends Trimable> implements IChunkCache<T> {
|
||||
protected final Long2ObjectLinkedOpenHashMap<WeakReference<T>> getCache;
|
||||
private final IChunkCache<T> delegate;
|
||||
|
||||
protected ChunkCache(IChunkCache<T> delegate) {
|
||||
public ChunkCache(IChunkCache<T> delegate) {
|
||||
this.getCache = new Long2ObjectLinkedOpenHashMap<>();
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create the IGetBlocks
|
||||
*
|
||||
* @param index chunk index {@link com.boydti.fawe.util.MathMan#pairInt(int, int)}
|
||||
* @param provider used to create if it isn't already cached
|
||||
* @return cached IGetBlocks
|
||||
*/
|
||||
@Override
|
||||
public synchronized T get(int x, int z) {
|
||||
long pair = MathMan.pairInt(x, z);
|
110
worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/cache/preloader/AsyncPreloader.java
vendored
Normal file
110
worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/cache/preloader/AsyncPreloader.java
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
package com.boydti.fawe.beta.implementation.cache.preloader;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.object.collection.MutablePair;
|
||||
import com.boydti.fawe.util.FaweTimer;
|
||||
import com.sk89q.worldedit.IncompleteRegionException;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
public class AsyncPreloader implements Preloader, Runnable {
|
||||
private final ConcurrentHashMap<UUID, MutablePair<World, Set<BlockVector2>>> update;
|
||||
|
||||
public AsyncPreloader() {
|
||||
this.update = new ConcurrentHashMap<>();
|
||||
Fawe.get().getQueueHandler().async(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(Player player) {
|
||||
cancelAndGet(player);
|
||||
}
|
||||
|
||||
private MutablePair<World, Set<BlockVector2>> cancelAndGet(Player player) {
|
||||
MutablePair<World, Set<BlockVector2>> existing = update.get(player.getUniqueId());
|
||||
if (existing != null) {
|
||||
existing.setValue(null);
|
||||
}
|
||||
return existing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Player player) {
|
||||
LocalSession session = WorldEdit.getInstance().getSessionManager().getIfPresent(player);
|
||||
if (session == null) return;
|
||||
World world = player.getWorld();
|
||||
MutablePair<World, Set<BlockVector2>> existing = cancelAndGet(player);
|
||||
try {
|
||||
Region region = session.getSelection(world);
|
||||
if (!(region instanceof CuboidRegion) || region.getArea() > 50466816) {
|
||||
// TOO LARGE or NOT CUBOID
|
||||
return;
|
||||
}
|
||||
if (existing == null) {
|
||||
MutablePair<World, Set<BlockVector2>> previous = update.putIfAbsent(player.getUniqueId(), existing = new MutablePair<>());
|
||||
if (previous != null) {
|
||||
existing = previous;
|
||||
}
|
||||
synchronized (existing) { // Ensure key & value are mutated together
|
||||
existing.setKey(world);
|
||||
existing.setValue(region.getChunks());
|
||||
}
|
||||
synchronized (update) {
|
||||
update.notify();
|
||||
}
|
||||
}
|
||||
} catch (IncompleteRegionException ignore){}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
FaweTimer timer = Fawe.get().getTimer();
|
||||
try {
|
||||
while (true) {
|
||||
if (!update.isEmpty()) {
|
||||
if (timer.getTPS() > 19) {
|
||||
Iterator<Map.Entry<UUID, MutablePair<World, Set<BlockVector2>>>> plrIter = update.entrySet().iterator();
|
||||
Map.Entry<UUID, MutablePair<World, Set<BlockVector2>>> entry = plrIter.next();
|
||||
MutablePair<World, Set<BlockVector2>> pair = entry.getValue();
|
||||
World world = pair.getKey();
|
||||
Set<BlockVector2> chunks = pair.getValue();
|
||||
if (chunks != null) {
|
||||
Iterator<BlockVector2> chunksIter = chunks.iterator();
|
||||
while (chunksIter.hasNext() && pair.getValue() == chunks) { // Ensure the queued load is still valid
|
||||
BlockVector2 chunk = chunksIter.next();
|
||||
queueLoad(world, chunk);
|
||||
}
|
||||
}
|
||||
plrIter.remove();
|
||||
} else {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
} else {
|
||||
synchronized (update) {
|
||||
update.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void queueLoad(World world, BlockVector2 chunk) {
|
||||
world.checkLoadedChunk(BlockVector3.at(chunk.getX() << 4, 0, chunk.getZ() << 4));
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.boydti.fawe.beta.implementation.cache.preloader;
|
||||
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
|
||||
public interface Preloader {
|
||||
void cancel(Player player);
|
||||
|
||||
void update(Player player);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package com.boydti.fawe.beta.implementation.holder;
|
||||
package com.boydti.fawe.beta.implementation.chunk;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.beta.ChunkFilterBlock;
|
||||
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
|
||||
import com.boydti.fawe.beta.Filter;
|
||||
import com.boydti.fawe.beta.FilterBlockMask;
|
||||
import com.boydti.fawe.beta.Flood;
|
||||
@ -59,11 +59,6 @@ public class ChunkHolder<T extends Future<T>> implements IChunk {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IQueueExtent getQueue() {
|
||||
return extent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setTile(int x, int y, int z, CompoundTag tag) {
|
||||
return false;
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta.implementation.holder;
|
||||
package com.boydti.fawe.beta.implementation.chunk;
|
||||
|
||||
import com.boydti.fawe.beta.IChunk;
|
||||
import com.boydti.fawe.beta.IDelegateChunk;
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta.implementation.holder;
|
||||
package com.boydti.fawe.beta.implementation.chunk;
|
||||
|
||||
import com.boydti.fawe.beta.IChunk;
|
||||
import com.boydti.fawe.beta.IQueueExtent;
|
@ -0,0 +1,137 @@
|
||||
package com.boydti.fawe.beta.implementation.chunk;
|
||||
|
||||
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
|
||||
import com.boydti.fawe.beta.Filter;
|
||||
import com.boydti.fawe.beta.FilterBlockMask;
|
||||
import com.boydti.fawe.beta.Flood;
|
||||
import com.boydti.fawe.beta.IChunk;
|
||||
import com.boydti.fawe.beta.IChunkSet;
|
||||
import com.boydti.fawe.beta.IQueueExtent;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
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 javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public enum NullChunk implements IChunk {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public void init(IQueueExtent extent, int x, int z) {}
|
||||
|
||||
@Override
|
||||
public int getX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getZ() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future call() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filterBlocks(Filter filter, ChunkFilterBlock block, @Nullable Region region) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flood(Flood flood, FilterBlockMask mask, ChunkFilterBlock block) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setTile(int x, int y, int z, CompoundTag tag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBlock(int x, int y, int z, BlockStateHolder block) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int z) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSection(int layer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getArray(int layer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
return BlockTypes.__RESERVED__.getDefaultState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
||||
return BlockTypes.__RESERVED__.getDefaultState().toBaseBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getTag(int x, int y, int z) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CompoundTag> getEntities() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] load(int layer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getEntity(UUID uuid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future call(IChunkSet set, Runnable finalize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta.implementation.holder;
|
||||
package com.boydti.fawe.beta.implementation.chunk;
|
||||
|
||||
import com.boydti.fawe.beta.IChunk;
|
||||
import com.boydti.fawe.beta.IDelegateChunk;
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta.implementation.holder;
|
||||
package com.boydti.fawe.beta.implementation.chunk;
|
||||
|
||||
import com.boydti.fawe.beta.IChunk;
|
||||
import com.boydti.fawe.beta.IQueueExtent;
|
@ -1,16 +1,10 @@
|
||||
package com.boydti.fawe.beta.implementation.holder;
|
||||
package com.boydti.fawe.beta.implementation.chunk;
|
||||
|
||||
import com.boydti.fawe.beta.IChunk;
|
||||
import com.boydti.fawe.beta.IQueueExtent;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A {@link ReferenceChunk} using {@link WeakReference} to hold the chunk.
|
@ -1,6 +1,6 @@
|
||||
package com.boydti.fawe.beta.filters;
|
||||
package com.boydti.fawe.beta.implementation.filter;
|
||||
|
||||
import com.boydti.fawe.beta.FilterBlock;
|
||||
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
|
||||
import com.boydti.fawe.beta.FilterBlockMask;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
@ -1,6 +1,6 @@
|
||||
package com.boydti.fawe.beta.filters;
|
||||
package com.boydti.fawe.beta.implementation.filter;
|
||||
|
||||
import com.boydti.fawe.beta.FilterBlock;
|
||||
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
|
||||
|
||||
public class CountFilter extends ForkedFilter<CountFilter> {
|
||||
|
@ -1,12 +1,14 @@
|
||||
package com.boydti.fawe.beta.filters;
|
||||
package com.boydti.fawe.beta.implementation.filter;
|
||||
|
||||
import com.boydti.fawe.beta.FilterBlock;
|
||||
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.function.mask.ABlockMask;
|
||||
import com.sk89q.worldedit.util.Countable;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -14,7 +16,7 @@ import java.util.List;
|
||||
|
||||
public class DistrFilter extends ForkedFilter<DistrFilter> {
|
||||
|
||||
private final int[] counter = new int[BlockTypes.states.length];
|
||||
private final int[] counter = new int[BlockTypesCache.states.length];
|
||||
|
||||
public DistrFilter() {
|
||||
super(null);
|
||||
@ -45,7 +47,7 @@ public class DistrFilter extends ForkedFilter<DistrFilter> {
|
||||
int total = 0;
|
||||
for (int i = 0; i < counter.length; i++) {
|
||||
int value = counter[i];
|
||||
if (value != 0 && mask.test(BlockTypes.states[i])) {
|
||||
if (value != 0 && mask.test(BlockTypesCache.states[i])) {
|
||||
total += value;
|
||||
}
|
||||
}
|
||||
@ -61,7 +63,7 @@ public class DistrFilter extends ForkedFilter<DistrFilter> {
|
||||
for (int i = 0; i < counter.length; i++) {
|
||||
final int count = counter[i];
|
||||
if (count != 0) {
|
||||
distribution.add(new Countable<>(BlockTypes.states[i], count));
|
||||
distribution.add(new Countable<>(BlockTypesCache.states[i], count));
|
||||
}
|
||||
}
|
||||
Collections.sort(distribution);
|
||||
@ -70,18 +72,18 @@ public class DistrFilter extends ForkedFilter<DistrFilter> {
|
||||
|
||||
public List<Countable<BlockType>> getTypeDistribution() {
|
||||
final List<Countable<BlockType>> distribution = new ArrayList<>();
|
||||
int[] typeCounter = new int[BlockTypes.values.length];
|
||||
int[] typeCounter = new int[BlockTypesCache.values.length];
|
||||
for (int i = 0; i < counter.length; i++) {
|
||||
final int count = counter[i];
|
||||
if (count != 0) {
|
||||
BlockState state = BlockTypes.states[i];
|
||||
BlockState state = BlockTypesCache.states[i];
|
||||
typeCounter[state.getBlockType().getInternalId()] += count;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < typeCounter.length; i++) {
|
||||
final int count = typeCounter[i];
|
||||
if (count != 0) {
|
||||
distribution.add(new Countable<>(BlockTypes.values[i], count));
|
||||
distribution.add(new Countable<>(BlockTypesCache.values[i], count));
|
||||
}
|
||||
}
|
||||
Collections.sort(distribution);
|
||||
@ -89,10 +91,10 @@ public class DistrFilter extends ForkedFilter<DistrFilter> {
|
||||
}
|
||||
|
||||
public void print(Actor actor, long size) {
|
||||
for (Countable<BlockState> c : getDistribution()) {
|
||||
for (Countable c : getDistribution()) {
|
||||
final String name = c.getID().toString();
|
||||
final String str = String.format("%-7s (%.3f%%) %s",
|
||||
c.getAmount(),
|
||||
String.valueOf(c.getAmount()),
|
||||
c.getAmount() / (double) size * 100,
|
||||
name);
|
||||
actor.print(str);
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta.filters;
|
||||
package com.boydti.fawe.beta.implementation.filter;
|
||||
|
||||
import com.boydti.fawe.beta.Filter;
|
||||
import java.util.Map;
|
@ -1,7 +1,7 @@
|
||||
package com.boydti.fawe.beta.filters;
|
||||
package com.boydti.fawe.beta.implementation.filter;
|
||||
|
||||
import com.boydti.fawe.beta.Filter;
|
||||
import com.boydti.fawe.beta.FilterBlock;
|
||||
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
|
||||
public class SetFilter implements Filter {
|
@ -0,0 +1,94 @@
|
||||
package com.boydti.fawe.beta.implementation.filter.block;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
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;
|
||||
|
||||
public abstract class AbstractFilterBlock extends FilterBlock {
|
||||
|
||||
public abstract BaseBlock getFullBlock();
|
||||
public abstract void setFullBlock(BaseBlock block);
|
||||
public abstract BlockVector3 getPosition();
|
||||
|
||||
@Override
|
||||
public Extent getExtent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getX() {
|
||||
return getPosition().getX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY() {
|
||||
return getPosition().getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getZ() {
|
||||
return getPosition().getZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrdinal() {
|
||||
return getBlock().getOrdinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrdinal(int ordinal) {
|
||||
setBlock(BlockState.getFromOrdinal(ordinal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock() {
|
||||
return getFullBlock().toBlockState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(BlockState state) {
|
||||
setFullBlock(state.toBaseBlock(getBlock().getNbtData()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getNbtData() {
|
||||
return getFullBlock().getNbtData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNbtData(@Nullable CompoundTag nbtData) {
|
||||
setFullBlock(getFullBlock().toBaseBlock(nbtData));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getMinimumPoint() {
|
||||
return BlockVector3.at(getX(), getY(), getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getMaximumPoint() {
|
||||
return BlockVector3.at(getX(), getY(), getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T block)
|
||||
throws WorldEditException {
|
||||
if (x == this.getX() && y == this.getY() && z == this.getZ()) {
|
||||
setFullBlock(block.toBaseBlock());
|
||||
return true;
|
||||
}
|
||||
return getExtent().setBlock(x,y, z, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
||||
return getExtent().setBiome(x, y, z,biome);
|
||||
}
|
||||
}
|
@ -1,25 +1,21 @@
|
||||
package com.boydti.fawe.beta;
|
||||
package com.boydti.fawe.beta.implementation.filter.block;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
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;
|
||||
|
||||
public class SingleFilterBlock extends FilterBlock {
|
||||
public abstract class AbstractSingleFilterBlock extends FilterBlock {
|
||||
|
||||
private BaseBlock block;
|
||||
private int x, y, z;
|
||||
|
||||
public SingleFilterBlock init(int x, int y, int z, BaseBlock block) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
public AbstractSingleFilterBlock init(BaseBlock block) {
|
||||
this.block = block;
|
||||
return this;
|
||||
}
|
||||
@ -69,35 +65,20 @@ public class SingleFilterBlock extends FilterBlock {
|
||||
block = block.toBaseBlock(nbtData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getMinimumPoint() {
|
||||
return BlockVector3.at(x, y, z);
|
||||
return BlockVector3.at(getX(), getY(), getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getMaximumPoint() {
|
||||
return BlockVector3.at(x, y, z);
|
||||
return BlockVector3.at(getX(), getY(), getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T block)
|
||||
throws WorldEditException {
|
||||
if (x == this.x && y == this.y && z == this.z) {
|
||||
if (x == this.getX() && y == this.getY() && z == this.getZ()) {
|
||||
setFullBlock(block.toBaseBlock());
|
||||
return true;
|
||||
}
|
||||
@ -106,6 +87,6 @@ public class SingleFilterBlock extends FilterBlock {
|
||||
|
||||
@Override
|
||||
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
||||
return getExtent().setBiome(x,y, z,biome);
|
||||
return getExtent().setBiome(x, y, z,biome);
|
||||
}
|
||||
}
|
@ -1,15 +1,16 @@
|
||||
package com.boydti.fawe.beta;
|
||||
package com.boydti.fawe.beta.implementation.filter.block;
|
||||
|
||||
import com.boydti.fawe.beta.Filter;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
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 com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ArrayFilterBlock extends SimpleFilterBlock {
|
||||
@ -52,7 +53,7 @@ public class ArrayFilterBlock extends SimpleFilterBlock {
|
||||
|
||||
@Override
|
||||
public BlockState getBlock() {
|
||||
return BlockTypes.states[ordinal];
|
||||
return BlockTypesCache.states[ordinal];
|
||||
}
|
||||
|
||||
@Override
|
@ -1,7 +1,13 @@
|
||||
package com.boydti.fawe.beta;
|
||||
package com.boydti.fawe.beta.implementation.filter.block;
|
||||
|
||||
import static com.sk89q.worldedit.world.block.BlockTypes.states;
|
||||
import static com.sk89q.worldedit.world.block.BlockTypesCache.states;
|
||||
|
||||
import com.boydti.fawe.beta.Filter;
|
||||
import com.boydti.fawe.beta.FilterBlockMask;
|
||||
import com.boydti.fawe.beta.Flood;
|
||||
import com.boydti.fawe.beta.IChunkGet;
|
||||
import com.boydti.fawe.beta.IChunkSet;
|
||||
import com.boydti.fawe.beta.IQueueExtent;
|
||||
import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
@ -13,6 +19,7 @@ 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 com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@ -225,7 +232,7 @@ public class CharFilterBlock extends ChunkFilterBlock {
|
||||
@Override
|
||||
public final BlockState getBlock() {
|
||||
final int ordinal = getArr[index];
|
||||
return BlockTypes.states[ordinal];
|
||||
return BlockTypesCache.states[ordinal];
|
||||
}
|
||||
|
||||
@Override
|
@ -1,5 +1,10 @@
|
||||
package com.boydti.fawe.beta;
|
||||
package com.boydti.fawe.beta.implementation.filter.block;
|
||||
|
||||
import com.boydti.fawe.beta.Filter;
|
||||
import com.boydti.fawe.beta.FilterBlockMask;
|
||||
import com.boydti.fawe.beta.Flood;
|
||||
import com.boydti.fawe.beta.IChunkGet;
|
||||
import com.boydti.fawe.beta.IChunkSet;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
|
||||
@ -15,7 +20,7 @@ public abstract class ChunkFilterBlock extends SimpleFilterBlock {
|
||||
int layer);
|
||||
|
||||
public abstract void flood(IChunkGet iget, IChunkSet iset, int layer,
|
||||
Flood flood, FilterBlockMask mask);
|
||||
Flood flood, FilterBlockMask mask);
|
||||
|
||||
|
||||
public abstract void filter(Filter filter, int x, int y, int z);
|
@ -1,4 +1,7 @@
|
||||
package com.boydti.fawe.beta;
|
||||
package com.boydti.fawe.beta.implementation.filter.block;
|
||||
|
||||
import com.boydti.fawe.beta.Filter;
|
||||
import com.boydti.fawe.beta.IDelegateFilter;
|
||||
|
||||
public abstract class DelegateFilter<T extends Filter> implements IDelegateFilter {
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta;
|
||||
package com.boydti.fawe.beta.implementation.filter.block;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
@ -0,0 +1,35 @@
|
||||
package com.boydti.fawe.beta.implementation.filter.block;
|
||||
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
|
||||
public class ExtentFilterBlock extends AbstractFilterBlock {
|
||||
private final Extent extent;
|
||||
private BlockVector3 pos;
|
||||
|
||||
public ExtentFilterBlock(Extent extent) {
|
||||
this.extent = extent;
|
||||
this.pos = BlockVector3.ZERO;
|
||||
}
|
||||
|
||||
public ExtentFilterBlock init(BlockVector3 pos) {
|
||||
this.pos = pos;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock() {
|
||||
return pos.getFullBlock(extent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFullBlock(BaseBlock block) {
|
||||
pos.setFullBlock(extent, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getPosition() {
|
||||
return pos;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta;
|
||||
package com.boydti.fawe.beta.implementation.filter.block;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta;
|
||||
package com.boydti.fawe.beta.implementation.filter.block;
|
||||
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
|
@ -0,0 +1,31 @@
|
||||
package com.boydti.fawe.beta.implementation.filter.block;
|
||||
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
|
||||
public class SingleFilterBlock extends AbstractSingleFilterBlock {
|
||||
|
||||
private int x, y, z;
|
||||
|
||||
public SingleFilterBlock init(int x, int y, int z, BaseBlock block) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
super.init(block);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getZ() {
|
||||
return z;
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.boydti.fawe.beta.implementation.filter.block;
|
||||
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
|
||||
public class VectorSingleFilterBlock extends AbstractSingleFilterBlock {
|
||||
private final BlockVector3 mutable;
|
||||
|
||||
public VectorSingleFilterBlock(BlockVector3 mutable) {
|
||||
this.mutable = mutable;
|
||||
}
|
||||
|
||||
public VectorSingleFilterBlock init(BaseBlock block) {
|
||||
super.init(block);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getX() {
|
||||
return mutable.getX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY() {
|
||||
return mutable.getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getZ() {
|
||||
return mutable.getZ();
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
package com.boydti.fawe.beta.implementation.packet;
|
||||
|
||||
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 java.util.HashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ChunkPacket implements Function<byte[], byte[]>, Supplier<byte[]> {
|
||||
|
||||
private final boolean full;
|
||||
private final Supplier<IBlocks> chunkSupplier;
|
||||
private IBlocks chunk;
|
||||
|
||||
private int chunkX;
|
||||
private int chunkZ;
|
||||
private byte[] sectionBytes;
|
||||
private Object nativePacket;
|
||||
|
||||
public ChunkPacket(int chunkX, int chunkZ, Supplier<IBlocks> chunkSupplier, boolean replaceAllSections) {
|
||||
this.chunkX = chunkX;
|
||||
this.chunkZ = chunkZ;
|
||||
this.chunkSupplier = chunkSupplier;
|
||||
this.full = replaceAllSections;
|
||||
}
|
||||
|
||||
public int getChunkX() {
|
||||
return chunkX;
|
||||
}
|
||||
|
||||
public int getChunkZ() {
|
||||
return chunkZ;
|
||||
}
|
||||
|
||||
public synchronized void setPosition(int chunkX, int chunkZ) {
|
||||
this.chunkX = chunkX;
|
||||
this.chunkZ = chunkZ;
|
||||
nativePacket = null;
|
||||
}
|
||||
|
||||
public boolean isFull() {
|
||||
return full;
|
||||
}
|
||||
|
||||
public IBlocks getChunk() {
|
||||
if (this.chunk == null) {
|
||||
synchronized (this) {
|
||||
if (this.chunk == null) {
|
||||
this.chunk = chunkSupplier.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
private byte[] getSectionBytes() {
|
||||
byte[] tmp = this.sectionBytes;
|
||||
if (tmp == null) {
|
||||
synchronized (this) {
|
||||
if (sectionBytes == null) {
|
||||
sectionBytes = getChunk().toByteArray(FaweCache.IMP.BYTE_BUFFER_8192.get(), this.full);
|
||||
}
|
||||
tmp = sectionBytes;
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public Object getNativePacket() {
|
||||
return nativePacket;
|
||||
}
|
||||
|
||||
public void setNativePacket(Object nativePacket) {
|
||||
this.nativePacket = nativePacket;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public byte[] get() {
|
||||
return apply(FaweCache.IMP.BYTE_BUFFER_8192.get());
|
||||
}
|
||||
|
||||
public CompoundTag getHeightMap() {
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
map.put("MOTION_BLOCKING", new long[36]);
|
||||
CompoundTag tag = FaweCache.IMP.asTag(map);
|
||||
// TODO
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] apply(byte[] buffer) {
|
||||
try {
|
||||
byte[] sectionBytes = getSectionBytes();
|
||||
|
||||
FastByteArrayOutputStream baos = new FastByteArrayOutputStream(buffer);
|
||||
FaweOutputStream fos = new FaweOutputStream(baos);
|
||||
|
||||
fos.writeInt(this.chunkX);
|
||||
fos.writeInt(this.chunkZ);
|
||||
|
||||
fos.writeBoolean(this.full);
|
||||
|
||||
fos.writeVarInt(getChunk().getBitMask());
|
||||
|
||||
|
||||
fos.writeNBT("", getHeightMap());
|
||||
|
||||
fos.writeVarInt(sectionBytes.length);
|
||||
fos.write(sectionBytes);
|
||||
// TODO entities / NBT
|
||||
// Set<CompoundTag> entities = chunk.getEntities();
|
||||
// Map<BlockVector3, CompoundTag> tiles = chunk.getTiles();
|
||||
fos.writeVarInt(0); // (Entities / NBT)
|
||||
return baos.toByteArray();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.boydti.fawe.beta.implementation.processors;
|
||||
|
||||
import com.boydti.fawe.beta.IBatchProcessor;
|
||||
import com.boydti.fawe.beta.IChunk;
|
||||
import com.boydti.fawe.beta.IChunkGet;
|
||||
import com.boydti.fawe.beta.IChunkSet;
|
||||
|
||||
public class BatchProcessorHolder implements IBatchProcessorHolder {
|
||||
private IBatchProcessor processor = EmptyBatchProcessor.INSTANCE;
|
||||
|
||||
@Override
|
||||
public IBatchProcessor getProcessor() {
|
||||
return processor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
return getProcessor().processSet(chunk, get, set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProcessor(IBatchProcessor set) {
|
||||
this.processor = set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
IBatchProcessor tmp = getProcessor();
|
||||
return super.toString() + "{" + (tmp == this ? "" : getProcessor()) + "}";
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.boydti.fawe.beta.implementation.processors;
|
||||
|
||||
import com.boydti.fawe.beta.IBatchProcessor;
|
||||
import com.boydti.fawe.beta.IChunk;
|
||||
import com.boydti.fawe.beta.IChunkGet;
|
||||
import com.boydti.fawe.beta.IChunkSet;
|
||||
import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ChunkSendProcessor implements IBatchProcessor {
|
||||
private final Supplier<Stream<Player>> players;
|
||||
private final World world;
|
||||
|
||||
public ChunkSendProcessor(World world, Supplier<Stream<Player>> players) {
|
||||
this.players = players;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
int chunkX = chunk.getX();
|
||||
int chunkZ = chunk.getZ();
|
||||
boolean replaceAll = true;
|
||||
ChunkPacket packet = new ChunkPacket(chunkX, chunkZ, () -> set, replaceAll);
|
||||
Stream<Player> stream = this.players.get();
|
||||
if (stream == null) {
|
||||
world.sendFakeChunk(null, packet);
|
||||
} else {
|
||||
stream.filter(player -> player.getWorld().equals(world))
|
||||
.forEach(player -> world.sendFakeChunk(player, packet));
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Extent construct(Extent child) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta.implementation;
|
||||
package com.boydti.fawe.beta.implementation.processors;
|
||||
|
||||
import com.boydti.fawe.beta.IBatchProcessor;
|
||||
import com.boydti.fawe.beta.IChunk;
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta.implementation;
|
||||
package com.boydti.fawe.beta.implementation.processors;
|
||||
|
||||
import com.boydti.fawe.beta.IBatchProcessor;
|
||||
import com.boydti.fawe.beta.IChunk;
|
@ -0,0 +1,634 @@
|
||||
package com.boydti.fawe.beta.implementation.processors;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.beta.Filter;
|
||||
import com.boydti.fawe.beta.IBatchProcessor;
|
||||
import com.boydti.fawe.beta.implementation.filter.block.ExtentFilterBlock;
|
||||
import com.boydti.fawe.object.FaweLimit;
|
||||
import com.boydti.fawe.object.changeset.FaweChangeSet;
|
||||
import com.boydti.fawe.object.exception.FaweException;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.entity.Entity;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.extent.PassthroughExtent;
|
||||
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
|
||||
import com.sk89q.worldedit.function.generator.GenBase;
|
||||
import com.sk89q.worldedit.function.generator.Resource;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.operation.Operation;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.session.ClipboardHolder;
|
||||
import com.sk89q.worldedit.util.Countable;
|
||||
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.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class LimitExtent extends PassthroughExtent {
|
||||
private final FaweLimit limit;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param extent the extent
|
||||
*/
|
||||
public LimitExtent(Extent extent, FaweLimit limit) {
|
||||
super(extent);
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
public List<? extends Entity> getEntities(Region region) {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
try {
|
||||
return getExtent().getEntities(region);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public List<? extends Entity> getEntities() {
|
||||
limit.THROW_MAX_CHECKS();
|
||||
try {
|
||||
return getExtent().getEntities();
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Entity createEntity(Location location, BaseEntity entity) {
|
||||
limit.THROW_MAX_CHANGES();
|
||||
limit.THROW_MAX_ENTITIES();
|
||||
try {
|
||||
return getExtent().createEntity(location, entity);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public void removeEntity(int x, int y, int z, UUID uuid) {
|
||||
limit.THROW_MAX_CHANGES();
|
||||
limit.THROW_MAX_ENTITIES();
|
||||
try {
|
||||
getExtent().removeEntity(x, y, z, uuid);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean regenerateChunk(int x, int z, @Nullable BiomeType type, @Nullable Long seed) {
|
||||
limit.THROW_MAX_CHANGES(Character.MAX_VALUE);
|
||||
try {
|
||||
return getExtent().regenerateChunk(x, z, type, seed);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHighestTerrainBlock(int x, int z, int minY, int maxY) {
|
||||
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
|
||||
try {
|
||||
return getExtent().getHighestTerrainBlock(x, z, minY, maxY);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHighestTerrainBlock(int x, int z, int minY, int maxY, Mask filter) {
|
||||
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
|
||||
try {
|
||||
return getExtent().getHighestTerrainBlock(x, z, minY, maxY, filter);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) {
|
||||
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
|
||||
try {
|
||||
return getExtent().getNearestSurfaceLayer(x, z, y, minY, maxY);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, boolean ignoreAir) {
|
||||
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
|
||||
try {
|
||||
return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, ignoreAir);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) {
|
||||
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
|
||||
try {
|
||||
return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax) {
|
||||
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
|
||||
try {
|
||||
return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, Mask mask) {
|
||||
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
|
||||
try {
|
||||
return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, mask);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, boolean ignoreAir) {
|
||||
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
|
||||
try {
|
||||
return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, ignoreAir);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCaves(Region region) throws WorldEditException {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
limit.THROW_MAX_CHANGES(region.getArea());
|
||||
try {
|
||||
getExtent().addCaves(region);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(Region region, GenBase gen) throws WorldEditException {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
limit.THROW_MAX_CHANGES(region.getArea());
|
||||
try {
|
||||
getExtent().generate(region, gen);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSchems(Region region, Mask mask, List<ClipboardHolder> clipboards, int rarity, boolean rotate) throws WorldEditException {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
limit.THROW_MAX_CHANGES(region.getArea());
|
||||
try {
|
||||
getExtent().addSchems(region, mask, clipboards, rarity, rotate);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnResource(Region region, Resource gen, int rarity, int frequency) throws WorldEditException {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
limit.THROW_MAX_CHANGES(region.getArea());
|
||||
try {
|
||||
getExtent().spawnResource(region, gen, rarity, frequency);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOre(Region region, Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws WorldEditException {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
limit.THROW_MAX_CHANGES(region.getArea());
|
||||
try {
|
||||
getExtent().addOre(region, mask, material, size, frequency, rarity, minY, maxY);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOres(Region region, Mask mask) throws WorldEditException {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
limit.THROW_MAX_CHANGES(region.getArea());
|
||||
try {
|
||||
getExtent().addOres(region, mask);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Countable<BlockType>> getBlockDistribution(Region region) {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
try {
|
||||
return getExtent().getBlockDistribution(region);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Countable<BlockState>> getBlockDistributionWithData(Region region) {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
try {
|
||||
return getExtent().getBlockDistributionWithData(region);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countBlocks(Region region, Set<BaseBlock> searchBlocks) {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
try {
|
||||
return getExtent().countBlocks(region, searchBlocks);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countBlocks(Region region, Mask searchMask) {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
try {
|
||||
return getExtent().countBlocks(region, searchMask);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> int setBlocks(Region region, B block) throws MaxChangedBlocksException {
|
||||
limit.THROW_MAX_CHANGES(region.getArea());
|
||||
try {
|
||||
return getExtent().setBlocks(region, block);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException {
|
||||
limit.THROW_MAX_CHANGES(region.getArea());
|
||||
try {
|
||||
return getExtent().setBlocks(region, pattern);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> int replaceBlocks(Region region, Set<BaseBlock> filter, B replacement) throws MaxChangedBlocksException {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
limit.THROW_MAX_CHANGES(region.getArea());
|
||||
try {
|
||||
return getExtent().replaceBlocks(region, filter, replacement);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int replaceBlocks(Region region, Set<BaseBlock> filter, Pattern pattern) throws MaxChangedBlocksException {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
limit.THROW_MAX_CHANGES(region.getArea());
|
||||
try {
|
||||
return getExtent().replaceBlocks(region, filter, pattern);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int replaceBlocks(Region region, Mask mask, Pattern pattern) throws MaxChangedBlocksException {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
limit.THROW_MAX_CHANGES(region.getArea());
|
||||
try {
|
||||
return getExtent().replaceBlocks(region, mask, pattern);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int center(Region region, Pattern pattern) throws MaxChangedBlocksException {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
limit.THROW_MAX_CHANGES(region.getArea());
|
||||
try {
|
||||
return getExtent().center(region, pattern);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setBlocks(Set<BlockVector3> vset, Pattern pattern) {
|
||||
limit.THROW_MAX_CHANGES(vset.size());
|
||||
try {
|
||||
return getExtent().setBlocks(vset, pattern);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Filter> T apply(Region region, T filter) {
|
||||
limit.THROW_MAX_CHECKS(region.getArea());
|
||||
limit.THROW_MAX_CHANGES(region.getArea());
|
||||
try {
|
||||
return getExtent().apply(region, filter);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Filter> T apply(Iterable<BlockVector3> positions, T filter) {
|
||||
int size;
|
||||
if (positions instanceof Collection) {
|
||||
size = ((Collection<BlockVector3>) positions).size();
|
||||
} else if (positions instanceof Region) {
|
||||
BlockVector3 dim = ((Region) positions).getDimensions();
|
||||
size = dim.getX() * dim.getY() * dim.getZ();
|
||||
} else if (positions instanceof Extent) {
|
||||
BlockVector3 min = ((Extent) positions).getMinimumPoint();
|
||||
BlockVector3 max = ((Extent) positions).getMinimumPoint();
|
||||
BlockVector3 dim = max.subtract(min).add(BlockVector3.ONE);
|
||||
size = dim.getX() * dim.getY() * dim.getZ();
|
||||
} else {
|
||||
ExtentFilterBlock block = new ExtentFilterBlock(this);
|
||||
for (BlockVector3 pos : positions) {
|
||||
limit.THROW_MAX_CHECKS();
|
||||
filter.applyBlock(block.init(pos));
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
limit.THROW_MAX_CHECKS(size);
|
||||
limit.THROW_MAX_CHANGES(size);
|
||||
try {
|
||||
return getExtent().apply(positions, filter);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
||||
public BlockState getBlock(BlockVector3 position) {
|
||||
limit.THROW_MAX_CHECKS();
|
||||
try {
|
||||
return getExtent().getBlock(position);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return BlockTypes.AIR.getDefaultState();
|
||||
}
|
||||
}
|
||||
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
limit.THROW_MAX_CHECKS();
|
||||
try {
|
||||
return getExtent().getBlock(x, y, z);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return BlockTypes.AIR.getDefaultState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(BlockVector3 position) {
|
||||
limit.THROW_MAX_CHECKS();
|
||||
try {
|
||||
return getExtent().getFullBlock(position);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return BlockTypes.AIR.getDefaultState().toBaseBlock();
|
||||
}
|
||||
}
|
||||
|
||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
||||
limit.THROW_MAX_CHECKS();
|
||||
try {
|
||||
return getExtent().getFullBlock(x, y, z);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return BlockTypes.AIR.getDefaultState().toBaseBlock();
|
||||
}
|
||||
}
|
||||
|
||||
public BiomeType getBiome(BlockVector2 position) {
|
||||
limit.THROW_MAX_CHECKS();
|
||||
try {
|
||||
return getExtent().getBiome(position);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return BiomeTypes.FOREST;
|
||||
}
|
||||
}
|
||||
|
||||
public BiomeType getBiomeType(int x, int z) {
|
||||
limit.THROW_MAX_CHECKS();
|
||||
try {
|
||||
return getExtent().getBiomeType(x, z);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return BiomeTypes.FOREST;
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public <T extends BlockStateHolder<T>> boolean setBlock(BlockVector3 position, T block) throws WorldEditException {
|
||||
limit.THROW_MAX_CHANGES();
|
||||
if (block.hasNbtData()) limit.MAX_BLOCKSTATES();
|
||||
try {
|
||||
return getExtent().setBlock(position, block);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T block) throws WorldEditException {
|
||||
limit.THROW_MAX_CHANGES();
|
||||
if (block.hasNbtData()) limit.MAX_BLOCKSTATES();
|
||||
try {
|
||||
return getExtent().setBlock(x, y, z, block);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException {
|
||||
limit.THROW_MAX_CHANGES();
|
||||
limit.MAX_BLOCKSTATES();
|
||||
try {
|
||||
return getExtent().setTile(x, y, z, tile);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setBiome(BlockVector2 position, BiomeType biome) {
|
||||
limit.THROW_MAX_CHANGES();
|
||||
try {
|
||||
return getExtent().setBiome(position, biome);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
||||
limit.THROW_MAX_CHANGES();
|
||||
try {
|
||||
return getExtent().setBiome(x, y, z, biome);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_FAILS()) {
|
||||
throw e;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.boydti.fawe.beta.implementation.processors;
|
||||
|
||||
import com.boydti.fawe.beta.IBatchProcessor;
|
||||
import com.boydti.fawe.beta.IChunk;
|
||||
import com.boydti.fawe.beta.IChunkGet;
|
||||
import com.boydti.fawe.beta.IChunkSet;
|
||||
import com.boydti.fawe.object.FaweLimit;
|
||||
import com.boydti.fawe.object.exception.FaweException;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
|
||||
public class LimitProcessor implements IBatchProcessor {
|
||||
private final FaweLimit limit;
|
||||
private final IBatchProcessor parent;
|
||||
public LimitProcessor(FaweLimit limit, IBatchProcessor parent) {
|
||||
this.limit = limit;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
try {
|
||||
return parent.processSet(chunk, get, set);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_CHANGES()) {
|
||||
throw e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processGet(int chunkX, int chunkZ) {
|
||||
try {
|
||||
return parent.processGet(chunkX, chunkZ);
|
||||
} catch (FaweException e) {
|
||||
if (!limit.MAX_CHECKS()) {
|
||||
throw e;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Extent construct(Extent child) {
|
||||
return new LimitExtent(parent.construct(child), limit);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta.implementation;
|
||||
package com.boydti.fawe.beta.implementation.processors;
|
||||
|
||||
import com.boydti.fawe.beta.IBatchProcessor;
|
||||
import com.boydti.fawe.beta.IChunk;
|
||||
@ -55,13 +55,18 @@ public class MultiBatchProcessor implements IBatchProcessor {
|
||||
|
||||
@Override
|
||||
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
for (IBatchProcessor processor : this.processors) {
|
||||
set = processor.processSet(chunk, get, set);
|
||||
if (set == null) {
|
||||
return null;
|
||||
try {
|
||||
for (IBatchProcessor processor : this.processors) {
|
||||
set = processor.processSet(chunk, get, set);
|
||||
if (set == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return set;
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
@ -0,0 +1,21 @@
|
||||
package com.boydti.fawe.beta.implementation.processors;
|
||||
|
||||
import com.boydti.fawe.beta.IBatchProcessor;
|
||||
import com.boydti.fawe.beta.IChunk;
|
||||
import com.boydti.fawe.beta.IChunkGet;
|
||||
import com.boydti.fawe.beta.IChunkSet;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.extent.NullExtent;
|
||||
|
||||
public enum NullProcessor implements IBatchProcessor {
|
||||
INSTANCE;
|
||||
@Override
|
||||
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Extent construct(Extent child) {
|
||||
return new NullExtent();
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
package com.boydti.fawe.beta.implementation;
|
||||
package com.boydti.fawe.beta.implementation.queue;
|
||||
|
||||
import com.boydti.fawe.beta.ChunkFilterBlock;
|
||||
import com.boydti.fawe.beta.IQueueWrapper;
|
||||
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
|
||||
import com.boydti.fawe.beta.Filter;
|
||||
import com.boydti.fawe.beta.IChunk;
|
||||
import com.boydti.fawe.beta.IQueueExtent;
|
||||
import com.boydti.fawe.beta.filters.CountFilter;
|
||||
import com.boydti.fawe.beta.filters.DistrFilter;
|
||||
import com.boydti.fawe.beta.implementation.filter.CountFilter;
|
||||
import com.boydti.fawe.beta.implementation.filter.DistrFilter;
|
||||
import com.boydti.fawe.beta.implementation.processors.BatchProcessorHolder;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.object.changeset.FaweChangeSet;
|
||||
import com.boydti.fawe.object.clipboard.WorldCopyClipboard;
|
||||
@ -27,12 +28,15 @@ 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.BlockType;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ForkJoinTask;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrapper {
|
||||
|
||||
private final World world;
|
||||
@ -67,26 +71,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
|
||||
return super.enableHistory(changeSet);
|
||||
}
|
||||
|
||||
private ChunkFilterBlock apply(ChunkFilterBlock block, Filter filter, IQueueExtent queue, Region region, int X, int Z) {
|
||||
if (!filter.appliesChunk(X, Z)) {
|
||||
return block;
|
||||
}
|
||||
IChunk chunk = queue.getOrCreateChunk(X, Z);
|
||||
// Initialize
|
||||
chunk.init(queue, X, Z);
|
||||
|
||||
IChunk newChunk = filter.applyChunk(chunk, region);
|
||||
if (newChunk != null) {
|
||||
chunk = newChunk;
|
||||
if (block == null) {
|
||||
block = queue.initFilterBlock();
|
||||
}
|
||||
chunk.filterBlocks(filter, block, region);
|
||||
}
|
||||
queue.submit(chunk);
|
||||
return block;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Filter> T apply(Region region, T filter) {
|
||||
// The chunks positions to iterate over
|
||||
final Set<BlockVector2> chunks = region.getChunks();
|
||||
@ -96,7 +81,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
|
||||
final int size = Math.min(chunks.size(), Settings.IMP.QUEUE.PARALLEL_THREADS);
|
||||
if (size <= 1) {
|
||||
BlockVector2 pos = chunksIter.next();
|
||||
apply(null, filter, getExtent(), region, pos.getX(), pos.getZ());
|
||||
getExtent().apply(null, filter, region, pos.getX(), pos.getZ());
|
||||
} else {
|
||||
final ForkJoinTask[] tasks = IntStream.range(0, size).mapToObj(i -> handler.submit(() -> {
|
||||
try {
|
||||
@ -117,7 +102,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
|
||||
X = pos.getX();
|
||||
Z = pos.getZ();
|
||||
}
|
||||
block = apply(block, newFilter, queue, region, X, Z);
|
||||
block = queue.apply(block, newFilter, region, X, Z);
|
||||
}
|
||||
queue.flush();
|
||||
}
|
||||
@ -167,6 +152,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
|
||||
if (vset instanceof Region) {
|
||||
setBlocks((Region) vset, pattern);
|
||||
}
|
||||
// TODO optimize parallel
|
||||
for (BlockVector3 blockVector3 : vset) {
|
||||
pattern.apply(this, blockVector3, blockVector3);
|
||||
}
|
||||
@ -226,7 +212,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
|
||||
* returned by a given pattern.
|
||||
*
|
||||
* @param region the region to replace the blocks within
|
||||
* @param filter a list of block types to match, or null to use {@link ExistingBlockMask}
|
||||
* @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.function.mask.ExistingBlockMask}
|
||||
* @param replacement the replacement block
|
||||
* @return number of blocks affected
|
||||
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
||||
@ -241,7 +227,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
|
||||
* returned by a given pattern.
|
||||
*
|
||||
* @param region the region to replace the blocks within
|
||||
* @param filter a list of block types to match, or null to use {@link ExistingBlockMask}
|
||||
* @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.function.mask.ExistingBlockMask}
|
||||
* @param pattern the pattern that provides the new blocks
|
||||
* @return number of blocks affected
|
||||
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
@ -1,4 +1,4 @@
|
||||
package com.boydti.fawe.beta.implementation;
|
||||
package com.boydti.fawe.beta.implementation.queue;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.FaweCache;
|
||||
@ -8,6 +8,8 @@ import com.boydti.fawe.beta.IChunkGet;
|
||||
import com.boydti.fawe.beta.IChunkSet;
|
||||
import com.boydti.fawe.beta.IQueueExtent;
|
||||
import com.boydti.fawe.beta.Trimable;
|
||||
import com.boydti.fawe.beta.implementation.cache.ChunkCache;
|
||||
import com.boydti.fawe.beta.IChunkCache;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.object.collection.CleanableThreadLocal;
|
||||
import com.boydti.fawe.util.MemUtil;
|
||||
@ -19,6 +21,7 @@ import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@ -39,6 +42,7 @@ public abstract class QueueHandler implements Trimable, Runnable {
|
||||
private ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool();
|
||||
private ThreadPoolExecutor blockingExecutor = FaweCache.IMP.newBlockingExecutor();
|
||||
private ConcurrentLinkedQueue<FutureTask> syncTasks = new ConcurrentLinkedQueue<>();
|
||||
private ConcurrentLinkedQueue<FutureTask> syncWhenFree = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private Map<World, WeakReference<IChunkCache<IChunkGet>>> chunkGetCache = new HashMap<>();
|
||||
private CleanableThreadLocal<IQueueExtent> queuePool = new CleanableThreadLocal<>(QueueHandler.this::create);
|
||||
@ -60,53 +64,61 @@ public abstract class QueueHandler implements Trimable, Runnable {
|
||||
throw new IllegalStateException("Not main thread");
|
||||
}
|
||||
if (!syncTasks.isEmpty()) {
|
||||
long now = System.currentTimeMillis();
|
||||
targetTPS = 18 - Math.max(Settings.IMP.QUEUE.EXTRA_TIME_MS * 0.05, 0);
|
||||
long diff = 50 + this.last - (this.last = now);
|
||||
long absDiff = Math.abs(diff);
|
||||
if (diff == 0) {
|
||||
allocate = Math.min(50, allocate + 1);
|
||||
} else if (diff < 0) {
|
||||
allocate = Math.max(5, allocate + diff);
|
||||
} else if (!Fawe.get().getTimer().isAbove(targetTPS)) {
|
||||
allocate = Math.max(5, allocate - 1);
|
||||
}
|
||||
long currentAllocate = allocate - absDiff;
|
||||
long currentAllocate = getAllocate();
|
||||
|
||||
if (!MemUtil.isMemoryFree()) {
|
||||
// TODO reduce mem usage
|
||||
// FaweCache trim
|
||||
// Preloader trim
|
||||
}
|
||||
|
||||
boolean wait = false;
|
||||
do {
|
||||
Runnable task = syncTasks.poll();
|
||||
if (task == null) {
|
||||
if (wait) {
|
||||
synchronized (syncTasks) {
|
||||
try {
|
||||
syncTasks.wait(1);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
task = syncTasks.poll();
|
||||
wait = false;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (task != null) {
|
||||
task.run();
|
||||
wait = true;
|
||||
}
|
||||
} while (System.currentTimeMillis() - now < currentAllocate);
|
||||
operate(syncTasks, last, currentAllocate);
|
||||
} else if (!syncWhenFree.isEmpty()) {
|
||||
operate(syncWhenFree, last, getAllocate());
|
||||
} else {
|
||||
// trim??
|
||||
}
|
||||
while (!syncTasks.isEmpty()) {
|
||||
final FutureTask task = syncTasks.poll();
|
||||
}
|
||||
|
||||
private long getAllocate() {
|
||||
long now = System.currentTimeMillis();
|
||||
targetTPS = 18 - Math.max(Settings.IMP.QUEUE.EXTRA_TIME_MS * 0.05, 0);
|
||||
long diff = 50 + this.last - (this.last = now);
|
||||
long absDiff = Math.abs(diff);
|
||||
if (diff == 0) {
|
||||
allocate = Math.min(50, allocate + 1);
|
||||
} else if (diff < 0) {
|
||||
allocate = Math.max(5, allocate + diff);
|
||||
} else if (!Fawe.get().getTimer().isAbove(targetTPS)) {
|
||||
allocate = Math.max(5, allocate - 1);
|
||||
}
|
||||
return allocate - absDiff;
|
||||
}
|
||||
|
||||
private void operate(Queue<FutureTask> queue, long start, long currentAllocate) {
|
||||
boolean wait = false;
|
||||
do {
|
||||
Runnable task = queue.poll();
|
||||
if (task == null) {
|
||||
if (wait) {
|
||||
synchronized (syncTasks) {
|
||||
try {
|
||||
queue.wait(1);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
task = queue.poll();
|
||||
wait = false;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (task != null) {
|
||||
task.run();
|
||||
wait = true;
|
||||
}
|
||||
}
|
||||
} while (System.currentTimeMillis() - start < currentAllocate);
|
||||
}
|
||||
|
||||
public <T extends Future<T>> void complete(Future<T> task) {
|
||||
@ -136,50 +148,83 @@ public abstract class QueueHandler implements Trimable, Runnable {
|
||||
}
|
||||
|
||||
public <T> Future<T> sync(Runnable run, T value) {
|
||||
return sync(run, value, syncTasks);
|
||||
}
|
||||
|
||||
public <T> Future<T> sync(Runnable run) {
|
||||
return sync(run, syncTasks);
|
||||
}
|
||||
|
||||
public <T> Future<T> sync(Callable<T> call) throws Exception {
|
||||
return sync(call, syncTasks);
|
||||
}
|
||||
|
||||
public <T> Future<T> sync(Supplier<T> call) {
|
||||
return sync(call, syncTasks);
|
||||
}
|
||||
|
||||
// Lower priorty sync task (runs only when there are no other tasks)
|
||||
public <T> Future<T> syncWhenFree(Runnable run, T value) {
|
||||
return sync(run, value, syncWhenFree);
|
||||
}
|
||||
|
||||
public <T> Future<T> syncWhenFree(Runnable run) {
|
||||
return sync(run, syncWhenFree);
|
||||
}
|
||||
|
||||
public <T> Future<T> syncWhenFree(Callable<T> call) throws Exception {
|
||||
return sync(call, syncWhenFree);
|
||||
}
|
||||
|
||||
public <T> Future<T> syncWhenFree(Supplier<T> call) {
|
||||
return sync(call, syncWhenFree);
|
||||
}
|
||||
|
||||
private <T> Future<T> sync(Runnable run, T value, Queue<FutureTask> queue) {
|
||||
if (Fawe.isMainThread()) {
|
||||
run.run();
|
||||
return Futures.immediateFuture(value);
|
||||
}
|
||||
final FutureTask<T> result = new FutureTask<>(run, value);
|
||||
syncTasks.add(result);
|
||||
notifySync();
|
||||
queue.add(result);
|
||||
notifySync(queue);
|
||||
return result;
|
||||
}
|
||||
|
||||
public <T> Future<T> sync(Runnable run) {
|
||||
private <T> Future<T> sync(Runnable run, Queue<FutureTask> queue) {
|
||||
if (Fawe.isMainThread()) {
|
||||
run.run();
|
||||
return Futures.immediateCancelledFuture();
|
||||
}
|
||||
final FutureTask<T> result = new FutureTask<>(run, null);
|
||||
syncTasks.add(result);
|
||||
notifySync();
|
||||
queue.add(result);
|
||||
notifySync(queue);
|
||||
return result;
|
||||
}
|
||||
|
||||
public <T> Future<T> sync(Callable<T> call) throws Exception {
|
||||
private <T> Future<T> sync(Callable<T> call, Queue<FutureTask> queue) throws Exception {
|
||||
if (Fawe.isMainThread()) {
|
||||
return Futures.immediateFuture(call.call());
|
||||
}
|
||||
final FutureTask<T> result = new FutureTask<>(call);
|
||||
syncTasks.add(result);
|
||||
notifySync();
|
||||
queue.add(result);
|
||||
notifySync(queue);
|
||||
return result;
|
||||
}
|
||||
|
||||
public <T> Future<T> sync(Supplier<T> call) {
|
||||
private <T> Future<T> sync(Supplier<T> call, Queue<FutureTask> queue) {
|
||||
if (Fawe.isMainThread()) {
|
||||
return Futures.immediateFuture(call.get());
|
||||
}
|
||||
final FutureTask<T> result = new FutureTask<>(call::get);
|
||||
syncTasks.add(result);
|
||||
notifySync();
|
||||
queue.add(result);
|
||||
notifySync(queue);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void notifySync() {
|
||||
synchronized (syncTasks) {
|
||||
syncTasks.notifyAll();
|
||||
private void notifySync(Object object) {
|
||||
synchronized (object) {
|
||||
object.notifyAll();
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,28 @@
|
||||
package com.boydti.fawe.beta.implementation;
|
||||
package com.boydti.fawe.beta.implementation.queue;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.beta.CharFilterBlock;
|
||||
import com.boydti.fawe.beta.ChunkFilterBlock;
|
||||
import com.boydti.fawe.beta.implementation.filter.block.CharFilterBlock;
|
||||
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
|
||||
import com.boydti.fawe.beta.IBatchProcessor;
|
||||
import com.boydti.fawe.beta.IChunk;
|
||||
import com.boydti.fawe.beta.IChunkGet;
|
||||
import com.boydti.fawe.beta.IChunkSet;
|
||||
import com.boydti.fawe.beta.IQueueExtent;
|
||||
import com.boydti.fawe.beta.IChunkCache;
|
||||
import com.boydti.fawe.beta.implementation.chunk.NullChunk;
|
||||
import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks;
|
||||
import com.boydti.fawe.beta.implementation.holder.ChunkHolder;
|
||||
import com.boydti.fawe.beta.implementation.holder.ReferenceChunk;
|
||||
import com.boydti.fawe.beta.implementation.chunk.ChunkHolder;
|
||||
import com.boydti.fawe.beta.implementation.chunk.ReferenceChunk;
|
||||
import com.boydti.fawe.beta.implementation.processors.BatchProcessorHolder;
|
||||
import com.boydti.fawe.beta.implementation.processors.EmptyBatchProcessor;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.object.changeset.FaweChangeSet;
|
||||
import com.boydti.fawe.object.exception.FaweException;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.MemUtil;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
@ -221,13 +220,15 @@ public class SingleThreadQueueExtent extends BatchProcessorHolder implements IQu
|
||||
|
||||
@Override
|
||||
public final IChunk getOrCreateChunk(int x, int z) {
|
||||
if (!processGet(x, z)) {
|
||||
throw FaweCache.CHUNK;
|
||||
}
|
||||
final long pair = (long) x << 32 | z & 0xffffffffL;
|
||||
if (pair == lastPair) {
|
||||
return lastChunk;
|
||||
}
|
||||
if (!processGet(x, z)) {
|
||||
lastPair = pair;
|
||||
lastChunk = NullChunk.INSTANCE;
|
||||
return NullChunk.INSTANCE;
|
||||
}
|
||||
|
||||
IChunk chunk = chunks.get(pair);
|
||||
if (chunk instanceof ReferenceChunk) {
|
||||
@ -276,11 +277,20 @@ public class SingleThreadQueueExtent extends BatchProcessorHolder implements IQu
|
||||
private void pollSubmissions(int targetSize, boolean aggressive) {
|
||||
final int overflow = submissions.size() - targetSize;
|
||||
if (aggressive) {
|
||||
if (targetSize == 0) {
|
||||
while (!submissions.isEmpty()) {
|
||||
Future future = submissions.poll();
|
||||
try {
|
||||
while (future != null) future = (Future) future.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < overflow; i++) {
|
||||
Future first = submissions.poll();
|
||||
try {
|
||||
while ((first = (Future) first.get()) != null) {
|
||||
}
|
||||
while (first != null) first = (Future) first.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -327,7 +337,6 @@ public class SingleThreadQueueExtent extends BatchProcessorHolder implements IQu
|
||||
chunks.clear();
|
||||
}
|
||||
pollSubmissions(0, true);
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
@ -6,7 +6,7 @@ import static com.sk89q.worldedit.util.formatting.text.TextComponent.newline;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.FaweAPI;
|
||||
import com.boydti.fawe.beta.SingleFilterBlock;
|
||||
import com.boydti.fawe.beta.implementation.filter.block.SingleFilterBlock;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.object.RunnableVal;
|
||||
import com.boydti.fawe.object.brush.visualization.cfi.HeightMapMCAGenerator;
|
||||
@ -75,6 +75,7 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.IntStream;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.enginehub.piston.annotation.Command;
|
||||
import org.enginehub.piston.annotation.CommandContainer;
|
||||
import org.enginehub.piston.annotation.param.Arg;
|
||||
@ -431,7 +432,7 @@ public class CFICommands {
|
||||
Clipboard clipboard = holder.getClipboard();
|
||||
boolean[] ids = new boolean[BlockTypes.size()];
|
||||
for (BlockVector3 pt : clipboard.getRegion()) {
|
||||
ids[clipboard.getBlock(pt).getInternalBlockTypeId()] = true;
|
||||
ids[clipboard.getBlock(pt).getBlockType().getInternalId()] = true;
|
||||
}
|
||||
blocks = new HashSet<>();
|
||||
for (int combined = 0; combined < ids.length; combined++) {
|
||||
@ -605,7 +606,7 @@ public class CFICommands {
|
||||
@CommandPermissions("worldedit.anvil.cfi")
|
||||
public void waterId(Player player, BlockStateHolder block) throws WorldEditException {
|
||||
CFISettings settings = assertSettings(player);
|
||||
settings.getGenerator().setWaterId(block.getBlockType().getInternalId());
|
||||
settings.getGenerator().setWater(block.toImmutableState());
|
||||
|
||||
player.print("Set water id!");
|
||||
settings.resetComponent();
|
||||
@ -620,7 +621,7 @@ public class CFICommands {
|
||||
@CommandPermissions("worldedit.anvil.cfi")
|
||||
public void baseId(Player player, BlockStateHolder block) throws WorldEditException {
|
||||
CFISettings settings = assertSettings(player);
|
||||
settings.getGenerator().setBedrockId(block.getBlockType().getInternalId());
|
||||
settings.getGenerator().setBedrock(block.toImmutableState());
|
||||
player.print(TextComponent.of("Set base id!"));
|
||||
settings.resetComponent();
|
||||
component(player);
|
||||
|
@ -14,6 +14,8 @@ public class Settings extends Config {
|
||||
|
||||
@Ignore
|
||||
public boolean PROTOCOL_SUPPORT_FIX = false;
|
||||
@Ignore
|
||||
public boolean PLOTSQUARED_HOOK = false;
|
||||
|
||||
@Comment("These first 6 aren't configurable") // This is a comment
|
||||
@Final // Indicates that this value isn't configurable
|
||||
|
@ -4,17 +4,11 @@ import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.object.clipboard.CPUOptimizedClipboard;
|
||||
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
|
||||
import com.boydti.fawe.object.clipboard.FaweClipboard;
|
||||
import com.boydti.fawe.object.clipboard.LinearClipboard;
|
||||
import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.math.Vector3;
|
||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
@ -29,7 +23,7 @@ public class CorruptSchematicStreamer {
|
||||
|
||||
private final InputStream stream;
|
||||
private final UUID uuid;
|
||||
private FaweClipboard fc;
|
||||
private LinearClipboard fc;
|
||||
final AtomicInteger volume = new AtomicInteger();
|
||||
final AtomicInteger width = new AtomicInteger();
|
||||
final AtomicInteger height = new AtomicInteger();
|
||||
@ -78,7 +72,7 @@ public class CorruptSchematicStreamer {
|
||||
}
|
||||
}
|
||||
|
||||
public FaweClipboard setupClipboard() {
|
||||
public LinearClipboard setupClipboard() {
|
||||
if (fc != null) {
|
||||
return fc;
|
||||
}
|
||||
@ -87,11 +81,11 @@ public class CorruptSchematicStreamer {
|
||||
Fawe.debug("No dimensions found! Estimating based on factors:" + dimensions);
|
||||
}
|
||||
if (Settings.IMP.CLIPBOARD.USE_DISK) {
|
||||
fc = new DiskOptimizedClipboard(dimensions.getBlockX(), dimensions.getBlockY(), dimensions.getBlockZ(), uuid);
|
||||
fc = new DiskOptimizedClipboard(dimensions, uuid);
|
||||
} else if (Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL == 0) {
|
||||
fc = new CPUOptimizedClipboard(dimensions.getBlockX(), dimensions.getBlockY(), dimensions.getBlockZ());
|
||||
fc = new CPUOptimizedClipboard(dimensions);
|
||||
} else {
|
||||
fc = new MemoryOptimizedClipboard(dimensions.getBlockX(), dimensions.getBlockY(), dimensions.getBlockZ());
|
||||
fc = new MemoryOptimizedClipboard(dimensions);
|
||||
}
|
||||
return fc;
|
||||
}
|
||||
|
@ -1,90 +0,0 @@
|
||||
package com.boydti.fawe.jnbt;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.object.exception.FaweException;
|
||||
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class NBTStreamer {
|
||||
private final NBTInputStream is;
|
||||
private final HashMap<String, BiConsumer> readers;
|
||||
|
||||
public NBTStreamer(NBTInputStream stream) {
|
||||
this.is = stream;
|
||||
readers = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the entire stream and runs the applicable readers
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void readFully() throws IOException {
|
||||
is.readNamedTagLazy(readers::get);
|
||||
is.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the stream until all readers have been used<br>
|
||||
* - Use readFully if you expect a reader to appear more than once
|
||||
* - Can exit early without having reading the entire file
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void readQuick() throws IOException {
|
||||
try {
|
||||
is.readNamedTagLazy(node -> {
|
||||
if (readers.isEmpty()) {
|
||||
throw FaweCache.MANUAL;
|
||||
}
|
||||
return readers.remove(node);
|
||||
});
|
||||
} catch (FaweException ignore) {}
|
||||
is.close();
|
||||
}
|
||||
|
||||
public <T, V> void addReader(String node, BiConsumer<T, V> run) {
|
||||
if (run instanceof NBTStreamReader) {
|
||||
((NBTStreamReader) run).init(node);
|
||||
}
|
||||
readers.put(node, run);
|
||||
}
|
||||
|
||||
public static abstract class NBTStreamReader<T, V> implements BiConsumer<T, V> {
|
||||
private String node;
|
||||
|
||||
public void init(String node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public String getNode() {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class ByteReader implements BiConsumer<Integer, Integer> {
|
||||
@Override
|
||||
public void accept(Integer index, Integer value) {
|
||||
run(index, value);
|
||||
}
|
||||
|
||||
public abstract void run(int index, int byteValue);
|
||||
}
|
||||
|
||||
public interface LazyReader extends BiConsumer<Integer, DataInputStream> {}
|
||||
|
||||
public static abstract class LongReader implements BiConsumer<Integer, Long> {
|
||||
@Override
|
||||
public void accept(Integer index, Long value) {
|
||||
run(index, value);
|
||||
}
|
||||
|
||||
public abstract void run(int index, long byteValue);
|
||||
}
|
||||
}
|
@ -1,420 +0,0 @@
|
||||
package com.boydti.fawe.jnbt;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.object.FaweInputStream;
|
||||
import com.boydti.fawe.object.FaweOutputStream;
|
||||
import com.boydti.fawe.object.clipboard.CPUOptimizedClipboard;
|
||||
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
|
||||
import com.boydti.fawe.object.clipboard.FaweClipboard;
|
||||
import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard;
|
||||
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
|
||||
import com.boydti.fawe.object.io.FastByteArraysInputStream;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
import com.sk89q.worldedit.registry.state.PropertyKey;
|
||||
import com.sk89q.worldedit.util.Direction;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockCategories;
|
||||
import com.sk89q.worldedit.world.block.BlockID;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypeSwitch;
|
||||
import com.sk89q.worldedit.world.block.BlockTypeSwitchBuilder;
|
||||
import com.sk89q.worldedit.world.entity.EntityType;
|
||||
import com.sk89q.worldedit.world.entity.EntityTypes;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import com.sk89q.worldedit.world.registry.LegacyMapper;
|
||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||
import net.jpountz.lz4.LZ4BlockOutputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
// TODO FIXME
|
||||
public class SchematicStreamer extends NBTStreamer {
|
||||
private final UUID uuid;
|
||||
private FastByteArrayOutputStream idOut = new FastByteArrayOutputStream();
|
||||
private FastByteArrayOutputStream dataOut = new FastByteArrayOutputStream();
|
||||
private FastByteArrayOutputStream addOut;
|
||||
|
||||
private FaweOutputStream ids;
|
||||
private FaweOutputStream datas;
|
||||
private FaweOutputStream adds;
|
||||
|
||||
public SchematicStreamer(NBTInputStream stream, UUID uuid) {
|
||||
super(stream);
|
||||
this.uuid = uuid;
|
||||
clipboard = new BlockArrayClipboard(new CuboidRegion(BlockVector3.at(0, 0, 0), BlockVector3.at(0, 0, 0)), fc);
|
||||
}
|
||||
|
||||
public void addBlockReaders() throws IOException {
|
||||
NBTStreamReader<? extends Integer, ? extends Integer> idInit = new NBTStreamReader<Integer, Integer>() {
|
||||
@Override
|
||||
public void accept(Integer length, Integer type) {
|
||||
setupClipboard(length);
|
||||
ids = new FaweOutputStream(new LZ4BlockOutputStream(idOut));
|
||||
}
|
||||
};
|
||||
NBTStreamReader<? extends Integer, ? extends Integer> dataInit = new NBTStreamReader<Integer, Integer>() {
|
||||
@Override
|
||||
public void accept(Integer length, Integer type) {
|
||||
setupClipboard(length);
|
||||
datas = new FaweOutputStream(new LZ4BlockOutputStream(dataOut));
|
||||
}
|
||||
};
|
||||
NBTStreamReader<? extends Integer, ? extends Integer> addInit = new NBTStreamReader<Integer, Integer>() {
|
||||
@Override
|
||||
public void accept(Integer length, Integer type) {
|
||||
setupClipboard(length*2);
|
||||
addOut = new FastByteArrayOutputStream();
|
||||
adds = new FaweOutputStream(new LZ4BlockOutputStream(addOut));
|
||||
}
|
||||
};
|
||||
|
||||
addReader("Schematic.Blocks.?", idInit);
|
||||
addReader("Schematic.Data.?", dataInit);
|
||||
addReader("Schematic.AddBlocks.?", addInit);
|
||||
addReader("Schematic.Blocks.#", new ByteReader() {
|
||||
@Override
|
||||
public void run(int index, int value) {
|
||||
try {
|
||||
ids.write(value);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
addReader("Schematic.Data.#", new ByteReader() {
|
||||
@Override
|
||||
public void run(int index, int value) {
|
||||
try {
|
||||
datas.write(value);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
addReader("Schematic.AddBlocks.#", new ByteReader() {
|
||||
@Override
|
||||
public void run(int index, int value) {
|
||||
if (value != 0) {
|
||||
int first = value & 0x0F;
|
||||
int second = (value & 0xF0) >> 4;
|
||||
try {
|
||||
if (first != 0) adds.write(first);
|
||||
if (second != 0) adds.write(second);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
ByteReader biomeReader = new ByteReader() {
|
||||
@Override
|
||||
public void run(int index, int value) {
|
||||
BiomeType biome = BiomeTypes.getLegacy(value);
|
||||
if (biome != null) {
|
||||
fc.setBiome(index, biome);
|
||||
}
|
||||
}
|
||||
};
|
||||
NBTStreamReader<Integer, Integer> initializer23 = new NBTStreamReader<Integer, Integer>() {
|
||||
@Override
|
||||
public void accept(Integer value1, Integer value2) {
|
||||
if (fc == null) setupClipboard(length * width * height);
|
||||
}
|
||||
};
|
||||
addReader("Schematic.AWEBiomes.?", initializer23);
|
||||
addReader("Schematic.Biomes.?", initializer23);
|
||||
addReader("Schematic.AWEBiomes.#", biomeReader); // AWE stores as an int[]
|
||||
addReader("Schematic.Biomes.#", biomeReader); // FAWE stores as a byte[] (4x smaller)
|
||||
|
||||
// Tiles
|
||||
addReader("Schematic.TileEntities.#", (BiConsumer<Integer, CompoundTag>) (index, value) -> {
|
||||
if (fc == null) {
|
||||
setupClipboard(0);
|
||||
}
|
||||
int x = value.getInt("x");
|
||||
int y = value.getInt("y");
|
||||
int z = value.getInt("z");
|
||||
fc.setTile(x, y, z, value);
|
||||
});
|
||||
// Entities
|
||||
addReader("Schematic.Entities.#", (BiConsumer<Integer, CompoundTag>) (index, compound) -> {
|
||||
if (fc == null) {
|
||||
setupClipboard(0);
|
||||
}
|
||||
String id = compound.getString("id");
|
||||
if (id.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
ListTag positionTag = compound.getListTag("Pos");
|
||||
ListTag directionTag = compound.getListTag("Rotation");
|
||||
EntityType type = EntityTypes.parse(id);
|
||||
if (type != null) {
|
||||
compound.getValue().put("Id", new StringTag(type.getId()));
|
||||
BaseEntity state = new BaseEntity(type, compound);
|
||||
fc.createEntity(clipboard, positionTag.asDouble(0), positionTag.asDouble(1), positionTag.asDouble(2), (float) directionTag.asDouble(0), (float) directionTag.asDouble(1), state);
|
||||
} else {
|
||||
Fawe.debug("Invalid entity: " + id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully() throws IOException {
|
||||
super.readFully();
|
||||
if (ids != null) ids.close();
|
||||
if (datas != null) datas.close();
|
||||
if (adds != null) adds.close();
|
||||
FaweInputStream idIn = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(idOut.toByteArrays())));
|
||||
FaweInputStream dataIn = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(dataOut.toByteArrays())));
|
||||
|
||||
LegacyMapper remap = LegacyMapper.getInstance();
|
||||
BlockVector3 dimensions = fc.getDimensions();
|
||||
int length = dimensions.getBlockX() * dimensions.getBlockY() * dimensions.getBlockZ();
|
||||
if (adds == null) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
fc.setBlock(i, remap.getBlockFromLegacyCombinedId(((idIn.read() & 0xFF) << 4) + (dataIn.read() & 0xF)));
|
||||
}
|
||||
} else {
|
||||
FaweInputStream addIn = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(dataOut.toByteArrays())));
|
||||
for (int i = 0; i < length; i++) {
|
||||
fc.setBlock(i, remap.getBlockFromLegacyCombinedId(((addIn.read() & 0xFF) << 8) + ((idIn.read() & 0xFF) << 4) + (dataIn.read() & 0xF)));
|
||||
}
|
||||
addIn.close();
|
||||
}
|
||||
idIn.close();
|
||||
dataIn.close();
|
||||
}
|
||||
|
||||
private void fixStates() {
|
||||
fc.forEach(new FaweClipboard.BlockReader() {
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> void run(int x, int y, int z, B block) {
|
||||
BlockType type = block.getBlockType();
|
||||
if (BlockCategories.STAIRS.contains(type)) {
|
||||
Object half = block.getState(PropertyKey.HALF);
|
||||
Direction facing = block.getState(PropertyKey.FACING);
|
||||
|
||||
BlockVector3 forward = facing.toBlockVector();
|
||||
Direction left = facing.getLeft();
|
||||
Direction right = facing.getRight();
|
||||
|
||||
BlockStateHolder<com.sk89q.worldedit.world.block.BaseBlock> forwardBlock = fc.getBlock(x + forward.getBlockX(), y + forward.getBlockY(), z + forward.getBlockZ());
|
||||
BlockType forwardType = forwardBlock.getBlockType();
|
||||
if (forwardType.hasProperty(PropertyKey.SHAPE) && forwardType.hasProperty(PropertyKey.FACING)) {
|
||||
Direction forwardFacing = (Direction) forwardBlock.getState(PropertyKey.FACING);
|
||||
if (forwardFacing == left) {
|
||||
BlockStateHolder<com.sk89q.worldedit.world.block.BaseBlock> rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ());
|
||||
BlockType rightType = rightBlock.getBlockType();
|
||||
if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) {
|
||||
fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "inner_left"));
|
||||
}
|
||||
return;
|
||||
} else if (forwardFacing == right) {
|
||||
BlockStateHolder<com.sk89q.worldedit.world.block.BaseBlock> leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ());
|
||||
BlockType leftType = leftBlock.getBlockType();
|
||||
if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) {
|
||||
fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "inner_right"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BlockStateHolder<com.sk89q.worldedit.world.block.BaseBlock> backwardsBlock = fc.getBlock(x - forward.getBlockX(), y - forward.getBlockY(), z - forward.getBlockZ());
|
||||
BlockType backwardsType = backwardsBlock.getBlockType();
|
||||
if (backwardsType.hasProperty(PropertyKey.SHAPE) && backwardsType.hasProperty(PropertyKey.FACING)) {
|
||||
Direction backwardsFacing = (Direction) backwardsBlock.getState(PropertyKey.FACING);
|
||||
if (backwardsFacing == left) {
|
||||
BlockStateHolder<com.sk89q.worldedit.world.block.BaseBlock> rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ());
|
||||
BlockType rightType = rightBlock.getBlockType();
|
||||
if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) {
|
||||
fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "outer_left"));
|
||||
}
|
||||
return;
|
||||
} else if (backwardsFacing == right) {
|
||||
BlockStateHolder<com.sk89q.worldedit.world.block.BaseBlock> leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ());
|
||||
BlockType leftType = leftBlock.getBlockType();
|
||||
if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) {
|
||||
fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "outer_right"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int group = group(type);
|
||||
if (group == -1) return;
|
||||
BlockStateHolder set = block;
|
||||
|
||||
if (set.getState(PropertyKey.NORTH) == Boolean.FALSE && merge(group, x, y, z - 1)) set = set.with(PropertyKey.NORTH, true);
|
||||
if (set.getState(PropertyKey.EAST) == Boolean.FALSE && merge(group, x + 1, y, z)) set = set.with(PropertyKey.EAST, true);
|
||||
if (set.getState(PropertyKey.SOUTH) == Boolean.FALSE && merge(group, x, y, z + 1)) set = set.with(PropertyKey.SOUTH, true);
|
||||
if (set.getState(PropertyKey.WEST) == Boolean.FALSE && merge(group, x - 1, y, z)) set = set.with(PropertyKey.WEST, true);
|
||||
|
||||
if (group == 2) {
|
||||
int ns = ((Boolean) set.getState(PropertyKey.NORTH) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.SOUTH) ? 1 : 0);
|
||||
int ew = ((Boolean) set.getState(PropertyKey.EAST) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.WEST) ? 1 : 0);
|
||||
if (Math.abs(ns - ew) != 2 || fc.getBlock(x, y + 1, z).getBlockType().getMaterial().isSolid()) {
|
||||
set = set.with(PropertyKey.UP, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (set != block) fc.setBlock(x, y, z, set);
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
private BlockTypeSwitch<Boolean> fullCube = new BlockTypeSwitchBuilder<>(false).add(type -> {
|
||||
BlockMaterial mat = type.getMaterial();
|
||||
return (mat.isFullCube() && !mat.isFragileWhenPushed() && mat.getLightValue() == 0 && mat.isOpaque() && mat.isSolid() && !mat.isTranslucent());
|
||||
}, true).build();
|
||||
|
||||
private boolean merge(int group, int x, int y, int z) {
|
||||
BlockStateHolder<com.sk89q.worldedit.world.block.BaseBlock> block = fc.getBlock(x, y, z);
|
||||
BlockType type = block.getBlockType();
|
||||
return group(type) == group || fullCube.apply(type);
|
||||
}
|
||||
|
||||
private int group(BlockType type) {
|
||||
switch (type.getInternalId()) {
|
||||
case BlockID.ACACIA_FENCE:
|
||||
case BlockID.BIRCH_FENCE:
|
||||
case BlockID.DARK_OAK_FENCE:
|
||||
case BlockID.JUNGLE_FENCE:
|
||||
case BlockID.OAK_FENCE:
|
||||
case BlockID.SPRUCE_FENCE:
|
||||
return 0;
|
||||
case BlockID.NETHER_BRICK_FENCE:
|
||||
return 1;
|
||||
case BlockID.COBBLESTONE_WALL:
|
||||
case BlockID.MOSSY_COBBLESTONE_WALL:
|
||||
return 2;
|
||||
case BlockID.IRON_BARS:
|
||||
case BlockID.BLACK_STAINED_GLASS_PANE:
|
||||
case BlockID.BLUE_STAINED_GLASS_PANE:
|
||||
case BlockID.BROWN_MUSHROOM_BLOCK:
|
||||
case BlockID.BROWN_STAINED_GLASS_PANE:
|
||||
case BlockID.CYAN_STAINED_GLASS_PANE:
|
||||
case BlockID.GLASS_PANE:
|
||||
case BlockID.GRAY_STAINED_GLASS_PANE:
|
||||
case BlockID.GREEN_STAINED_GLASS_PANE:
|
||||
case BlockID.LIGHT_BLUE_STAINED_GLASS_PANE:
|
||||
case BlockID.LIGHT_GRAY_STAINED_GLASS_PANE:
|
||||
case BlockID.LIME_STAINED_GLASS_PANE:
|
||||
case BlockID.MAGENTA_STAINED_GLASS_PANE:
|
||||
case BlockID.ORANGE_STAINED_GLASS_PANE:
|
||||
case BlockID.PINK_STAINED_GLASS_PANE:
|
||||
case BlockID.PURPLE_STAINED_GLASS_PANE:
|
||||
case BlockID.RED_STAINED_GLASS_PANE:
|
||||
case BlockID.WHITE_STAINED_GLASS_PANE:
|
||||
case BlockID.YELLOW_STAINED_GLASS_PANE:
|
||||
return 3;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void addDimensionReaders() {
|
||||
addReader("Schematic.Height",
|
||||
(BiConsumer<Integer, Short>) (index, value) -> height = (value));
|
||||
addReader("Schematic.Width", (BiConsumer<Integer, Short>) (index, value) -> width = (value));
|
||||
addReader("Schematic.Length",
|
||||
(BiConsumer<Integer, Short>) (index, value) -> length = (value));
|
||||
addReader("Schematic.WEOriginX",
|
||||
(BiConsumer<Integer, Integer>) (index, value) -> originX = (value));
|
||||
addReader("Schematic.WEOriginY",
|
||||
(BiConsumer<Integer, Integer>) (index, value) -> originY = (value));
|
||||
addReader("Schematic.WEOriginZ",
|
||||
(BiConsumer<Integer, Integer>) (index, value) -> originZ = (value));
|
||||
addReader("Schematic.WEOffsetX",
|
||||
(BiConsumer<Integer, Integer>) (index, value) -> offsetX = (value));
|
||||
addReader("Schematic.WEOffsetY",
|
||||
(BiConsumer<Integer, Integer>) (index, value) -> offsetY = (value));
|
||||
addReader("Schematic.WEOffsetZ",
|
||||
(BiConsumer<Integer, Integer>) (index, value) -> offsetZ = (value));
|
||||
}
|
||||
|
||||
private int height;
|
||||
private int width;
|
||||
private int length;
|
||||
|
||||
private int originX;
|
||||
private int originY;
|
||||
private int originZ;
|
||||
|
||||
private int offsetX;
|
||||
private int offsetY;
|
||||
private int offsetZ;
|
||||
|
||||
private BlockArrayClipboard clipboard;
|
||||
private FaweClipboard fc;
|
||||
|
||||
private FaweClipboard setupClipboard(int size) {
|
||||
if (fc != null) {
|
||||
if (fc.getDimensions().getX() == 0) {
|
||||
fc.setDimensions(BlockVector3.at(size, 1, 1));
|
||||
}
|
||||
return fc;
|
||||
}
|
||||
if (Settings.IMP.CLIPBOARD.USE_DISK) {
|
||||
return fc = new DiskOptimizedClipboard(size, 1, 1, uuid);
|
||||
} else if (Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL == 0) {
|
||||
return fc = new CPUOptimizedClipboard(size, 1, 1);
|
||||
} else {
|
||||
return fc = new MemoryOptimizedClipboard(size, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public BlockVector3 getOrigin() {
|
||||
return BlockVector3.at(originX, originY, originZ);
|
||||
}
|
||||
|
||||
public BlockVector3 getOffset() {
|
||||
return BlockVector3.at(offsetX, offsetY, offsetZ);
|
||||
}
|
||||
|
||||
public BlockVector3 getDimensions() {
|
||||
return BlockVector3.at(width, height, length);
|
||||
}
|
||||
|
||||
public void setClipboard(FaweClipboard clipboard) {
|
||||
this.fc = clipboard;
|
||||
}
|
||||
|
||||
public Clipboard getClipboard() throws IOException {
|
||||
try {
|
||||
setupClipboard(0);
|
||||
addDimensionReaders();
|
||||
addBlockReaders();
|
||||
readFully();
|
||||
BlockVector3 min = BlockVector3.at(originX, originY, originZ);
|
||||
BlockVector3 offset = BlockVector3.at(offsetX, offsetY, offsetZ);
|
||||
BlockVector3 origin = min.subtract(offset);
|
||||
BlockVector3 dimensions = BlockVector3.at(width, height, length);
|
||||
fc.setDimensions(dimensions);
|
||||
fixStates();
|
||||
CuboidRegion region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE));
|
||||
clipboard.init(region, fc);
|
||||
clipboard.setOrigin(origin);
|
||||
return clipboard;
|
||||
} catch (Throwable e) {
|
||||
if (fc != null) {
|
||||
fc.close();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,49 +1,161 @@
|
||||
package com.boydti.fawe.object.brush.visualization.cfi;
|
||||
package com.boydti.fawe.jnbt.anvil;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.beta.IChunkSet;
|
||||
import com.boydti.fawe.jnbt.streamer.StreamDelegate;
|
||||
import com.boydti.fawe.jnbt.streamer.ValueReader;
|
||||
import com.boydti.fawe.object.collection.BitArray4096;
|
||||
import com.boydti.fawe.object.collection.BlockVector3ChunkMap;
|
||||
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.NBTConstants;
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
import com.sk89q.jnbt.NBTOutputStream;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.registry.state.Property;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockID;
|
||||
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 com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class WritableMCAChunk {
|
||||
public class MCAChunk implements IChunkSet {
|
||||
public final boolean[] hasSections = new boolean[16];
|
||||
public final byte[] skyLight = new byte[65536];
|
||||
public final byte[] blockLight = new byte[65536];
|
||||
|
||||
public boolean hasBiomes = false;
|
||||
public final int[] biomes = new int[256];
|
||||
public final byte[] biomes = new byte[256];
|
||||
|
||||
public final int[] blocks = new int[65536];
|
||||
public final char[] blocks = new char[65536];
|
||||
|
||||
public Map<Short, CompoundTag> tiles = new HashMap<>();
|
||||
public Map<UUID, CompoundTag> entities = new HashMap<>();
|
||||
public final BlockVector3ChunkMap<CompoundTag> tiles = new BlockVector3ChunkMap<CompoundTag>();
|
||||
public final Map<UUID, CompoundTag> entities = new HashMap<>();
|
||||
public long inhabitedTime = System.currentTimeMillis();
|
||||
public long lastUpdate;
|
||||
|
||||
public int modified;
|
||||
public boolean deleted;
|
||||
|
||||
public int chunkX, chunkZ;
|
||||
public int chunkX;
|
||||
public int chunkZ;
|
||||
|
||||
protected WritableMCAChunk() {
|
||||
public MCAChunk() {}
|
||||
|
||||
private boolean readLayer(Section section) {
|
||||
if (section.palette == null || section.layer == -1 || section.blocksLength == -1 || section.palette[section.palette.length - 1] == null || section.blocks == null) {
|
||||
// not initialized
|
||||
return false;
|
||||
}
|
||||
|
||||
int bitsPerEntry = MathMan.log2nlz(section.palette.length - 1);
|
||||
BitArray4096 bitArray = new BitArray4096(section.blocks, bitsPerEntry);
|
||||
char[] buffer = FaweCache.IMP.SECTION_BITS_TO_CHAR.get();
|
||||
bitArray.toRaw(buffer);
|
||||
int offset = section.layer << 12;
|
||||
for (int i = 0; i < buffer.length; i++) {
|
||||
BlockState block = section.palette[buffer[i]];
|
||||
blocks[offset + i] = block.getOrdinalChar();
|
||||
}
|
||||
|
||||
section.layer = -1;
|
||||
section.blocksLength = -1;
|
||||
section.blocks = null;
|
||||
section.palette = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static class Section {
|
||||
public int layer = -1;
|
||||
public long[] blocks;
|
||||
public int blocksLength = -1;
|
||||
public BlockState[] palette;
|
||||
}
|
||||
|
||||
public MCAChunk(NBTInputStream nis, int chunkX, int chunkZ, boolean readPos) throws IOException {
|
||||
this.chunkX = chunkX;
|
||||
this.chunkZ = chunkZ;
|
||||
read(nis, readPos);
|
||||
}
|
||||
|
||||
public void read(NBTInputStream nis, boolean readPos) throws IOException {
|
||||
StreamDelegate root = createDelegate(nis, readPos);
|
||||
nis.readNamedTagLazy(root);
|
||||
}
|
||||
|
||||
public StreamDelegate createDelegate(NBTInputStream nis, boolean readPos) {
|
||||
StreamDelegate root = new StreamDelegate();
|
||||
StreamDelegate level = root.add("").add("Level");
|
||||
|
||||
level.add("InhabitedTime").withLong((i, v) -> inhabitedTime = v);
|
||||
level.add("LastUpdate").withLong((i, v) -> lastUpdate = v);
|
||||
|
||||
if (readPos) {
|
||||
level.add("xPos").withInt((i, v) -> MCAChunk.this.chunkX = v);
|
||||
level.add("zPos").withInt((i, v) -> MCAChunk.this.chunkZ = v);
|
||||
}
|
||||
|
||||
Section section = new Section();
|
||||
|
||||
StreamDelegate layers = level.add("Sections");
|
||||
StreamDelegate layer = layers.add();
|
||||
layer.withInfo((length, type) -> {
|
||||
section.layer = -1;
|
||||
section.blocksLength = -1;
|
||||
});
|
||||
layer.add("Y").withInt((i, y) -> section.layer = y);
|
||||
layer.add("Palette").withElem((ValueReader<Map<String, Object>>) (index, map) -> {
|
||||
String name = (String) map.get("Name");
|
||||
BlockType type = BlockTypes.get(name);
|
||||
BlockState state = type.getDefaultState();
|
||||
Map<String, String> properties = (Map<String, String>) map.get("Properties");
|
||||
if (properties != null) {
|
||||
for (Map.Entry<String, String> entry : properties.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
Property property = type.getProperty(key);
|
||||
state = state.with(property, property.getValueFor(value));
|
||||
}
|
||||
}
|
||||
section.palette[index] = state;
|
||||
readLayer(section);
|
||||
});
|
||||
StreamDelegate blockStates = layer.add("BlockStates");
|
||||
blockStates.withInfo((length, type) -> {
|
||||
if (section.blocks == null) {
|
||||
section.blocks = FaweCache.IMP.LONG_BUFFER_1024.get();
|
||||
}
|
||||
section.blocksLength = length;
|
||||
});
|
||||
blockStates.withLong((index, value) -> section.blocks[index] = value);
|
||||
level.add("TileEntities").withElem((ValueReader<Map<String, Object>>) (index, value) -> {
|
||||
CompoundTag tile = FaweCache.IMP.asTag(value);
|
||||
int x = tile.getInt("x") & 15;
|
||||
int y = tile.getInt("y");
|
||||
int z = tile.getInt("z") & 15;
|
||||
tiles.put(x, y, z, tile);
|
||||
});
|
||||
level.add("Entities").withElem((ValueReader<Map<String, Object>>) (index, value) -> {
|
||||
CompoundTag entity = FaweCache.IMP.asTag(value);
|
||||
entities.put(entity.getUUID(), entity);
|
||||
});
|
||||
level.add("Biomes").withInt((index, value) -> biomes[index] = (byte) value);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
@ -54,14 +166,22 @@ public class WritableMCAChunk {
|
||||
return chunkZ;
|
||||
}
|
||||
|
||||
public void setLoc(int X, int Z) {
|
||||
@Override
|
||||
public boolean hasSection(int layer) {
|
||||
return hasSections[layer];
|
||||
}
|
||||
|
||||
public void setPosition(int X, int Z) {
|
||||
this.chunkX = X;
|
||||
this.chunkZ = Z;
|
||||
}
|
||||
|
||||
public void clear(int X, int Z) {
|
||||
this.chunkX = X;
|
||||
this.chunkZ = Z;
|
||||
@Override
|
||||
public IChunkSet reset() {
|
||||
return this.reset(true);
|
||||
}
|
||||
|
||||
public IChunkSet reset(boolean full) {
|
||||
if (!tiles.isEmpty()) {
|
||||
tiles.clear();
|
||||
}
|
||||
@ -71,11 +191,13 @@ public class WritableMCAChunk {
|
||||
modified = 0;
|
||||
deleted = false;
|
||||
hasBiomes = false;
|
||||
// TODO optimize
|
||||
for (int i = 0; i < 65536; i++) {
|
||||
blocks[i] = BlockID.AIR;
|
||||
if (full) {
|
||||
for (int i = 0; i < 65536; i++) {
|
||||
blocks[i] = BlockID.AIR;
|
||||
}
|
||||
}
|
||||
Arrays.fill(hasSections, false);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void write(NBTOutputStream nbtOut) throws IOException {
|
||||
@ -127,11 +249,10 @@ public class WritableMCAChunk {
|
||||
int num_palette = 0;
|
||||
try {
|
||||
for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) {
|
||||
int stateId = blocks[i];
|
||||
int ordinal = BlockState.getFromInternalId(stateId).getOrdinal(); // TODO fixme Remove all use of BlockTypes.BIT_OFFSET so that this conversion isn't necessary
|
||||
int ordinal = blocks[i];
|
||||
int palette = blockToPalette[ordinal];
|
||||
if (palette == Integer.MAX_VALUE) {
|
||||
BlockState state = BlockTypes.states[ordinal];
|
||||
// BlockState state = BlockTypesCache.states[ordinal];
|
||||
blockToPalette[ordinal] = palette = num_palette;
|
||||
paletteToBlock[num_palette] = ordinal;
|
||||
num_palette++;
|
||||
@ -149,7 +270,7 @@ public class WritableMCAChunk {
|
||||
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
int ordinal = paletteToBlock[i];
|
||||
BlockState state = BlockTypes.states[ordinal];
|
||||
BlockState state = BlockTypesCache.states[ordinal];
|
||||
BlockType type = state.getBlockType();
|
||||
out.writeNamedTag("Name", type.getId());
|
||||
|
||||
@ -192,13 +313,13 @@ public class WritableMCAChunk {
|
||||
}
|
||||
|
||||
|
||||
out.writeNamedTagName("BlockLight", NBTConstants.TYPE_BYTE_ARRAY);
|
||||
out.writeInt(2048);
|
||||
out.write(blockLight, layer << 11, 1 << 11);
|
||||
|
||||
out.writeNamedTagName("SkyLight", NBTConstants.TYPE_BYTE_ARRAY);
|
||||
out.writeInt(2048);
|
||||
out.write(skyLight, layer << 11, 1 << 11);
|
||||
// out.writeNamedTagName("BlockLight", NBTConstants.TYPE_BYTE_ARRAY);
|
||||
// out.writeInt(2048);
|
||||
// out.write(blockLight, layer << 11, 1 << 11);
|
||||
//
|
||||
// out.writeNamedTagName("SkyLight", NBTConstants.TYPE_BYTE_ARRAY);
|
||||
// out.writeInt(2048);
|
||||
// out.write(skyLight, layer << 11, 1 << 11);
|
||||
|
||||
|
||||
out.writeEndTag();
|
||||
@ -250,6 +371,15 @@ public class WritableMCAChunk {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
if (deleted) return true;
|
||||
for (boolean hasSection : hasSections) {
|
||||
if (hasSection) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isModified() {
|
||||
return modified != 0;
|
||||
}
|
||||
@ -272,14 +402,17 @@ public class WritableMCAChunk {
|
||||
return bitMask;
|
||||
}
|
||||
|
||||
public void setTile(int x, int y, int z, CompoundTag tile) {
|
||||
@Override
|
||||
public boolean setTile(int x, int y, int z, CompoundTag tile) {
|
||||
setModified();
|
||||
short pair = MathMan.tripleBlockCoord(x, y, z);
|
||||
if (tile != null) {
|
||||
tiles.put(pair, tile);
|
||||
tiles.put(x, y, z, tile);
|
||||
} else {
|
||||
tiles.remove(pair);
|
||||
if (tiles.remove(x, y, z) == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setEntity(CompoundTag entityTag) {
|
||||
@ -289,17 +422,39 @@ public class WritableMCAChunk {
|
||||
entities.put(new UUID(most, least), entityTag);
|
||||
}
|
||||
|
||||
public void setBiome(int x, int z, BiomeType biome) {
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int z) {
|
||||
return BiomeTypes.get(this.biomes[(z << 4) | x] & 0xFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType[] getBiomes() {
|
||||
BiomeType[] tmp = new BiomeType[256];
|
||||
for (int i = 0; i < 256; i++) {
|
||||
tmp[i] = BiomeTypes.get(this.biomes[i] & 0xFF);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBiome(BlockVector2 pos, BiomeType biome) {
|
||||
return this.setBiome(pos.getX(), 0, pos.getZ(), biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
||||
setModified();
|
||||
biomes[x + (z << 4)] = biome.getInternalId();
|
||||
biomes[x + (z << 4)] = (byte) biome.getInternalId();
|
||||
return true;
|
||||
}
|
||||
|
||||
public Set<CompoundTag> getEntities() {
|
||||
return new HashSet<>(entities.values());
|
||||
}
|
||||
|
||||
public Map<Short, CompoundTag> getTiles() {
|
||||
return tiles == null ? new HashMap<>() : tiles;
|
||||
@Override
|
||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||
return tiles == null ? Collections.emptyMap() : tiles;
|
||||
}
|
||||
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
@ -310,94 +465,51 @@ public class WritableMCAChunk {
|
||||
return tiles.get(pair);
|
||||
}
|
||||
|
||||
public boolean doesSectionExist(int cy) {
|
||||
return hasSections[cy];
|
||||
}
|
||||
|
||||
private final int getIndex(int x, int y, int z) {
|
||||
return x | (z << 4) | (y << 8);
|
||||
}
|
||||
|
||||
public int getBlockCombinedId(int x, int y, int z) {
|
||||
public int getBlockOrdinal(int x, int y, int z) {
|
||||
return blocks[x | (z << 4) | (y << 8)];
|
||||
}
|
||||
|
||||
public BiomeType[] getBiomeArray() {
|
||||
return null;
|
||||
@Override
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
int ordinal = getBlockOrdinal(x, y, z);
|
||||
return BlockState.getFromOrdinal(ordinal);
|
||||
}
|
||||
|
||||
public Set<UUID> getEntityRemoves() {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
public void setSkyLight(int x, int y, int z, int value) {
|
||||
setNibble(getIndex(x, y, z), skyLight, value);
|
||||
@Override
|
||||
public boolean setBlock(int x, int y, int z, BlockStateHolder holder) {
|
||||
setBlock(x, y, z, holder.getOrdinalChar());
|
||||
holder.applyTileEntity(this, x, y, z);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setBlockLight(int x, int y, int z, int value) {
|
||||
setNibble(getIndex(x, y, z), blockLight, value);
|
||||
}
|
||||
|
||||
public int getSkyLight(int x, int y, int z) {
|
||||
if (!hasSections[y >> 4]) {
|
||||
return 0;
|
||||
}
|
||||
return getNibble(getIndex(x, y, z), skyLight);
|
||||
}
|
||||
|
||||
public int getBlockLight(int x, int y, int z) {
|
||||
if (!hasSections[y >> 4]) {
|
||||
return 0;
|
||||
}
|
||||
return getNibble(getIndex(x, y, z), blockLight);
|
||||
}
|
||||
|
||||
public void setFullbright() {
|
||||
for (int layer = 0; layer < 16; layer++) {
|
||||
if (hasSections[layer]) {
|
||||
Arrays.fill(skyLight, layer << 7, ((layer + 1) << 7), (byte) 255);
|
||||
}
|
||||
@Override
|
||||
public void setBlocks(int layer, char[] data) {
|
||||
int offset = layer << 12;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
blocks[offset + i] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
public void removeLight() {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
removeLight(i);
|
||||
@Override
|
||||
public char[] getArray(int layer) {
|
||||
char[] tmp = FaweCache.IMP.SECTION_BITS_TO_CHAR.get();
|
||||
int offset = layer << 12;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
tmp[i] = blocks[offset + i];
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public void removeLight(int i) {
|
||||
if (hasSections[i]) {
|
||||
Arrays.fill(skyLight, i << 7, ((i + 1) << 7), (byte) 0);
|
||||
Arrays.fill(blockLight, i << 7, ((i + 1) << 7), (byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
public int getNibble(int index, byte[] array) {
|
||||
int indexShift = index >> 1;
|
||||
if ((index & 1) == 0) {
|
||||
return array[indexShift] & 15;
|
||||
} else {
|
||||
return array[indexShift] >> 4 & 15;
|
||||
}
|
||||
}
|
||||
|
||||
public void setNibble(int index, byte[] array, int value) {
|
||||
int indexShift = index >> 1;
|
||||
byte existing = array[indexShift];
|
||||
int valueShift = value << 4;
|
||||
if (existing == value + valueShift) {
|
||||
return;
|
||||
}
|
||||
if ((index & 1) == 0) {
|
||||
array[indexShift] = (byte) (existing & 240 | value);
|
||||
} else {
|
||||
array[indexShift] = (byte) (existing & 15 | valueShift);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBlock(int x, int y, int z, int combinedId) {
|
||||
blocks[getIndex(x, y, z)] = combinedId;
|
||||
public void setBlock(int x, int y, int z, char ordinal) {
|
||||
blocks[getIndex(x, y, z)] = ordinal;
|
||||
}
|
||||
|
||||
public void setBiome(BiomeType biome) {
|
||||
@ -407,4 +519,9 @@ public class WritableMCAChunk {
|
||||
public void removeEntity(UUID uuid) {
|
||||
entities.remove(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive) {
|
||||
return isEmpty();
|
||||
}
|
||||
}
|
@ -0,0 +1,708 @@
|
||||
package com.boydti.fawe.jnbt.anvil;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.beta.Trimable;
|
||||
import com.boydti.fawe.jnbt.streamer.StreamDelegate;
|
||||
import com.boydti.fawe.object.RunnableVal4;
|
||||
import com.boydti.fawe.object.collection.CleanableThreadLocal;
|
||||
import com.boydti.fawe.object.io.BufferedRandomAccessFile;
|
||||
import com.boydti.fawe.object.io.FastByteArrayInputStream;
|
||||
import com.boydti.fawe.util.MainUtil;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
/**
|
||||
* Chunk format: http://minecraft.gamepedia.com/Chunk_format#Entity_format
|
||||
* e.g.: `.Level.Entities.#` (Starts with a . as the root tag is unnamed)
|
||||
* Note: This class isn't thread safe. You can use it in an async thread, but not multiple at the same time
|
||||
*/
|
||||
public class MCAFile implements Trimable {
|
||||
|
||||
private static Field fieldBuf2;
|
||||
private static Field fieldBuf3;
|
||||
|
||||
static {
|
||||
try {
|
||||
fieldBuf2 = InflaterInputStream.class.getDeclaredField("buf");
|
||||
fieldBuf2.setAccessible(true);
|
||||
fieldBuf3 = NBTInputStream.class.getDeclaredField("buf");
|
||||
fieldBuf3.setAccessible(true);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private final ForkJoinPool pool;
|
||||
private final byte[] locations;
|
||||
private boolean readLocations;
|
||||
|
||||
private File file;
|
||||
private RandomAccessFile raf;
|
||||
|
||||
private boolean deleted;
|
||||
private int X, Z;
|
||||
private MCAChunk[] chunks;
|
||||
private boolean[] chunkInitialized;
|
||||
private Object[] locks;
|
||||
|
||||
final ThreadLocal<byte[]> byteStore1 = new ThreadLocal<byte[]>() {
|
||||
@Override
|
||||
protected byte[] initialValue() {
|
||||
return new byte[4096];
|
||||
}
|
||||
};
|
||||
final ThreadLocal<byte[]> byteStore2 = new ThreadLocal<byte[]>() {
|
||||
@Override
|
||||
protected byte[] initialValue() {
|
||||
return new byte[4096];
|
||||
}
|
||||
};
|
||||
final ThreadLocal<byte[]> byteStore3 = new ThreadLocal<byte[]>() {
|
||||
@Override
|
||||
protected byte[] initialValue() {
|
||||
return new byte[1024];
|
||||
}
|
||||
};
|
||||
|
||||
public MCAFile(ForkJoinPool pool) {
|
||||
this.pool = pool;
|
||||
this.locations = new byte[4096];
|
||||
this.chunks = new MCAChunk[32 * 32];
|
||||
this.chunkInitialized = new boolean[this.chunks.length];
|
||||
this.locks = new Object[this.chunks.length];
|
||||
for (int i = 0; i < locks.length; i++) {
|
||||
locks[i] = new Object();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive) {
|
||||
boolean hasChunk = false;
|
||||
for (int i = 0; i < chunkInitialized.length; i++) {
|
||||
if (!chunkInitialized[i]) {
|
||||
chunks[i] = null;
|
||||
} else {
|
||||
hasChunk = true;
|
||||
}
|
||||
}
|
||||
CleanableThreadLocal.clean(byteStore1);
|
||||
CleanableThreadLocal.clean(byteStore2);
|
||||
CleanableThreadLocal.clean(byteStore3);
|
||||
return !hasChunk;
|
||||
}
|
||||
|
||||
public MCAFile init(File file) throws FileNotFoundException {
|
||||
String[] split = file.getName().split("\\.");
|
||||
int X = Integer.parseInt(split[1]);
|
||||
int Z = Integer.parseInt(split[2]);
|
||||
return init(file, X, Z);
|
||||
}
|
||||
|
||||
public MCAFile init(File file, int mcrX, int mcrZ) throws FileNotFoundException {
|
||||
if (raf != null) {
|
||||
flush(true);
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
locations[i] = 0;
|
||||
}
|
||||
try {
|
||||
raf.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
raf = null;
|
||||
}
|
||||
deleted = false;
|
||||
Arrays.fill(chunkInitialized, false);
|
||||
readLocations = false;
|
||||
this.X = mcrX;
|
||||
this.Z = mcrZ;
|
||||
this.file = file;
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException(file.getName());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCAFile init(World world, int mcrX, int mcrZ) throws FileNotFoundException {
|
||||
return init(new File(world.getStoragePath().toFile(), File.separator + "regions" + File.separator + "r." + mcrX + "." + mcrZ + ".mca"));
|
||||
}
|
||||
|
||||
public int getIndex(int chunkX, int chunkZ) {
|
||||
return ((chunkX & 31) << 2) + ((chunkZ & 31) << 7);
|
||||
}
|
||||
|
||||
|
||||
private RandomAccessFile getRaf() throws FileNotFoundException {
|
||||
if (this.raf == null) {
|
||||
this.raf = new RandomAccessFile(file, "rw");
|
||||
}
|
||||
return this.raf;
|
||||
}
|
||||
|
||||
private void readHeader() throws IOException {
|
||||
if (!readLocations) {
|
||||
readLocations = true;
|
||||
getRaf();
|
||||
if (raf.length() < 8192) {
|
||||
raf.setLength(8192);
|
||||
} else {
|
||||
raf.seek(0);
|
||||
raf.readFully(locations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
if (raf != null) {
|
||||
try {
|
||||
raf.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
deleted = false;
|
||||
readLocations = false;
|
||||
Arrays.fill(chunkInitialized, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
CleanableThreadLocal.clean(byteStore1);
|
||||
CleanableThreadLocal.clean(byteStore2);
|
||||
CleanableThreadLocal.clean(byteStore3);
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
public void setDeleted(boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return X;
|
||||
}
|
||||
|
||||
public int getZ() {
|
||||
return Z;
|
||||
}
|
||||
|
||||
public RandomAccessFile getRandomAccessFile() {
|
||||
return raf;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public MCAChunk getCachedChunk(int cx, int cz) {
|
||||
int pair = getIndex(cx, cz);
|
||||
MCAChunk chunk = chunks[pair];
|
||||
if (chunk != null && chunkInitialized[pair]) {
|
||||
return chunk;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setChunk(MCAChunk chunk) {
|
||||
int cx = chunk.getX();
|
||||
int cz = chunk.getZ();
|
||||
int pair = getIndex(cx, cz);
|
||||
chunks[pair] = chunk;
|
||||
}
|
||||
|
||||
public MCAChunk getChunk(int cx, int cz) throws IOException {
|
||||
int pair = getIndex(cx, cz);
|
||||
MCAChunk chunk = chunks[pair];
|
||||
if (chunk == null) {
|
||||
Object lock = locks[pair];
|
||||
synchronized (lock) {
|
||||
chunk = chunks[pair];
|
||||
if (chunk == null) {
|
||||
chunk = new MCAChunk();
|
||||
chunk.setPosition(cx, cz);
|
||||
chunks[pair] = chunk;
|
||||
}
|
||||
}
|
||||
} else if (chunkInitialized[pair]) {
|
||||
return chunk;
|
||||
}
|
||||
synchronized (chunk) {
|
||||
if (!chunkInitialized[pair]) {
|
||||
readChunk(chunk, pair);
|
||||
chunkInitialized[pair] = true;
|
||||
}
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
private MCAChunk readChunk(MCAChunk chunk, int i) throws IOException {
|
||||
int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF))) << 12;
|
||||
if (offset == 0) {
|
||||
return null;
|
||||
}
|
||||
int size = (locations[i + 3] & 0xFF) << 12;
|
||||
try (NBTInputStream nis = getChunkIS(offset)) {
|
||||
chunk.read(nis, false);
|
||||
}
|
||||
System.out.println("TODO multithreaded"); // TODO
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* CX, CZ, OFFSET, SIZE
|
||||
*
|
||||
* @param onEach
|
||||
* @throws IOException
|
||||
*/
|
||||
public void forEachSortedChunk(RunnableVal4<Integer, Integer, Integer, Integer> onEach) throws IOException {
|
||||
char[] offsets = new char[(int) (raf.length() / 4096) - 2];
|
||||
Arrays.fill(offsets, Character.MAX_VALUE);
|
||||
char i = 0;
|
||||
for (int z = 0; z < 32; z++) {
|
||||
for (int x = 0; x < 32; x++, i += 4) {
|
||||
int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF))) - 2;
|
||||
int size = locations[i + 3] & 0xFF;
|
||||
if (size != 0) {
|
||||
if (offset < offsets.length) {
|
||||
offsets[offset] = i;
|
||||
} else {
|
||||
Fawe.debug("Ignoring invalid offset " + offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < offsets.length; i++) {
|
||||
int index = offsets[i];
|
||||
if (index != Character.MAX_VALUE) {
|
||||
int offset = i + 2;
|
||||
int size = locations[index + 3] & 0xFF;
|
||||
int index2 = index >> 2;
|
||||
int x = (index2) & 31;
|
||||
int z = (index2) >> 5;
|
||||
onEach.run(x, z, offset << 12, size << 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param onEach cx, cz, offset, size
|
||||
*/
|
||||
public void forEachChunk(RunnableVal4<Integer, Integer, Integer, Integer> onEach) {
|
||||
int i = 0;
|
||||
for (int z = 0; z < 32; z++) {
|
||||
for (int x = 0; x < 32; x++, i += 4) {
|
||||
int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF)));
|
||||
int size = locations[i + 3] & 0xFF;
|
||||
if (size != 0) {
|
||||
onEach.run(x, z, offset << 12, size << 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void forEachChunk(Consumer<MCAChunk> onEach) {
|
||||
int i = 0;
|
||||
for (int z = 0; z < 32; z++) {
|
||||
for (int x = 0; x < 32; x++, i += 4) {
|
||||
int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF)));
|
||||
int size = locations[i + 3] & 0xFF;
|
||||
if (size != 0) {
|
||||
try {
|
||||
onEach.accept(getChunk(x, z));
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getOffset(int cx, int cz) {
|
||||
int i = getIndex(cx, cz);
|
||||
int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF)));
|
||||
return offset << 12;
|
||||
}
|
||||
|
||||
public int getSize(int cx, int cz) {
|
||||
int i = getIndex(cx, cz);
|
||||
return (locations[i + 3] & 0xFF) << 12;
|
||||
}
|
||||
|
||||
public byte[] getChunkCompressedBytes(int offset) throws IOException {
|
||||
if (offset == 0) {
|
||||
return null;
|
||||
}
|
||||
synchronized (raf) {
|
||||
raf.seek(offset);
|
||||
int size = raf.readInt();
|
||||
int compression = raf.read();
|
||||
byte[] data = new byte[size];
|
||||
raf.readFully(data);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
private NBTInputStream getChunkIS(int offset) throws IOException {
|
||||
try {
|
||||
byte[] data = getChunkCompressedBytes(offset);
|
||||
FastByteArrayInputStream bais = new FastByteArrayInputStream(data);
|
||||
InflaterInputStream iis = new InflaterInputStream(bais, new Inflater(), 1);
|
||||
fieldBuf2.set(iis, byteStore2.get());
|
||||
BufferedInputStream bis = new BufferedInputStream(iis);
|
||||
NBTInputStream nis = new NBTInputStream(bis);
|
||||
fieldBuf3.set(nis, byteStore3.get());
|
||||
return nis;
|
||||
} catch (IllegalAccessException unlikely) {
|
||||
unlikely.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void streamChunk(int cx, int cz, StreamDelegate delegate) throws IOException {
|
||||
streamChunk(getOffset(cx, cz), delegate);
|
||||
}
|
||||
|
||||
public void streamChunk(int offset, StreamDelegate delegate) throws IOException {
|
||||
byte[] data = getChunkCompressedBytes(offset);
|
||||
streamChunk(data, delegate);
|
||||
}
|
||||
|
||||
public void streamChunk(byte[] data, StreamDelegate delegate) throws IOException {
|
||||
if (data != null) {
|
||||
try {
|
||||
FastByteArrayInputStream bais = new FastByteArrayInputStream(data);
|
||||
InflaterInputStream iis = new InflaterInputStream(bais, new Inflater(), 1);
|
||||
fieldBuf2.set(iis, byteStore2.get());
|
||||
BufferedInputStream bis = new BufferedInputStream(iis);
|
||||
NBTInputStream nis = new NBTInputStream(bis);
|
||||
fieldBuf3.set(nis, byteStore3.get());
|
||||
nis.readNamedTagLazy(delegate);
|
||||
} catch (IllegalAccessException unlikely) {
|
||||
unlikely.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param onEach chunk
|
||||
*/
|
||||
public void forEachCachedChunk(Consumer<MCAChunk> onEach) {
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
MCAChunk chunk = chunks[i];
|
||||
if (chunk != null && this.chunkInitialized[i]) {
|
||||
onEach.accept(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<MCAChunk> getCachedChunks() {
|
||||
int size = 0;
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
if (chunks[i] != null && this.chunkInitialized[i]) size++;
|
||||
}
|
||||
ArrayList<MCAChunk> list = new ArrayList<>(size);
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
MCAChunk chunk = chunks[i];
|
||||
if (chunk != null && this.chunkInitialized[i]) {
|
||||
list.add(chunk);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private byte[] toBytes(MCAChunk chunk) throws Exception {
|
||||
if (chunk.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
byte[] uncompressed = chunk.toBytes(byteStore3.get());
|
||||
byte[] compressed = MainUtil.compress(uncompressed, byteStore2.get(), null);
|
||||
return compressed;
|
||||
}
|
||||
|
||||
private byte[] getChunkBytes(int cx, int cz) throws Exception {
|
||||
MCAChunk mca = getCachedChunk(cx, cz);
|
||||
if (mca == null) {
|
||||
int offset = getOffset(cx, cz);
|
||||
if (offset == 0) {
|
||||
return null;
|
||||
}
|
||||
return getChunkCompressedBytes(offset);
|
||||
}
|
||||
return toBytes(mca);
|
||||
}
|
||||
|
||||
|
||||
private void writeSafe(RandomAccessFile raf, int offset, byte[] data) throws IOException {
|
||||
int len = data.length + 5;
|
||||
raf.seek(offset);
|
||||
if (raf.length() - offset < len) {
|
||||
raf.setLength(((offset + len + 4095) / 4096) * 4096);
|
||||
}
|
||||
// Length of remaining data
|
||||
raf.writeInt(data.length + 1);
|
||||
// Compression type
|
||||
raf.write(2);
|
||||
raf.write(data);
|
||||
}
|
||||
|
||||
private void writeHeader(RandomAccessFile raf, int cx, int cz, int offsetMedium, int sizeByte, boolean writeTime) throws IOException {
|
||||
int i = getIndex(cx, cz);
|
||||
locations[i] = (byte) (offsetMedium >> 16);
|
||||
locations[i + 1] = (byte) (offsetMedium >> 8);
|
||||
locations[i + 2] = (byte) (offsetMedium);
|
||||
locations[i + 3] = (byte) sizeByte;
|
||||
raf.seek(i);
|
||||
raf.write((offsetMedium >> 16));
|
||||
raf.write((offsetMedium >> 8));
|
||||
raf.write((offsetMedium >> 0));
|
||||
raf.write(sizeByte);
|
||||
raf.seek(i + 4096);
|
||||
if (offsetMedium == 0 && sizeByte == 0) {
|
||||
raf.writeInt(0);
|
||||
} else {
|
||||
raf.writeInt((int) (System.currentTimeMillis() / 1000L));
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (raf == null) return;
|
||||
synchronized (raf) {
|
||||
if (raf != null) {
|
||||
flush(true);
|
||||
try {
|
||||
raf.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
raf = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isModified() {
|
||||
if (isDeleted()) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
MCAChunk chunk = chunks[i];
|
||||
if (chunk != null && this.chunkInitialized[i]) {
|
||||
if (chunk.isModified() || chunk.isDeleted()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the chunk to the file
|
||||
* @param wait - If the flush method needs to wait for the pool
|
||||
*/
|
||||
public void flush(boolean wait) {
|
||||
synchronized (raf) {
|
||||
// If the file is marked as deleted, nothing is written
|
||||
if (isDeleted()) {
|
||||
clear();
|
||||
file.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
// Chunks that need to be relocated
|
||||
Int2ObjectOpenHashMap<byte[]> relocate = new Int2ObjectOpenHashMap<>();
|
||||
// The position of each chunk
|
||||
final Int2ObjectOpenHashMap<Integer> offsetMap = new Int2ObjectOpenHashMap<>(); // Offset -> <byte cx, byte cz, short size>
|
||||
// The data of each modified chunk
|
||||
final Int2ObjectOpenHashMap<byte[]> compressedMap = new Int2ObjectOpenHashMap<>();
|
||||
// The data of each chunk that needs to be moved
|
||||
final Int2ObjectOpenHashMap<byte[]> append = new Int2ObjectOpenHashMap<>();
|
||||
boolean[] modified = new boolean[1];
|
||||
// Get the current time for the chunk timestamp
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
// Load the chunks into the append or compressed map
|
||||
final ForkJoinPool finalPool = this.pool;
|
||||
forEachCachedChunk(chunk -> {
|
||||
if (chunk.isModified() || chunk.isDeleted()) {
|
||||
modified[0] = true;
|
||||
chunk.setLastUpdate(now);
|
||||
if (!chunk.isDeleted()) {
|
||||
MCAFile.this.pool.submit(() -> {
|
||||
try {
|
||||
byte[] compressed = toBytes(chunk);
|
||||
int pair = MathMan.pair((short) (chunk.getX() & 31), (short) (chunk.getZ() & 31));
|
||||
Int2ObjectOpenHashMap map;
|
||||
if (getOffset(chunk.getX(), chunk.getZ()) == 0) {
|
||||
map = append;
|
||||
} else {
|
||||
map = compressedMap;
|
||||
}
|
||||
synchronized (map) {
|
||||
map.put(pair, compressed);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// If any changes were detected
|
||||
if (modified[0]) {
|
||||
file.setLastModified(now);
|
||||
|
||||
// Load the offset data into the offset map
|
||||
forEachChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
|
||||
@Override
|
||||
public void run(Integer cx, Integer cz, Integer offset, Integer size) {
|
||||
short pair1 = MathMan.pairByte((byte) (cx & 31), (byte) (cz & 31));
|
||||
short pair2 = (short) (size >> 12);
|
||||
offsetMap.put((int) offset, (Integer) MathMan.pair(pair1, pair2));
|
||||
}
|
||||
});
|
||||
// Wait for previous tasks
|
||||
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
|
||||
|
||||
|
||||
int start = 8192;
|
||||
int written = start;
|
||||
int end = 8192;
|
||||
int nextOffset = 8192;
|
||||
try {
|
||||
for (int count = 0; count < offsetMap.size(); count++) {
|
||||
// Get the previous position of the next chunk
|
||||
Integer loc = offsetMap.get(nextOffset);
|
||||
while (loc == null) {
|
||||
nextOffset += 4096;
|
||||
loc = offsetMap.get(nextOffset);
|
||||
}
|
||||
int offset = nextOffset;
|
||||
|
||||
// Get the x/z from the paired location
|
||||
short cxz = MathMan.unpairX(loc);
|
||||
int cx = MathMan.unpairShortX(cxz);
|
||||
int cz = MathMan.unpairShortY(cxz);
|
||||
|
||||
// Get the size from the pair
|
||||
int size = MathMan.unpairY(loc) << 12;
|
||||
|
||||
nextOffset += size;
|
||||
end = Math.min(start + size, end);
|
||||
int pair = getIndex(cx, cz);
|
||||
byte[] newBytes = relocate.get(pair);
|
||||
|
||||
// newBytes is null if the chunk isn't modified or marked for moving
|
||||
if (newBytes == null) {
|
||||
MCAChunk cached = getCachedChunk(cx, cz);
|
||||
// If the previous offset marks the current write position (start) then we only write the header
|
||||
if (offset == start) {
|
||||
if (cached == null || !cached.isModified()) {
|
||||
writeHeader(raf, cx, cz, start >> 12, size >> 12, true);
|
||||
start += size;
|
||||
written = start + size;
|
||||
continue;
|
||||
} else {
|
||||
newBytes = compressedMap.get(pair);
|
||||
}
|
||||
} else {
|
||||
// The chunk needs to be moved, fetch the data if necessary
|
||||
newBytes = compressedMap.get(pair);
|
||||
if (newBytes == null) {
|
||||
if (cached == null || !cached.isDeleted()) {
|
||||
newBytes = getChunkCompressedBytes(getOffset(cx, cz));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newBytes == null) {
|
||||
writeHeader(raf, cx, cz, 0, 0, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// The length to be written (compressed data + 5 byte chunk header)
|
||||
int len = newBytes.length + 5;
|
||||
int oldSize = (size + 4095) >> 12;
|
||||
int newSize = (len + 4095) >> 12;
|
||||
int nextOffset2 = end;
|
||||
|
||||
// If the current write position (start) + length of data to write (len) are longer than the position of the next chunk, we need to move the next chunks
|
||||
while (start + len > end) {
|
||||
Integer nextLoc = offsetMap.get(nextOffset2);
|
||||
if (nextLoc != null) {
|
||||
short nextCXZ = MathMan.unpairX(nextLoc);
|
||||
int nextCX = MathMan.unpairShortX(nextCXZ);
|
||||
int nextCZ = MathMan.unpairShortY(nextCXZ);
|
||||
MCAChunk cached = getCachedChunk(nextCX, nextCZ);
|
||||
if (cached == null || !cached.isModified()) {
|
||||
byte[] nextBytes = getChunkCompressedBytes(nextOffset2);
|
||||
relocate.put(MathMan.pair((short) (nextCX & 31), (short) (nextCZ & 31)), nextBytes);
|
||||
}
|
||||
int nextSize = MathMan.unpairY(nextLoc) << 12;
|
||||
end += nextSize;
|
||||
nextOffset2 += nextSize;
|
||||
} else {
|
||||
end += 4096;
|
||||
nextOffset2 += 4096;
|
||||
}
|
||||
}
|
||||
// Write the chunk + chunk header
|
||||
writeSafe(raf, start, newBytes);
|
||||
// Write the location data (beginning of file)
|
||||
writeHeader(raf, cx, cz, start >> 12, newSize, true);
|
||||
|
||||
written = start + newBytes.length + 5;
|
||||
start += newSize << 12;
|
||||
}
|
||||
|
||||
// Write all the chunks which need to be appended
|
||||
if (!append.isEmpty()) {
|
||||
for (Int2ObjectMap.Entry<byte[]> entry : append.int2ObjectEntrySet()) {
|
||||
int pair = entry.getIntKey();
|
||||
short cx = MathMan.unpairX(pair);
|
||||
short cz = MathMan.unpairY(pair);
|
||||
byte[] bytes = entry.getValue();
|
||||
int len = bytes.length + 5;
|
||||
int newSize = (len + 4095) >> 12;
|
||||
writeSafe(raf, start, bytes);
|
||||
writeHeader(raf, cx, cz, start >> 12, newSize, true);
|
||||
written = start + bytes.length + 5;
|
||||
start += newSize << 12;
|
||||
}
|
||||
}
|
||||
// Round the file length, since the vanilla server doesn't like it for some reason
|
||||
raf.setLength(4096 * ((written + 4095) / 4096));
|
||||
if (raf instanceof BufferedRandomAccessFile) {
|
||||
((BufferedRandomAccessFile) raf).flush();
|
||||
}
|
||||
raf.close();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (wait) {
|
||||
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.boydti.fawe.jnbt.anvil.mcatest;
|
||||
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MCATest {
|
||||
public MCATest() throws IOException {
|
||||
File file = new File("plugins/FastAsyncWorldEdit/tobitower.schematic");
|
||||
Clipboard loaded = ClipboardFormats.findByFile(file).load(file);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface InfoReader extends StreamReader<Integer> {
|
||||
void apply(int length, int type) throws IOException;
|
||||
|
||||
@Override
|
||||
default void apply(int i, Integer value) throws IOException {
|
||||
apply(i, value.intValue());
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IntValueReader extends ValueReader<Integer> {
|
||||
void applyInt(int index, int value) throws IOException;
|
||||
|
||||
@Override
|
||||
default void apply(int index, Integer value) throws IOException {
|
||||
applyInt(index, value);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public interface LazyReader extends StreamReader<DataInputStream> {
|
||||
void apply(int index, NBTInputStream stream) throws IOException;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface LongValueReader extends ValueReader<Long> {
|
||||
void applyLong(int index, long value) throws IOException;
|
||||
|
||||
@Override
|
||||
default void apply(int index, Long value) throws IOException {
|
||||
applyLong(index, value);
|
||||
}
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
import com.sk89q.jnbt.NBTConstants;
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class StreamDelegate {
|
||||
private static final byte[][] ZERO_KEYS = new byte[0][];
|
||||
private static final StreamDelegate[] ZERO_VALUES = new StreamDelegate[0];
|
||||
|
||||
private byte[] buffer;
|
||||
private byte[][] keys;
|
||||
private StreamDelegate[] values;
|
||||
|
||||
private LazyReader lazyReader;
|
||||
private ValueReader elemReader;
|
||||
private InfoReader infoReader;
|
||||
private ValueReader valueReader;
|
||||
|
||||
public StreamDelegate() {
|
||||
keys = ZERO_KEYS;
|
||||
values = ZERO_VALUES;
|
||||
}
|
||||
|
||||
public StreamDelegate addAndGetParent(String name) {
|
||||
add(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamDelegate add() {
|
||||
return add("");
|
||||
}
|
||||
|
||||
public StreamDelegate add(String name) {
|
||||
return add(name, new StreamDelegate());
|
||||
}
|
||||
|
||||
private StreamDelegate add(String name, StreamDelegate scope) {
|
||||
if (valueReader != null) {
|
||||
System.out.println("Scope " + name + " | " + scope + " may not run, as the stream is only read once, and a value reader is already set");
|
||||
}
|
||||
byte[] bytes = name.getBytes(NBTConstants.CHARSET);
|
||||
int maxSize = bytes.length;
|
||||
|
||||
byte[][] tmpKeys = new byte[keys.length + 1][];
|
||||
StreamDelegate[] tmpValues = new StreamDelegate[keys.length + 1];
|
||||
tmpKeys[keys.length] = bytes;
|
||||
tmpValues[keys.length] = scope;
|
||||
|
||||
int i = 0;
|
||||
for (; i < keys.length; i++) {
|
||||
byte[] key = keys[i];
|
||||
if (key.length >= bytes.length) {
|
||||
tmpKeys[i] = bytes;
|
||||
tmpValues[i] = scope;
|
||||
break;
|
||||
}
|
||||
tmpKeys[i] = key;
|
||||
tmpValues[i] = values[i];
|
||||
maxSize = Math.max(maxSize, key.length);
|
||||
}
|
||||
for (; i < keys.length; i++) {
|
||||
byte[] key = keys[i];
|
||||
tmpKeys[i + 1] = key;
|
||||
tmpValues[i + 1] = values[i];
|
||||
maxSize = Math.max(maxSize, key.length);
|
||||
}
|
||||
|
||||
this.keys = tmpKeys;
|
||||
this.values = tmpValues;
|
||||
if (this.buffer == null || buffer.length < maxSize) {
|
||||
buffer = new byte[maxSize];
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
public StreamDelegate get0() {
|
||||
if (keys.length > 0 && keys[0].length == 0) {
|
||||
return values[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public StreamDelegate get(DataInputStream is) throws IOException {
|
||||
int nameLength = is.readShort() & 0xFFFF;
|
||||
if (nameLength == 0 && keys.length > 0 && keys[0].length == 0) {
|
||||
return values[0];
|
||||
}
|
||||
if (nameLength > buffer.length) {
|
||||
is.skipBytes(nameLength);
|
||||
return null;
|
||||
}
|
||||
int index = 0;
|
||||
outer:
|
||||
switch (keys.length) {
|
||||
case 0:
|
||||
break;
|
||||
default: {
|
||||
for (; index < keys.length; index++) {
|
||||
byte[] key = keys[index];
|
||||
if (key.length < nameLength) continue;
|
||||
if (key.length == nameLength) {
|
||||
break;
|
||||
} else {
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
if (index != keys.length - 1) {
|
||||
int max;
|
||||
for (max = index + 1; max < keys.length;) {
|
||||
byte[] key = keys[max];
|
||||
if (key.length == nameLength) {
|
||||
max++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (index != max) {
|
||||
is.readFully(buffer, 0, nameLength);
|
||||
middle:
|
||||
for (int i = index; i < max; i++) {
|
||||
byte[] key = keys[i];
|
||||
for (int j = 0; j < nameLength; j++) {
|
||||
if (buffer[j] != key[j]) {
|
||||
continue middle;
|
||||
}
|
||||
}
|
||||
return values[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
case 1: {
|
||||
byte[] key = keys[index];
|
||||
if (key.length == nameLength) {
|
||||
int i = 0;
|
||||
for (; nameLength > 0; nameLength--, i++) {
|
||||
byte b = is.readByte();
|
||||
buffer[i] = b;
|
||||
if (b != key[i]) {
|
||||
nameLength--;
|
||||
break outer;
|
||||
}
|
||||
|
||||
}
|
||||
return values[index];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
is.skipBytes(nameLength);
|
||||
return null;
|
||||
}
|
||||
|
||||
public StreamDelegate withLong(LongValueReader valueReader) {
|
||||
return withElem(valueReader);
|
||||
}
|
||||
|
||||
public StreamDelegate withInt(IntValueReader valueReader) {
|
||||
return withElem(valueReader);
|
||||
}
|
||||
|
||||
public StreamDelegate withValue(ValueReader valueReader) {
|
||||
if (keys.length != 0) {
|
||||
System.out.println("Reader " + valueReader + " may not run, as the stream is only read once, and a value reader is already set");
|
||||
}
|
||||
this.valueReader = valueReader;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamDelegate withStream(LazyReader lazyReader) {
|
||||
this.lazyReader = lazyReader;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamDelegate withElem(ValueReader elemReader) {
|
||||
this.elemReader = elemReader;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamDelegate withInfo(InfoReader infoReader) {
|
||||
this.infoReader = infoReader;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void acceptRoot(NBTInputStream is, int type, int depth) throws IOException {
|
||||
if (lazyReader != null) {
|
||||
lazyReader.apply(0, is);
|
||||
} else if (elemReader != null) {
|
||||
Object raw = is.readTagPayloadRaw(type, depth);
|
||||
elemReader.apply(0, raw);
|
||||
} else if (valueReader != null) {
|
||||
Object raw = is.readTagPayloadRaw(type, depth);
|
||||
valueReader.apply(0, raw);
|
||||
} else {
|
||||
is.readTagPaylodLazy(type, depth + 1, this);
|
||||
}
|
||||
}
|
||||
|
||||
public ValueReader getValueReader() {
|
||||
return valueReader;
|
||||
}
|
||||
|
||||
public ValueReader getElemReader() {
|
||||
return elemReader;
|
||||
}
|
||||
|
||||
public void acceptInfo(int length, int type) throws IOException {
|
||||
if (infoReader != null) {
|
||||
infoReader.apply(length, type);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean acceptLazy(int length, NBTInputStream is) throws IOException {
|
||||
if (lazyReader != null) {
|
||||
lazyReader.apply(length, is);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface StreamReader<T> {
|
||||
void apply(int i, T value) throws IOException;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.boydti.fawe.jnbt.streamer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface ValueReader<T> extends StreamReader<T> {
|
||||
void apply(int index, T value) throws IOException;
|
||||
|
||||
default void applyInt(int index, int value) throws IOException {
|
||||
apply(index, (T) (Integer) value);
|
||||
}
|
||||
|
||||
default void applyLong(int index, long value) throws IOException {
|
||||
apply(index, (T) (Long) value);
|
||||
}
|
||||
|
||||
default void applyFloat(int index, float value) throws IOException {
|
||||
apply(index, (T) (Float) value);
|
||||
}
|
||||
|
||||
default void applyDouble(int index, double value) throws IOException {
|
||||
apply(index, (T) (Double) value);
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package com.boydti.fawe.object;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class FaweLimit {
|
||||
@ -51,6 +53,47 @@ public class FaweLimit {
|
||||
public boolean MAX_ITERATIONS() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnlimited() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void THROW_MAX_CHANGES() {
|
||||
}
|
||||
|
||||
public void THROW_MAX_FAILS() {
|
||||
}
|
||||
|
||||
public void THROW_MAX_CHECKS() {
|
||||
}
|
||||
|
||||
public void THROW_MAX_ITERATIONS() {
|
||||
}
|
||||
|
||||
public void THROW_MAX_BLOCKSTATES() {
|
||||
}
|
||||
|
||||
public void THROW_MAX_ENTITIES() {
|
||||
}
|
||||
|
||||
public void THROW_MAX_CHANGES(int amt) {
|
||||
}
|
||||
|
||||
public void THROW_MAX_FAILS(int amt) {
|
||||
}
|
||||
|
||||
public void THROW_MAX_CHECKS(int amt) {
|
||||
}
|
||||
|
||||
public void THROW_MAX_ITERATIONS(int amt) {
|
||||
}
|
||||
|
||||
public void THROW_MAX_BLOCKSTATES(int amt) {
|
||||
}
|
||||
|
||||
public void THROW_MAX_ENTITIES(int amt) {
|
||||
}
|
||||
};
|
||||
MAX.SPEED_REDUCTION = 0;
|
||||
MAX.INVENTORY_MODE = 0;
|
||||
@ -92,6 +135,54 @@ public class FaweLimit {
|
||||
return MAX_ENTITIES-- > 0;
|
||||
}
|
||||
|
||||
public void THROW_MAX_CHANGES() {
|
||||
if (MAX_CHANGES-- <= 0) throw FaweCache.MAX_CHANGES;
|
||||
}
|
||||
|
||||
public void THROW_MAX_FAILS() {
|
||||
if (MAX_FAILS-- <= 0) throw FaweCache.MAX_CHECKS;
|
||||
}
|
||||
|
||||
public void THROW_MAX_CHECKS() {
|
||||
if (MAX_CHECKS-- <= 0) throw FaweCache.MAX_CHECKS;
|
||||
}
|
||||
|
||||
public void THROW_MAX_ITERATIONS() {
|
||||
if (MAX_ITERATIONS-- <= 0) throw FaweCache.MAX_ITERATIONS;
|
||||
}
|
||||
|
||||
public void THROW_MAX_BLOCKSTATES() {
|
||||
if (MAX_BLOCKSTATES-- <= 0) throw FaweCache.MAX_TILES;
|
||||
}
|
||||
|
||||
public void THROW_MAX_ENTITIES() {
|
||||
if (MAX_ENTITIES-- <= 0) throw FaweCache.MAX_ENTITIES;
|
||||
}
|
||||
|
||||
public void THROW_MAX_CHANGES(int amt) {
|
||||
if ((MAX_CHANGES -= amt) <= 0) throw FaweCache.MAX_CHANGES;
|
||||
}
|
||||
|
||||
public void THROW_MAX_FAILS(int amt) {
|
||||
if ((MAX_FAILS -= amt) <= 0) throw FaweCache.MAX_CHECKS;
|
||||
}
|
||||
|
||||
public void THROW_MAX_CHECKS(int amt) {
|
||||
if ((MAX_CHECKS -= amt) <= 0) throw FaweCache.MAX_CHECKS;
|
||||
}
|
||||
|
||||
public void THROW_MAX_ITERATIONS(int amt) {
|
||||
if ((MAX_ITERATIONS -= amt) <= 0) throw FaweCache.MAX_ITERATIONS;
|
||||
}
|
||||
|
||||
public void THROW_MAX_BLOCKSTATES(int amt) {
|
||||
if ((MAX_BLOCKSTATES -= amt) <= 0) throw FaweCache.MAX_TILES;
|
||||
}
|
||||
|
||||
public void THROW_MAX_ENTITIES(int amt) {
|
||||
if ((MAX_ENTITIES -= amt) <= 0) throw FaweCache.MAX_ENTITIES;
|
||||
}
|
||||
|
||||
public boolean isUnlimited() {
|
||||
return MAX_CHANGES == Integer.MAX_VALUE &&
|
||||
MAX_FAILS == Integer.MAX_VALUE &&
|
||||
|
@ -0,0 +1,8 @@
|
||||
package com.boydti.fawe.object;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface Lazy<T> extends Supplier<T> {
|
||||
Supplier<T> init();
|
||||
public default T get() { return init().get(); }
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
package com.boydti.fawe.object.brush;
|
||||
|
||||
import com.boydti.fawe.object.clipboard.CPUOptimizedClipboard;
|
||||
import com.boydti.fawe.object.clipboard.FaweClipboard;
|
||||
import com.boydti.fawe.object.clipboard.OffsetFaweClipboard;
|
||||
import com.boydti.fawe.object.clipboard.LinearClipboard;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.command.tool.brush.Brush;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.util.Direction;
|
||||
@ -39,23 +39,23 @@ public class ErodeBrush implements Brush {
|
||||
public void erosion(final EditSession es, int erodeFaces, int erodeRec, int fillFaces, int fillRec, BlockVector3 target, double size) {
|
||||
int brushSize = (int) size + 1;
|
||||
int brushSizeSquared = (int) (size * size);
|
||||
int dimension = brushSize * 2 + 1;
|
||||
FaweClipboard buffer1 = new OffsetFaweClipboard(new CPUOptimizedClipboard(dimension, dimension, dimension), brushSize);
|
||||
FaweClipboard buffer2 = new OffsetFaweClipboard(new CPUOptimizedClipboard(dimension, dimension, dimension), brushSize);
|
||||
BlockVector3 dimension = BlockVector3.ONE.multiply(brushSize * 2 + 1);
|
||||
Clipboard buffer1 = new CPUOptimizedClipboard(dimension);
|
||||
Clipboard buffer2 = new CPUOptimizedClipboard(dimension);
|
||||
|
||||
final int bx = target.getBlockX();
|
||||
final int by = target.getBlockY();
|
||||
final int bz = target.getBlockZ();
|
||||
|
||||
for (int x = -brushSize; x <= brushSize; x++) {
|
||||
for (int x = -brushSize, relx = 0; x <= brushSize; x++, relx++) {
|
||||
int x0 = x + bx;
|
||||
for (int y = -brushSize; y <= brushSize; y++) {
|
||||
for (int y = -brushSize, rely = 0; y <= brushSize; y++, rely++) {
|
||||
int y0 = y + by;
|
||||
for (int z = -brushSize; z <= brushSize; z++) {
|
||||
for (int z = -brushSize, relz = 0; z <= brushSize; z++, relz++) {
|
||||
int z0 = z + bz;
|
||||
BlockState state = es.getBlock(x0, y0, z0);
|
||||
buffer1.setBlock(x, y, z, state);
|
||||
buffer2.setBlock(x, y, z, state);
|
||||
buffer1.setBlock(relx, rely, relz, state);
|
||||
buffer2.setBlock(relx, rely, relz, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -70,30 +70,28 @@ public class ErodeBrush implements Brush {
|
||||
fillIteration(brushSize, brushSizeSquared, fillFaces, swap % 2 == 0 ? buffer1 : buffer2, swap % 2 == 1 ? buffer1 : buffer2);
|
||||
swap++;
|
||||
}
|
||||
FaweClipboard finalBuffer = swap % 2 == 0 ? buffer1 : buffer2;
|
||||
Clipboard finalBuffer = swap % 2 == 0 ? buffer1 : buffer2;
|
||||
|
||||
finalBuffer.forEach(new FaweClipboard.BlockReader() {
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> void run(int x, int y, int z, B block) {
|
||||
es.setBlock(x + bx, y + by, z + bz, block);
|
||||
}
|
||||
}, true);
|
||||
for (BlockVector3 pos : finalBuffer) {
|
||||
BlockState block = pos.getBlock(finalBuffer);
|
||||
es.setBlock(pos.getX() + bx - brushSize, pos.getY() + by - brushSize, pos.getZ() + bz - brushSize, block);
|
||||
}
|
||||
}
|
||||
|
||||
private void fillIteration(int brushSize, int brushSizeSquared, int fillFaces,
|
||||
FaweClipboard current, FaweClipboard target) {
|
||||
Clipboard current, Clipboard target) {
|
||||
int[] frequency = null;
|
||||
for (int x = -brushSize; x <= brushSize; x++) {
|
||||
for (int x = -brushSize, relx = 0; x <= brushSize; x++, relx++) {
|
||||
int x2 = x * x;
|
||||
for (int z = -brushSize; z <= brushSize; z++) {
|
||||
for (int z = -brushSize, relz = 0; z <= brushSize; z++, relz++) {
|
||||
int x2y2 = x2 + z * z;
|
||||
for (int y = -brushSize; y <= brushSize; y++) {
|
||||
for (int y = -brushSize, rely = 0; y <= brushSize; y++, rely++) {
|
||||
int cube = x2y2 + y * y;
|
||||
target.setBlock(x, y, z, current.getBlock(x, y, z));
|
||||
target.setBlock(x, y, z, current.getBlock(relx, rely, relz));
|
||||
if (cube >= brushSizeSquared) {
|
||||
continue;
|
||||
}
|
||||
BaseBlock state = current.getBlock(x, y, z);
|
||||
BaseBlock state = current.getFullBlock(relx, rely, relz);
|
||||
if (state.getBlockType().getMaterial().isMovementBlocker()) {
|
||||
continue;
|
||||
}
|
||||
@ -106,7 +104,7 @@ public class ErodeBrush implements Brush {
|
||||
Arrays.fill(frequency, 0);
|
||||
}
|
||||
for (BlockVector3 offs : FACES_TO_CHECK) {
|
||||
BaseBlock next = current.getBlock(x + offs.getBlockX(), y + offs.getBlockY(), z + offs.getBlockZ());
|
||||
BaseBlock next = current.getFullBlock(relx + offs.getBlockX(), rely + offs.getBlockY(), relz + offs.getBlockZ());
|
||||
if (!next.getBlockType().getMaterial().isMovementBlocker()) {
|
||||
continue;
|
||||
}
|
||||
@ -118,7 +116,7 @@ public class ErodeBrush implements Brush {
|
||||
}
|
||||
}
|
||||
if (total >= fillFaces) {
|
||||
target.setBlock(x, y, z, highestState);
|
||||
target.setBlock(relx, rely, relz, highestState);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -126,19 +124,20 @@ public class ErodeBrush implements Brush {
|
||||
}
|
||||
|
||||
private void erosionIteration(int brushSize, int brushSizeSquared, int erodeFaces,
|
||||
FaweClipboard current, FaweClipboard target) {
|
||||
Clipboard current, Clipboard target) {
|
||||
int[] frequency = null;
|
||||
for (int x = -brushSize; x <= brushSize; x++) {
|
||||
|
||||
for (int x = -brushSize, relx = 0; x <= brushSize; x++, relx++) {
|
||||
int x2 = x * x;
|
||||
for (int z = -brushSize; z <= brushSize; z++) {
|
||||
for (int z = -brushSize, relz = 0; z <= brushSize; z++, relz++) {
|
||||
int x2y2 = x2 + z * z;
|
||||
for (int y = -brushSize; y <= brushSize; y++) {
|
||||
for (int y = -brushSize, rely = 0; y <= brushSize; y++, rely++) {
|
||||
int cube = x2y2 + y * y;
|
||||
target.setBlock(x, y, z, current.getBlock(x, y, z));
|
||||
target.setBlock(x, y, z, current.getBlock(relx, rely, relz));
|
||||
if (cube >= brushSizeSquared) {
|
||||
continue;
|
||||
}
|
||||
BaseBlock state = current.getBlock(x, y, z);
|
||||
BaseBlock state = current.getFullBlock(relx, rely, relz);
|
||||
if (!state.getBlockType().getMaterial().isMovementBlocker()) {
|
||||
continue;
|
||||
}
|
||||
@ -151,7 +150,7 @@ public class ErodeBrush implements Brush {
|
||||
Arrays.fill(frequency, 0);
|
||||
}
|
||||
for (BlockVector3 offs : FACES_TO_CHECK) {
|
||||
BaseBlock next = current.getBlock(x + offs.getBlockX(), y + offs.getBlockY(), z + offs.getBlockZ());
|
||||
BaseBlock next = current.getFullBlock(relx + offs.getBlockX(), rely + offs.getBlockY(), relz + offs.getBlockZ());
|
||||
if (next.getBlockType().getMaterial().isMovementBlocker()) {
|
||||
continue;
|
||||
}
|
||||
@ -163,7 +162,7 @@ public class ErodeBrush implements Brush {
|
||||
}
|
||||
}
|
||||
if (total >= erodeFaces) {
|
||||
target.setBlock(x, y, z, highestState);
|
||||
target.setBlock(relx, rely, relz, highestState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,6 @@ public class VisualExtent extends AbstractDelegateExtent {
|
||||
@Override
|
||||
public Operation commit() {
|
||||
IQueueExtent queue = (IQueueExtent) getExtent();
|
||||
queue.sendBlockUpdates(this.player);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -59,7 +58,6 @@ public class VisualExtent extends AbstractDelegateExtent {
|
||||
|
||||
public void clear() {
|
||||
IQueueExtent queue = (IQueueExtent) getExtent();
|
||||
queue.clearBlockUpdates(player);
|
||||
queue.cancel();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,142 @@
|
||||
package com.boydti.fawe.object.brush.visualization.cfi;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.TextureUtil;
|
||||
import com.sk89q.worldedit.world.block.BlockID;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferInt;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public final class CFIDrawer {
|
||||
private final HeightMapMCAGenerator gen;
|
||||
private final TextureUtil tu;
|
||||
private final ForkJoinPool pool;
|
||||
|
||||
public CFIDrawer(HeightMapMCAGenerator generator, TextureUtil textureUtil) {
|
||||
this.gen = generator;
|
||||
this.tu = textureUtil;
|
||||
this.pool = new ForkJoinPool();
|
||||
}
|
||||
|
||||
public CFIDrawer(HeightMapMCAGenerator generator) {
|
||||
this(generator, Fawe.get().getCachedTextureUtil(false, 0, 100));
|
||||
}
|
||||
|
||||
public BufferedImage draw() {
|
||||
BufferedImage img = new BufferedImage(gen.getWidth(), gen.getLength(), BufferedImage.TYPE_INT_RGB);
|
||||
final char[] overlay = gen.overlay == null ? gen.floor.get() : gen.overlay.get();
|
||||
final char[] floor = gen.floor.get();
|
||||
final char[] main = gen.main.get();
|
||||
final byte[] heights = gen.heights.get();
|
||||
final byte[] biomes = gen.biomes.get();
|
||||
final int waterHeight = gen.primitives.waterHeight;
|
||||
final int width = gen.getWidth();
|
||||
final int length = gen.getLength();
|
||||
|
||||
int[] raw = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();
|
||||
|
||||
int parallelism = pool.getParallelism();
|
||||
int size = (heights.length + parallelism - 1) / parallelism;
|
||||
for (int i = 0; i < parallelism; i++) {
|
||||
int start = i * size;
|
||||
int end = Math.min(heights.length, start + size);
|
||||
pool.submit((Runnable) () -> {
|
||||
for (int index = start; index < end; index ++) {
|
||||
int height = (heights[index] & 0xFF);
|
||||
char ordinal;
|
||||
if ((ordinal = overlay[index]) == 0) {
|
||||
height--;
|
||||
ordinal = floor[index];
|
||||
if (ordinal == 0) {
|
||||
height--;
|
||||
ordinal = main[index];
|
||||
}
|
||||
}
|
||||
// draw ordinal
|
||||
int color;
|
||||
switch (ordinal >> 4) {
|
||||
case 2:
|
||||
color = getAverageBiomeColor(biomes, width, index);
|
||||
break;
|
||||
case 78:
|
||||
color = (0xDD << 16) + (0xDD << 8) + (0xDD << 0);
|
||||
break;
|
||||
default:
|
||||
color = tu.getColor(BlockTypes.getFromStateOrdinal(ordinal));
|
||||
break;
|
||||
}
|
||||
int slope = getSlope(heights, width, index, height);
|
||||
if (slope != 0) {
|
||||
slope = (slope << 3) + (slope << 2);
|
||||
int r = MathMan.clamp(((color >> 16) & 0xFF) + slope, 0, 255);
|
||||
int g = MathMan.clamp(((color >> 8) & 0xFF) + slope, 0, 255);
|
||||
int b = MathMan.clamp(((color >> 0) & 0xFF) + slope, 0, 255);
|
||||
color = (r << 16) + (g << 8) + (b << 0);
|
||||
}
|
||||
if (height + 1 < waterHeight) {
|
||||
char waterId = gen.primitives.waterOrdinal;
|
||||
int waterColor = 0;
|
||||
switch (waterId) {
|
||||
case BlockID.WATER:
|
||||
color = tu.averageColor((0x11 << 16) + (0x66 << 8) + (0xCC), color);
|
||||
break;
|
||||
case BlockID.LAVA:
|
||||
color = (0xCC << 16) + (0x33 << 8) + (0);
|
||||
break;
|
||||
default:
|
||||
color = tu.getColor(BlockTypes.getFromStateOrdinal(waterId));
|
||||
break;
|
||||
}
|
||||
}
|
||||
raw[index] = color;
|
||||
}
|
||||
});
|
||||
}
|
||||
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
|
||||
pool.shutdownNow();
|
||||
return img;
|
||||
}
|
||||
|
||||
private final int getAverageBiomeColor(byte[] biomes, int width, int index) {
|
||||
int c0 = tu.getBiome(biomes[index] & 0xFF).grassCombined;
|
||||
int c2 = getBiome(biomes, index + 1 + width, index);
|
||||
int c1 = getBiome(biomes, index - 1 - width, index);
|
||||
// int c3 = getBiome(biomes, index + width, index);
|
||||
// int c4 = getBiome(biomes, index - width, index);
|
||||
int r = ((c0 >> 16) & 0xFF) + ((c1 >> 16) & 0xFF) + ((c2 >> 16) & 0xFF);// + ((c3 >> 16) & 0xFF) + ((c4 >> 16) & 0xFF);
|
||||
int g = ((c0 >> 8) & 0xFF) + ((c1 >> 8) & 0xFF) + ((c2 >> 8) & 0xFF);// + ((c3 >> 8) & 0xFF) + ((c4 >> 8) & 0xFF);
|
||||
int b = ((c0) & 0xFF) + ((c1) & 0xFF) + ((c2) & 0xFF);// + ((c3) & 0xFF) + ((c4) & 0xFF);
|
||||
r = r * 85 >> 8;
|
||||
g = g * 85 >> 8;
|
||||
b = b * 85 >> 8;
|
||||
return (r << 16) + (g << 8) + (b);
|
||||
}
|
||||
|
||||
private final int getBiome(byte[] biomes, int newIndex, int index) {
|
||||
if (newIndex < 0 || newIndex >= biomes.length) newIndex = index;
|
||||
int biome = biomes[newIndex] & 0xFF;
|
||||
return tu.getBiome(biome).grassCombined;
|
||||
}
|
||||
|
||||
private int getSlope(byte[] heights, int width, int index, int height) {
|
||||
return (
|
||||
+ getHeight(heights, index + 1, height)
|
||||
// + getHeight(heights, index + width, height)
|
||||
+ getHeight(heights, index + width + 1, height)
|
||||
- getHeight(heights, index - 1, height)
|
||||
// - getHeight(heights, index - width, height)
|
||||
- getHeight(heights, index - width - 1, height)
|
||||
);
|
||||
}
|
||||
|
||||
private int getHeight(byte[] heights, int index, int height) {
|
||||
if (index < 0 || index >= heights.length) return height;
|
||||
return heights[index] & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,6 @@
|
||||
package com.boydti.fawe.object.brush.visualization.cfi;
|
||||
|
||||
import com.boydti.fawe.jnbt.anvil.MCAChunk;
|
||||
import com.boydti.fawe.object.collection.CleanableThreadLocal;
|
||||
import com.boydti.fawe.object.io.BufferedRandomAccessFile;
|
||||
import com.boydti.fawe.util.MainUtil;
|
||||
@ -71,7 +72,7 @@ public abstract class MCAWriter implements Extent {
|
||||
|
||||
public abstract boolean shouldWrite(int chunkX, int chunkZ);
|
||||
|
||||
public abstract WritableMCAChunk write(WritableMCAChunk input, int startX, int endX, int startZ, int endZ);
|
||||
public abstract MCAChunk write(MCAChunk input, int startX, int endX, int startZ, int endZ);
|
||||
|
||||
public void generate() throws IOException {
|
||||
if (!folder.exists()) {
|
||||
@ -80,9 +81,12 @@ public abstract class MCAWriter implements Extent {
|
||||
final ForkJoinPool pool = new ForkJoinPool();
|
||||
int tcx = (width - 1) >> 4;
|
||||
int tcz = (length - 1) >> 4;
|
||||
final ThreadLocal<WritableMCAChunk> chunkStore = ThreadLocal.withInitial(() -> {
|
||||
WritableMCAChunk chunk = new WritableMCAChunk();
|
||||
Arrays.fill(chunk.blocks, BlockID.AIR);
|
||||
final ThreadLocal<MCAChunk> chunkStore = new ThreadLocal<MCAChunk>() {
|
||||
@Override
|
||||
protected MCAChunk initialValue() {
|
||||
MCAChunk chunk = new MCAChunk();
|
||||
Arrays.fill(chunk.blocks, (char) BlockID.AIR);
|
||||
// Arrays.fill(chunk.skyLight, (byte) 255);
|
||||
return chunk;
|
||||
});
|
||||
final ThreadLocal<byte[]> byteStore1 = ThreadLocal.withInitial(() -> new byte[500000]);
|
||||
@ -122,12 +126,13 @@ public abstract class MCAWriter implements Extent {
|
||||
if (shouldWrite(cx, cz)) {
|
||||
pool.submit(() -> {
|
||||
try {
|
||||
WritableMCAChunk chunk = chunkStore.get();
|
||||
chunk.clear(fcx, fcz);
|
||||
MCAChunk chunk = chunkStore.get();
|
||||
chunk.reset();
|
||||
chunk.setPosition(fcx, fcz);
|
||||
chunk = write(chunk, csx, cex, csz, cez);
|
||||
if (chunk != null) {
|
||||
// Generation offset
|
||||
chunk.setLoc( fcx + (getOffsetX() >> 4), fcz + (getOffsetZ() >> 4));
|
||||
chunk.setPosition( fcx + (getOffsetX() >> 4), fcz + (getOffsetZ() >> 4));
|
||||
|
||||
// Compress
|
||||
byte[] bytes = chunk.toBytes(byteStore1.get());
|
||||
|
@ -17,6 +17,8 @@ import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
@ -457,7 +459,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
|
||||
public int maxZ;
|
||||
|
||||
public DiskStorageSummary(int x, int z) {
|
||||
blocks = new int[BlockTypes.states.length];
|
||||
blocks = new int[BlockTypesCache.states.length];
|
||||
minX = x;
|
||||
maxX = x;
|
||||
minZ = z;
|
||||
@ -482,7 +484,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
|
||||
HashMap<BlockState, Integer> map = new HashMap<>();
|
||||
for (int i = 0; i < blocks.length; i++) {
|
||||
if (blocks[i] != 0) {
|
||||
BlockState state = BlockTypes.states[i];
|
||||
BlockState state = BlockTypesCache.states[i];
|
||||
map.put(state, blocks[i]);
|
||||
}
|
||||
}
|
||||
|
@ -1,130 +0,0 @@
|
||||
package com.boydti.fawe.object.clipboard;
|
||||
|
||||
import com.boydti.fawe.jnbt.NBTStreamer;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.entity.Entity;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import java.util.List;
|
||||
|
||||
public class AbstractDelegateFaweClipboard extends FaweClipboard {
|
||||
private final FaweClipboard parent;
|
||||
|
||||
public AbstractDelegateFaweClipboard(FaweClipboard parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getBlock(int x, int y, int z) {
|
||||
return parent.getBlock(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> boolean setBlock(int x, int y, int z, B block) {
|
||||
return parent.setBlock(x, y, z, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> boolean setBlock(int index, B block) {
|
||||
return parent.setBlock(index, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBiomes() {
|
||||
return parent.hasBiomes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBiome(int x, int z, BiomeType biome) {
|
||||
return parent.setBiome(x, z, biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiome(int x, int z) {
|
||||
return parent.getBiome(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiome(int index) {
|
||||
return parent.getBiome(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getBlock(int index) {
|
||||
return parent.getBlock(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBiome(int index, BiomeType biome) {
|
||||
parent.setBiome(index, biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setTile(int x, int y, int z, CompoundTag tag) {
|
||||
return parent.setTile(x, y, z, tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) {
|
||||
return parent.createEntity(world, x, y, z, yaw, pitch, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Entity> getEntities() {
|
||||
return parent.getEntities();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(ClipboardEntity clipboardEntity) {
|
||||
return parent.remove(clipboardEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrigin(BlockVector3 offset) {
|
||||
parent.setOrigin(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDimensions(BlockVector3 dimensions) {
|
||||
parent.setDimensions(dimensions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
parent.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
parent.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getDimensions() {
|
||||
return parent.getDimensions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(BlockReader task, boolean air) {
|
||||
parent.forEach(task, air);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void streamBiomes(NBTStreamer.ByteReader task) {
|
||||
parent.streamBiomes(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void streamCombinedIds(NBTStreamer.ByteReader task) {
|
||||
parent.streamCombinedIds(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CompoundTag> getTileEntities() {
|
||||
return parent.getTileEntities();
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package com.boydti.fawe.object.clipboard;
|
||||
|
||||
import com.boydti.fawe.jnbt.NBTStreamer;
|
||||
import com.boydti.fawe.jnbt.streamer.IntValueReader;
|
||||
import com.boydti.fawe.object.IntegerTrio;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
@ -8,41 +8,38 @@ import com.sk89q.jnbt.IntTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.entity.Entity;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
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.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CPUOptimizedClipboard extends FaweClipboard {
|
||||
private int length;
|
||||
private int height;
|
||||
private int width;
|
||||
private int area;
|
||||
private int volume;
|
||||
public class CPUOptimizedClipboard extends LinearClipboard {
|
||||
|
||||
private BiomeType[] biomes = null;
|
||||
private int[] states;
|
||||
private char[] states;
|
||||
|
||||
private final HashMap<IntegerTrio, CompoundTag> nbtMapLoc;
|
||||
private final HashMap<Integer, CompoundTag> nbtMapIndex;
|
||||
|
||||
private final HashSet<ClipboardEntity> entities;
|
||||
private final HashSet<BlockArrayClipboard.ClipboardEntity> entities;
|
||||
|
||||
public CPUOptimizedClipboard(int width, int height, int length) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.length = length;
|
||||
this.area = width * length;
|
||||
this.volume = area * height;
|
||||
this.states = new int[volume];
|
||||
public CPUOptimizedClipboard(BlockVector3 dimensions) {
|
||||
super(dimensions);
|
||||
this.states = new char[getVolume()];
|
||||
nbtMapLoc = new HashMap<>();
|
||||
nbtMapIndex = new HashMap<>();
|
||||
entities = new HashSet<>();
|
||||
@ -54,7 +51,7 @@ public class CPUOptimizedClipboard extends FaweClipboard {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBiome(int x, int z, BiomeType biome) {
|
||||
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
||||
setBiome(getIndex(x, 0, z), biome);
|
||||
return true;
|
||||
}
|
||||
@ -62,19 +59,24 @@ public class CPUOptimizedClipboard extends FaweClipboard {
|
||||
@Override
|
||||
public void setBiome(int index, BiomeType biome) {
|
||||
if (biomes == null) {
|
||||
biomes = new BiomeType[area];
|
||||
biomes = new BiomeType[getArea()];
|
||||
}
|
||||
biomes[index] = biome;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void streamBiomes(NBTStreamer.ByteReader task) {
|
||||
public void streamBiomes(IntValueReader task) {
|
||||
if (!hasBiomes()) return;
|
||||
int index = 0;
|
||||
for (int z = 0; z < length; z++) {
|
||||
for (int x = 0; x < width; x++, index++) {
|
||||
task.run(index, biomes[index].getInternalId());
|
||||
try {
|
||||
for (int z = 0; z < getLength(); z++) {
|
||||
for (int x = 0; x < getWidth(); x++, index++) {
|
||||
task.applyInt(index, biomes[index].getInternalId());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,7 +89,7 @@ public class CPUOptimizedClipboard extends FaweClipboard {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiome(int x, int z) {
|
||||
public BiomeType getBiomeType(int x, int z) {
|
||||
return getBiome(getIndex(x, 0, z));
|
||||
}
|
||||
|
||||
@ -107,109 +109,63 @@ public class CPUOptimizedClipboard extends FaweClipboard {
|
||||
return nbtMapIndex.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDimensions(BlockVector3 dimensions) {
|
||||
width = dimensions.getBlockX();
|
||||
height = dimensions.getBlockY();
|
||||
length = dimensions.getBlockZ();
|
||||
area = width * length;
|
||||
int newVolume = area * height;
|
||||
if (newVolume != volume) {
|
||||
volume = newVolume;
|
||||
states = new int[volume];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getDimensions() {
|
||||
return BlockVector3.at(width, height, length);
|
||||
}
|
||||
|
||||
private int yLast;
|
||||
private int yLastI;
|
||||
private int zLast;
|
||||
private int zLastI;
|
||||
|
||||
public int getIndex(int x, int y, int z) {
|
||||
return x + ((yLast == y) ? yLastI : (yLastI = (yLast = y) * area)) + ((zLast == z) ? zLastI
|
||||
: (zLastI = (zLast = z) * width));
|
||||
return x + ((yLast == y) ? yLastI : (yLastI = (yLast = y) * getArea())) + ((zLast == z) ? zLastI
|
||||
: (zLastI = (zLast = z) * getWidth()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getBlock(int x, int y, int z) {
|
||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
||||
int index = getIndex(x, y, z);
|
||||
return getBlock(index);
|
||||
return getFullBlock(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getBlock(int index) {
|
||||
int combinedId = states[index];
|
||||
BlockType type = BlockTypes.getFromStateId(combinedId);
|
||||
BaseBlock base = type.withStateId(combinedId).toBaseBlock();
|
||||
if (type.getMaterial().hasContainer()) {
|
||||
public BaseBlock getFullBlock(int index) {
|
||||
BlockState block = getBlock(index);
|
||||
if (block.getMaterial().hasContainer()) {
|
||||
CompoundTag nbt = getTag(index);
|
||||
if (nbt != null) {
|
||||
return base.toBaseBlock(nbt);
|
||||
return block.toBaseBlock(nbt);
|
||||
}
|
||||
}
|
||||
return base;
|
||||
return block.toBaseBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(final BlockReader task, boolean air) {
|
||||
if (air) {
|
||||
for (int y = 0, index = 0; y < height; y++) {
|
||||
for (int z = 0; z < length; z++) {
|
||||
for (int x = 0; x < width; x++, index++) {
|
||||
BaseBlock block = getBlock(index);
|
||||
task.run(x, y, z, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int y = 0, index = 0; y < height; y++) {
|
||||
for (int z = 0; z < length; z++) {
|
||||
for (int x = 0; x < width; x++, index++) {
|
||||
BaseBlock block = getBlock(index);
|
||||
if (!block.getMaterial().isAir()) {
|
||||
task.run(x, y, z, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public BlockState getBlock(int index) {
|
||||
char ordinal = states[index];
|
||||
return BlockState.getFromOrdinal(ordinal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void streamCombinedIds(NBTStreamer.ByteReader task) {
|
||||
int index = 0;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int z = 0; z < length; z++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
task.run(index, states[index++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
return getBlock(getIndex(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CompoundTag> getTileEntities() {
|
||||
public Collection<CompoundTag> getTileEntities() {
|
||||
convertTilesToIndex();
|
||||
for (Map.Entry<Integer, CompoundTag> entry : nbtMapIndex.entrySet()) {
|
||||
int index = entry.getKey();
|
||||
CompoundTag tag = entry.getValue();
|
||||
Map<String, Tag> values = ReflectionUtils.getMap(tag.getValue());
|
||||
if (!values.containsKey("x")) {
|
||||
int y = index / area;
|
||||
index -= y * area;
|
||||
int z = index / width;
|
||||
int x = index - (z * width);
|
||||
int y = index / getArea();
|
||||
index -= y * getArea();
|
||||
int z = index / getWidth();
|
||||
int x = index - (z * getWidth());
|
||||
values.put("x", new IntTag(x));
|
||||
values.put("y", new IntTag(y));
|
||||
values.put("z", new IntTag(z));
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(nbtMapIndex.values());
|
||||
return nbtMapIndex.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -234,7 +190,7 @@ public class CPUOptimizedClipboard extends FaweClipboard {
|
||||
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> boolean setBlock(int index, B block) {
|
||||
states[index] = block.getInternalId();
|
||||
states[index] = block.getOrdinalChar();
|
||||
boolean hasNbt = block instanceof BaseBlock && block.hasNbtData();
|
||||
if (hasNbt) {
|
||||
setTile(index, block.getNbtData());
|
||||
@ -242,9 +198,10 @@ public class CPUOptimizedClipboard extends FaweClipboard {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) {
|
||||
FaweClipboard.ClipboardEntity ret = new ClipboardEntity(world, x, y, z, yaw, pitch, entity);
|
||||
public Entity createEntity(Location location, BaseEntity entity) {
|
||||
BlockArrayClipboard.ClipboardEntity ret = new BlockArrayClipboard.ClipboardEntity(location, entity);
|
||||
entities.add(ret);
|
||||
return ret;
|
||||
}
|
||||
@ -255,7 +212,21 @@ public class CPUOptimizedClipboard extends FaweClipboard {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(ClipboardEntity clipboardEntity) {
|
||||
return entities.remove(clipboardEntity);
|
||||
public void removeEntity(Entity entity) {
|
||||
this.entities.remove(entity);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public void removeEntity(int x, int y, int z, UUID uuid) {
|
||||
Iterator<BlockArrayClipboard.ClipboardEntity> iter = this.entities.iterator();
|
||||
while (iter.hasNext()) {
|
||||
BlockArrayClipboard.ClipboardEntity entity = iter.next();
|
||||
UUID entUUID = entity.getState().getNbtData().getUUID();
|
||||
if (uuid.equals(entUUID)) {
|
||||
iter.remove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,165 @@
|
||||
package com.boydti.fawe.object.clipboard;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.entity.Entity;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
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 java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DelegateClipboard implements Clipboard {
|
||||
private final Clipboard parent;
|
||||
|
||||
public DelegateClipboard(Clipboard parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Clipboard getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
return parent.getURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrigin(BlockVector3 offset) {
|
||||
parent.setOrigin(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getDimensions() {
|
||||
return parent.getDimensions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Region getRegion() {
|
||||
return parent.getRegion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getOrigin() {
|
||||
return parent.getOrigin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBiomes() {
|
||||
return parent.hasBiomes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntity(Entity entity) {
|
||||
parent.removeEntity(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getMinimumPoint() {
|
||||
return parent.getMinimumPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getMaximumPoint() {
|
||||
return parent.getMaximumPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Entity> getEntities(Region region) {
|
||||
return parent.getEntities(region);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Entity> getEntities() {
|
||||
return parent.getEntities();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entity createEntity(Location location, BaseEntity entity) {
|
||||
return parent.createEntity(location, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public void removeEntity(int x, int y, int z, UUID uuid) {
|
||||
parent.removeEntity(x, y, z, uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWorld() {
|
||||
return parent.isWorld();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(BlockVector3 position) {
|
||||
return parent.getBlock(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
return parent.getBlock(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(BlockVector3 position) {
|
||||
return parent.getFullBlock(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
||||
return parent.getFullBlock(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiome(BlockVector2 position) {
|
||||
return parent.getBiome(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int z) {
|
||||
return parent.getBiomeType(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public <T extends BlockStateHolder<T>> boolean setBlock(BlockVector3 position, T block) throws WorldEditException {
|
||||
return parent.setBlock(position, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T block) throws WorldEditException {
|
||||
return parent.setBlock(x, y, z, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException {
|
||||
return parent.setTile(x, y, z, tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBiome(BlockVector2 position, BiomeType biome) {
|
||||
return parent.setBiome(position, biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
||||
return parent.setBiome(x, y, z, biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
parent.close();
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ package com.boydti.fawe.object.clipboard;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.jnbt.NBTStreamer;
|
||||
import com.boydti.fawe.jnbt.streamer.IntValueReader;
|
||||
import com.boydti.fawe.object.IntegerTrio;
|
||||
import com.boydti.fawe.util.MainUtil;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
@ -15,6 +15,8 @@ import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
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;
|
||||
@ -22,20 +24,29 @@ 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.Nullable;
|
||||
import java.io.Closeable;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@ -43,18 +54,12 @@ import java.util.UUID;
|
||||
* - Uses an auto closable RandomAccessFile for getting / setting id / data
|
||||
* - I don't know how to reduce nbt / entities to O(2) complexity, so it is stored in memory.
|
||||
*/
|
||||
public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
public class DiskOptimizedClipboard extends LinearClipboard implements Closeable {
|
||||
|
||||
private static int HEADER_SIZE = 14;
|
||||
|
||||
protected int length;
|
||||
protected int height;
|
||||
protected int width;
|
||||
protected int area;
|
||||
protected int volume;
|
||||
|
||||
private final HashMap<IntegerTrio, CompoundTag> nbtMap;
|
||||
private final HashSet<ClipboardEntity> entities;
|
||||
private final HashSet<BlockArrayClipboard.ClipboardEntity> entities;
|
||||
private final File file;
|
||||
|
||||
private RandomAccessFile braf;
|
||||
@ -63,11 +68,65 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
private FileChannel fileChannel;
|
||||
private boolean hasBiomes;
|
||||
|
||||
public DiskOptimizedClipboard(int width, int height, int length, UUID uuid) {
|
||||
this(width, height, length, MainUtil.getFile(Fawe.get() != null ? Fawe.imp().getDirectory() : new File("."), Settings.IMP.PATHS.CLIPBOARD + File.separator + uuid + ".bd"));
|
||||
public DiskOptimizedClipboard(BlockVector3 dimensions, UUID uuid) {
|
||||
this(dimensions, MainUtil.getFile(Fawe.get() != null ? Fawe.imp().getDirectory() : new File("."), Settings.IMP.PATHS.CLIPBOARD + File.separator + uuid + ".bd"));
|
||||
}
|
||||
|
||||
public DiskOptimizedClipboard(BlockVector3 dimensions) {
|
||||
this(dimensions, MainUtil.getFile(Fawe.imp() != null ? Fawe.imp().getDirectory() : new File("."), Settings.IMP.PATHS.CLIPBOARD + File.separator + UUID.randomUUID() + ".bd"));
|
||||
}
|
||||
|
||||
public DiskOptimizedClipboard(BlockVector3 dimensions, File file) {
|
||||
super(dimensions);
|
||||
if (getWidth() > Character.MAX_VALUE || getHeight() > Character.MAX_VALUE || getLength() > Character.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("Too large");
|
||||
}
|
||||
try {
|
||||
nbtMap = new HashMap<>();
|
||||
entities = new HashSet<>();
|
||||
this.file = file;
|
||||
try {
|
||||
if (!file.exists()) {
|
||||
File parent = file.getParentFile();
|
||||
if (parent != null) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
file.createNewFile();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.braf = new RandomAccessFile(file, "rw");
|
||||
long volume = (long) getArea() * 2L + (long) HEADER_SIZE;
|
||||
braf.setLength(0);
|
||||
braf.setLength(volume);
|
||||
init();
|
||||
// write getLength() etc
|
||||
byteBuffer.putChar(2, (char) getWidth());
|
||||
byteBuffer.putChar(4, (char) getHeight());
|
||||
byteBuffer.putChar(6, (char) getLength());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
return file.toURI();
|
||||
}
|
||||
|
||||
private static BlockVector3 readSize(File file) {
|
||||
try (DataInputStream is = new DataInputStream(new FileInputStream(file))) {
|
||||
is.skipBytes(2);
|
||||
return BlockVector3.at(is.readChar(), is.readChar(), is.readChar());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public DiskOptimizedClipboard(File file) {
|
||||
super(readSize(file));
|
||||
try {
|
||||
nbtMap = new HashMap<>();
|
||||
entities = new HashSet<>();
|
||||
@ -75,16 +134,9 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
this.braf = new RandomAccessFile(file, "rw");
|
||||
braf.setLength(file.length());
|
||||
init();
|
||||
width = byteBuffer.getChar(2);
|
||||
height = byteBuffer.getChar(4);
|
||||
length = byteBuffer.getChar(6);
|
||||
area = width * length;
|
||||
this.volume = length * width * height;
|
||||
|
||||
if (braf.length() - HEADER_SIZE == (volume << 2) + area) {
|
||||
if (braf.length() - HEADER_SIZE == (getVolume() << 1) + getArea()) {
|
||||
hasBiomes = true;
|
||||
}
|
||||
autoCloseTask();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -107,7 +159,7 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
hasBiomes = true;
|
||||
close();
|
||||
this.braf = new RandomAccessFile(file, "rw");
|
||||
this.braf.setLength(HEADER_SIZE + (volume << 2) + area);
|
||||
this.braf.setLength(HEADER_SIZE + (getVolume() << 1) + getArea());
|
||||
init();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
@ -123,7 +175,7 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBiome(int x, int z, BiomeType biome) {
|
||||
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
||||
setBiome(getIndex(x, 0, z), biome);
|
||||
return true;
|
||||
}
|
||||
@ -131,7 +183,7 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
@Override
|
||||
public void setBiome(int index, BiomeType biome) {
|
||||
if (initBiome()) {
|
||||
byteBuffer.put(HEADER_SIZE + (volume << 2) + index, (byte) biome.getInternalId());
|
||||
byteBuffer.put(HEADER_SIZE + (getVolume() << 1) + index, (byte) biome.getInternalId());
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,36 +192,36 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
if (!hasBiomes()) {
|
||||
return null;
|
||||
}
|
||||
int biomeId = byteBuffer.get(HEADER_SIZE + (volume << 2) + index) & 0xFF;
|
||||
int biomeId = byteBuffer.get(HEADER_SIZE + (getVolume() << 1) + index) & 0xFF;
|
||||
return BiomeTypes.get(biomeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void streamBiomes(NBTStreamer.ByteReader task) {
|
||||
public void streamBiomes(IntValueReader task) {
|
||||
if (!hasBiomes()) return;
|
||||
int index = 0;
|
||||
int mbbIndex = HEADER_SIZE + (volume << 2);
|
||||
for (int z = 0; z < length; z++) {
|
||||
for (int x = 0; x < width; x++, index++, mbbIndex++) {
|
||||
int biome = byteBuffer.get(mbbIndex) & 0xFF;
|
||||
task.run(index, biome);
|
||||
int mbbIndex = HEADER_SIZE + (getVolume() << 1);
|
||||
try {
|
||||
for (int z = 0; z < getLength(); z++) {
|
||||
for (int x = 0; x < getWidth(); x++, index++, mbbIndex++) {
|
||||
int biome = byteBuffer.get(mbbIndex) & 0xFF;
|
||||
task.applyInt(index, biome);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiome(int x, int z) {
|
||||
public BiomeType getBiomeType(int x, int z) {
|
||||
return getBiome(getIndex(x, 0, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getDimensions() {
|
||||
return BlockVector3.at(width, height, length);
|
||||
}
|
||||
|
||||
public BlockArrayClipboard toClipboard() {
|
||||
try {
|
||||
CuboidRegion region = new CuboidRegion(BlockVector3.at(0, 0, 0), BlockVector3.at(width - 1, height - 1, length - 1));
|
||||
CuboidRegion region = new CuboidRegion(BlockVector3.at(0, 0, 0), BlockVector3.at(getWidth() - 1, getHeight() - 1, getLength() - 1));
|
||||
int ox = byteBuffer.getShort(8);
|
||||
int oy = byteBuffer.getShort(10);
|
||||
int oz = byteBuffer.getShort(12);
|
||||
@ -182,45 +234,9 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
return null;
|
||||
}
|
||||
|
||||
public DiskOptimizedClipboard(int width, int height, int length, File file) {
|
||||
try {
|
||||
nbtMap = new HashMap<>();
|
||||
entities = new HashSet<>();
|
||||
this.file = file;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.length = length;
|
||||
this.area = width * length;
|
||||
this.volume = width * length * height;
|
||||
try {
|
||||
if (!file.exists()) {
|
||||
File parent = file.getParentFile();
|
||||
if (parent != null) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
file.createNewFile();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.braf = new RandomAccessFile(file, "rw");
|
||||
long volume = (long) width * (long) height * (long) length * 4L + (long) HEADER_SIZE;
|
||||
braf.setLength(0);
|
||||
braf.setLength(volume);
|
||||
if (width * height * length != 0) {
|
||||
init();
|
||||
// write length etc
|
||||
byteBuffer.putChar(2, (char) width);
|
||||
byteBuffer.putChar(4, (char) height);
|
||||
byteBuffer.putChar(6, (char) length);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrigin(BlockVector3 offset) {
|
||||
super.setOrigin(offset);
|
||||
try {
|
||||
byteBuffer.putShort(8, (short) offset.getBlockX());
|
||||
byteBuffer.putShort(10, (short) offset.getBlockY());
|
||||
@ -230,38 +246,11 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDimensions(BlockVector3 dimensions) {
|
||||
try {
|
||||
width = dimensions.getBlockX();
|
||||
height = dimensions.getBlockY();
|
||||
length = dimensions.getBlockZ();
|
||||
area = width * length;
|
||||
volume = width * length * height;
|
||||
long size = width * height * length * 4L + HEADER_SIZE + (hasBiomes() ? area : 0);
|
||||
if (braf.length() < size) {
|
||||
close();
|
||||
this.braf = new RandomAccessFile(file, "rw");
|
||||
braf.setLength(size);
|
||||
init();
|
||||
}
|
||||
byteBuffer.putChar(2, (char) width);
|
||||
byteBuffer.putChar(4, (char) height);
|
||||
byteBuffer.putChar(6, (char) length);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
byteBuffer.force();
|
||||
}
|
||||
|
||||
public DiskOptimizedClipboard(int width, int height, int length) {
|
||||
this(width, height, length, MainUtil.getFile(Fawe.imp() != null ? Fawe.imp().getDirectory() : new File("."), Settings.IMP.PATHS.CLIPBOARD + File.separator + UUID.randomUUID() + ".bd"));
|
||||
}
|
||||
|
||||
private void closeDirectBuffer(ByteBuffer cb) {
|
||||
if (cb == null || !cb.isDirect()) return;
|
||||
// we could use this type cast and call functions without reflection code,
|
||||
@ -287,11 +276,6 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
@ -311,174 +295,80 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
private void autoCloseTask() {
|
||||
// TaskManager.IMP.laterAsync(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// if (raf != null && System.currentTimeMillis() - lastAccessed > 10000) {
|
||||
// close();
|
||||
// } else if (raf == null) {
|
||||
// return;
|
||||
// } else {
|
||||
// TaskManager.IMP.laterAsync(this, 200);
|
||||
// }
|
||||
// }
|
||||
// }, 200);
|
||||
}
|
||||
|
||||
private int ylast;
|
||||
private int ylasti;
|
||||
private int zlast;
|
||||
private int zlasti;
|
||||
|
||||
@Override
|
||||
public void streamCombinedIds(NBTStreamer.ByteReader task) {
|
||||
try {
|
||||
byteBuffer.force();
|
||||
int pos = HEADER_SIZE;
|
||||
int index = 0;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int z = 0; z < length; z++) {
|
||||
for (int x = 0; x < width; x++, pos += 4) {
|
||||
int combinedId = byteBuffer.getInt(pos);
|
||||
task.run(index++, combinedId);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CompoundTag> getTileEntities() {
|
||||
return new ArrayList<>(nbtMap.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(BlockReader task, boolean air) {
|
||||
byteBuffer.force();
|
||||
int pos = HEADER_SIZE;
|
||||
IntegerTrio trio = new IntegerTrio();
|
||||
final boolean hasTile = !nbtMap.isEmpty();
|
||||
if (air) {
|
||||
if (hasTile) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int z = 0; z < length; z++) {
|
||||
for (int x = 0; x < width; x++, pos += 4) {
|
||||
int combinedId = byteBuffer.getInt(pos);
|
||||
BlockType type = BlockTypes.getFromStateId(combinedId);
|
||||
BlockState state = type.withStateId(combinedId);
|
||||
if (type.getMaterial().hasContainer()) {
|
||||
trio.set(x, y, z);
|
||||
CompoundTag nbt = nbtMap.get(trio);
|
||||
if (nbt != null) {
|
||||
task.run(x, y, z, state.toBaseBlock(nbt));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
task.run(x, y, z, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int z = 0; z < length; z++) {
|
||||
for (int x = 0; x < width; x++, pos += 4) {
|
||||
int combinedId = byteBuffer.getInt(pos);
|
||||
BlockState state = BlockState.getFromInternalId(combinedId);
|
||||
task.run(x, y, z, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int z = 0; z < length; z++) {
|
||||
for (int x = 0; x < width; x++, pos += 4) {
|
||||
int combinedId = byteBuffer.getInt(pos);
|
||||
BlockType type = BlockTypes.getFromStateId(combinedId);
|
||||
if (!type.getMaterial().isAir()) {
|
||||
BlockState state = type.withStateId(combinedId);
|
||||
if (type.getMaterial().hasContainer()) {
|
||||
trio.set(x, y, z);
|
||||
CompoundTag nbt = nbtMap.get(trio);
|
||||
if (nbt != null) {
|
||||
task.run(x, y, z, state.toBaseBlock(nbt));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
task.run(x, y, z, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public Collection<CompoundTag> getTileEntities() {
|
||||
return nbtMap.values();
|
||||
}
|
||||
|
||||
public int getIndex(int x, int y, int z) {
|
||||
return x + (ylast == y ? ylasti : (ylasti = (ylast = y) * area)) + (zlast == z
|
||||
? zlasti : (zlasti = (zlast = z) * width));
|
||||
return x + (ylast == y ? ylasti : (ylasti = (ylast = y) * getArea())) + (zlast == z
|
||||
? zlasti : (zlasti = (zlast = z) * getWidth()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getBlock(int x, int y, int z) {
|
||||
try {
|
||||
int index = HEADER_SIZE + (getIndex(x, y, z) << 2);
|
||||
int combinedId = byteBuffer.getInt(index);
|
||||
BlockType type = BlockTypes.getFromStateId(combinedId);
|
||||
BaseBlock base = type.withStateId(combinedId).toBaseBlock();
|
||||
if (type.getMaterial().hasContainer() && !nbtMap.isEmpty()) {
|
||||
CompoundTag nbt = nbtMap.get(new IntegerTrio(x, y, z));
|
||||
if (nbt != null) {
|
||||
return base.toBaseBlock(nbt);
|
||||
}
|
||||
}
|
||||
return base;
|
||||
} catch (IndexOutOfBoundsException ignore) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return BlockTypes.AIR.getDefaultState().toBaseBlock();
|
||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
||||
return toBaseBlock(getBlock(x, y, z), x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getBlock(int i) {
|
||||
try {
|
||||
int diskIndex = HEADER_SIZE + (i << 2);
|
||||
int combinedId = byteBuffer.getInt(diskIndex);
|
||||
BlockType type = BlockTypes.getFromStateId(combinedId);
|
||||
BaseBlock base = type.withStateId(combinedId).toBaseBlock();
|
||||
if (type.getMaterial().hasContainer() && !nbtMap.isEmpty()) {
|
||||
CompoundTag nbt;
|
||||
if (nbtMap.size() < 4) {
|
||||
nbt = null;
|
||||
for (Map.Entry<IntegerTrio, CompoundTag> entry : nbtMap.entrySet()) {
|
||||
IntegerTrio key = entry.getKey();
|
||||
int index = getIndex(key.x, key.y, key.z);
|
||||
if (index == i) {
|
||||
nbt = entry.getValue();
|
||||
break;
|
||||
}
|
||||
private BaseBlock toBaseBlock(BlockState state, int i) {
|
||||
if (state.getMaterial().hasContainer() && !nbtMap.isEmpty()) {
|
||||
CompoundTag nbt;
|
||||
if (nbtMap.size() < 4) {
|
||||
nbt = null;
|
||||
for (Map.Entry<IntegerTrio, CompoundTag> entry : nbtMap.entrySet()) {
|
||||
IntegerTrio key = entry.getKey();
|
||||
int index = getIndex(key.x, key.y, key.z);
|
||||
if (index == i) {
|
||||
nbt = entry.getValue();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// x + z * width + y * area;
|
||||
int y = i / area;
|
||||
int newI = i - y * area;
|
||||
int z = newI / width;
|
||||
int x = newI - z * width;
|
||||
nbt = nbtMap.get(new IntegerTrio(x, y, z));
|
||||
}
|
||||
if (nbt != null) {
|
||||
return base.toBaseBlock(nbt);
|
||||
}
|
||||
} else {
|
||||
int y = i / getArea();
|
||||
int newI = i - y * getArea();
|
||||
int z = newI / getWidth();
|
||||
int x = newI - z * getWidth();
|
||||
nbt = nbtMap.get(new IntegerTrio(x, y, z));
|
||||
}
|
||||
return base;
|
||||
return state.toBaseBlock(nbt);
|
||||
}
|
||||
return state.toBaseBlock();
|
||||
}
|
||||
|
||||
private BaseBlock toBaseBlock(BlockState state, int x, int y, int z) {
|
||||
if (state.getMaterial().hasContainer() && !nbtMap.isEmpty()) {
|
||||
CompoundTag nbt = nbtMap.get(new IntegerTrio(x, y, z));
|
||||
return state.toBaseBlock(nbt);
|
||||
}
|
||||
return state.toBaseBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(int i) {
|
||||
return toBaseBlock(getBlock(i), i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(int index) {
|
||||
try {
|
||||
int diskIndex = HEADER_SIZE + (index << 1);
|
||||
char ordinal = byteBuffer.getChar(diskIndex);
|
||||
return BlockState.getFromOrdinal(ordinal);
|
||||
} catch (IndexOutOfBoundsException ignore) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return BlockTypes.AIR.getDefaultState().toBaseBlock();
|
||||
return BlockTypes.AIR.getDefaultState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
return getBlock(getIndex(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -494,9 +384,9 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> boolean setBlock(int x, int y, int z, B block) {
|
||||
try {
|
||||
int index = HEADER_SIZE + (getIndex(x, y, z) << 2);
|
||||
int combined = block.getInternalId();
|
||||
byteBuffer.putInt(index, combined);
|
||||
int index = HEADER_SIZE + (getIndex(x, y, z) << 1);
|
||||
char ordinal = block.getOrdinalChar();
|
||||
byteBuffer.putChar(index, ordinal);
|
||||
boolean hasNbt = block instanceof BaseBlock && block.hasNbtData();
|
||||
if (hasNbt) {
|
||||
setTile(x, y, z, block.getNbtData());
|
||||
@ -511,15 +401,15 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> boolean setBlock(int i, B block) {
|
||||
try {
|
||||
int combined = block.getInternalId();
|
||||
int index = HEADER_SIZE + (i << 2);
|
||||
byteBuffer.putInt(index, combined);
|
||||
char ordinal = block.getOrdinalChar();
|
||||
int index = HEADER_SIZE + (i << 1);
|
||||
byteBuffer.putChar(index, ordinal);
|
||||
boolean hasNbt = block instanceof BaseBlock && block.hasNbtData();
|
||||
if (hasNbt) {
|
||||
int y = i / area;
|
||||
int newI = i - y * area;
|
||||
int z = newI / width;
|
||||
int x = newI - z * width;
|
||||
int y = i / getArea();
|
||||
int newI = i - y * getArea();
|
||||
int z = newI / getWidth();
|
||||
int x = newI - z * getWidth();
|
||||
setTile(x, y, z, block.getNbtData());
|
||||
}
|
||||
return true;
|
||||
@ -529,9 +419,10 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) {
|
||||
FaweClipboard.ClipboardEntity ret = new ClipboardEntity(world, x, y, z, yaw, pitch, entity);
|
||||
public Entity createEntity(Location location, BaseEntity entity) {
|
||||
BlockArrayClipboard.ClipboardEntity ret = new BlockArrayClipboard.ClipboardEntity(location, entity);
|
||||
entities.add(ret);
|
||||
return ret;
|
||||
}
|
||||
@ -542,7 +433,21 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(ClipboardEntity clipboardEntity) {
|
||||
return entities.remove(clipboardEntity);
|
||||
public void removeEntity(Entity entity) {
|
||||
this.entities.remove(entity);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public void removeEntity(int x, int y, int z, UUID uuid) {
|
||||
Iterator<BlockArrayClipboard.ClipboardEntity> iter = this.entities.iterator();
|
||||
while (iter.hasNext()) {
|
||||
BlockArrayClipboard.ClipboardEntity entity = iter.next();
|
||||
UUID entUUID = entity.getState().getNbtData().getUUID();
|
||||
if (uuid.equals(entUUID)) {
|
||||
iter.remove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.boydti.fawe.object.clipboard;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.entity.Entity;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
@ -39,6 +40,11 @@ public class EmptyClipboard implements Clipboard {
|
||||
public void setOrigin(BlockVector3 origin) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntity(Entity entity) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getMinimumPoint() {
|
||||
return BlockVector3.ZERO;
|
||||
|
@ -1,179 +0,0 @@
|
||||
package com.boydti.fawe.object.clipboard;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.boydti.fawe.jnbt.NBTStreamer;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.IntTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.entity.Entity;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public abstract class FaweClipboard {
|
||||
public abstract BaseBlock getBlock(int x, int y, int z);
|
||||
|
||||
public abstract <B extends BlockStateHolder<B>> boolean setBlock(int index, B block);
|
||||
|
||||
public abstract <B extends BlockStateHolder<B>> boolean setBlock(int x, int y, int z, B block);
|
||||
|
||||
/**
|
||||
* 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}
|
||||
* 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.
|
||||
*
|
||||
* @return true if the clipboard has biome data set
|
||||
*/
|
||||
public boolean hasBiomes() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract boolean setBiome(int x, int z, BiomeType biome);
|
||||
|
||||
public abstract BiomeType getBiome(int x, int z);
|
||||
|
||||
public abstract BiomeType getBiome(int index);
|
||||
|
||||
public abstract BaseBlock getBlock(int index);
|
||||
|
||||
public abstract void setBiome(int index, BiomeType biome);
|
||||
|
||||
public abstract boolean setTile(int x, int y, int z, CompoundTag tag);
|
||||
|
||||
public abstract Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity);
|
||||
|
||||
public abstract List<? extends Entity> getEntities();
|
||||
|
||||
public abstract boolean remove(ClipboardEntity clipboardEntity);
|
||||
|
||||
public void setOrigin(BlockVector3 offset) {
|
||||
} // Do nothing
|
||||
|
||||
public abstract void setDimensions(BlockVector3 dimensions);
|
||||
|
||||
public abstract BlockVector3 getDimensions();
|
||||
|
||||
/**
|
||||
* The locations provided are relative to the clipboard min
|
||||
*
|
||||
* @param task
|
||||
* @param air
|
||||
*/
|
||||
public abstract void forEach(BlockReader task, boolean air);
|
||||
|
||||
public interface BlockReader {
|
||||
<B extends BlockStateHolder<B>> void run(int x, int y, int z, B block);
|
||||
}
|
||||
|
||||
public abstract void streamBiomes(NBTStreamer.ByteReader task);
|
||||
|
||||
public void streamCombinedIds(NBTStreamer.ByteReader task) {
|
||||
forEach(new BlockReader() {
|
||||
private int index;
|
||||
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> void run(int x, int y, int z, B block) {
|
||||
task.run(index++, block.getInternalId());
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public List<CompoundTag> getTileEntities() {
|
||||
final List<CompoundTag> tiles = new ArrayList<>();
|
||||
forEach(new BlockReader() {
|
||||
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> void run(int x, int y, int z, B block) {
|
||||
if(!(block instanceof BaseBlock)) return;
|
||||
BaseBlock base = (BaseBlock)block;
|
||||
CompoundTag tag = base.getNbtData();
|
||||
if (tag != null) {
|
||||
Map<String, Tag> values = ReflectionUtils.getMap(tag.getValue());
|
||||
values.put("x", new IntTag(x));
|
||||
values.put("y", new IntTag(y));
|
||||
values.put("z", new IntTag(z));
|
||||
tiles.add(tag);
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
return tiles;
|
||||
}
|
||||
|
||||
public void close() {}
|
||||
|
||||
public void flush() {}
|
||||
|
||||
/**
|
||||
* Stores entity data.
|
||||
*/
|
||||
public class ClipboardEntity implements Entity {
|
||||
private final BaseEntity entity;
|
||||
private final Extent world;
|
||||
private final double x, y, z;
|
||||
private final float yaw, pitch;
|
||||
|
||||
public ClipboardEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) {
|
||||
checkNotNull(entity);
|
||||
checkNotNull(world);
|
||||
this.world = world;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
this.entity = new BaseEntity(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove() {
|
||||
return FaweClipboard.this.remove(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T getFacet(Class<? extends T> cls) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entity state. This is not a copy.
|
||||
*
|
||||
* @return the entity
|
||||
*/
|
||||
BaseEntity getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseEntity getState() {
|
||||
return new BaseEntity(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return new Location(world, x, y, z, yaw, pitch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Extent getExtent() {
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setLocation(Location location) {
|
||||
//Should not be teleporting this entity
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package com.boydti.fawe.object.clipboard;
|
||||
|
||||
import com.boydti.fawe.beta.implementation.filter.block.AbstractFilterBlock;
|
||||
import com.boydti.fawe.jnbt.streamer.IntValueReader;
|
||||
import com.google.common.collect.ForwardingIterator;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Best used when clipboard selections are small, or using legacy formats
|
||||
* (Small being < Integer.MAX_VALUE/BLOCK_SIZE_BYTES blocks)
|
||||
*/
|
||||
public abstract class LinearClipboard extends SimpleClipboard implements Clipboard, Closeable {
|
||||
public LinearClipboard(BlockVector3 dimensions) {
|
||||
super(dimensions);
|
||||
}
|
||||
|
||||
public abstract <B extends BlockStateHolder<B>> boolean setBlock(int i, B block);
|
||||
|
||||
public abstract BaseBlock getFullBlock(int i);
|
||||
|
||||
public abstract BlockState getBlock(int i);
|
||||
|
||||
public abstract void setBiome(int index, BiomeType biome);
|
||||
|
||||
public abstract BiomeType getBiome(int index);
|
||||
|
||||
/**
|
||||
* The locations provided are relative to the clipboard min
|
||||
*
|
||||
* @param task
|
||||
* @param air
|
||||
*/
|
||||
public abstract void streamBiomes(IntValueReader task);
|
||||
|
||||
public abstract Collection<CompoundTag> getTileEntities();
|
||||
|
||||
public void close() {}
|
||||
|
||||
public void flush() {}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<BlockVector3> iterator() {
|
||||
Region region = getRegion();
|
||||
if (region instanceof CuboidRegion) {
|
||||
Iterator<BlockVector3> iter = ((CuboidRegion) region).iterator_old();
|
||||
LinearFilter filter = new LinearFilter();
|
||||
|
||||
return new ForwardingIterator<BlockVector3>() {
|
||||
@Override
|
||||
protected Iterator<BlockVector3> delegate() {
|
||||
return iter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 next() {
|
||||
return filter.next(super.next());
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return super.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
private class LinearFilter extends AbstractFilterBlock {
|
||||
private int index = -1;
|
||||
private BlockVector3 position;
|
||||
|
||||
private LinearFilter next(BlockVector3 position) {
|
||||
this.position = position;
|
||||
index++;
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public BaseBlock getFullBlock() {
|
||||
return LinearClipboard.this.getFullBlock(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFullBlock(BaseBlock block) {
|
||||
LinearClipboard.this.setBlock(index, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getPosition() {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.boydti.fawe.object.clipboard;
|
||||
|
||||
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class LinearClipboardBuilder {
|
||||
FastByteArrayOutputStream blocksOut = new FastByteArrayOutputStream();
|
||||
FastByteArrayOutputStream biomesOut = new FastByteArrayOutputStream();
|
||||
|
||||
public int width, height, length;
|
||||
|
||||
public void setWidth(int width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public void setHeight(int height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public void setLength(int length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public LinearClipboard build() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package com.boydti.fawe.object.clipboard;
|
||||
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.jnbt.NBTStreamer;
|
||||
import com.boydti.fawe.jnbt.streamer.IntValueReader;
|
||||
import com.boydti.fawe.object.IntegerTrio;
|
||||
import com.boydti.fawe.util.MainUtil;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
@ -10,33 +10,34 @@ import com.sk89q.jnbt.IntTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.entity.Entity;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
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.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.jpountz.util.SafeUtils;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MemoryOptimizedClipboard extends FaweClipboard {
|
||||
public class MemoryOptimizedClipboard extends LinearClipboard {
|
||||
|
||||
private static final int BLOCK_SIZE = 1048576 * 4;
|
||||
private static final int BLOCK_SIZE = 1048576 * 2;
|
||||
private static final int BLOCK_MASK = 1048575;
|
||||
private static final int BLOCK_SHIFT = 20;
|
||||
|
||||
private int length;
|
||||
private int height;
|
||||
private int width;
|
||||
private int area;
|
||||
private int volume;
|
||||
|
||||
private byte[][] states;
|
||||
|
||||
private byte[] buffer = new byte[MainUtil.getMaxCompressedLength(BLOCK_SIZE)];
|
||||
@ -45,7 +46,7 @@ public class MemoryOptimizedClipboard extends FaweClipboard {
|
||||
private final HashMap<IntegerTrio, CompoundTag> nbtMapLoc;
|
||||
private final HashMap<Integer, CompoundTag> nbtMapIndex;
|
||||
|
||||
private final HashSet<ClipboardEntity> entities;
|
||||
private final HashSet<BlockArrayClipboard.ClipboardEntity> entities;
|
||||
|
||||
private int lastCombinedIdsI = -1;
|
||||
|
||||
@ -55,17 +56,13 @@ public class MemoryOptimizedClipboard extends FaweClipboard {
|
||||
|
||||
private int compressionLevel;
|
||||
|
||||
public MemoryOptimizedClipboard(int width, int height, int length) {
|
||||
this(width, height, length, Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL);
|
||||
public MemoryOptimizedClipboard(BlockVector3 dimensions) {
|
||||
this(dimensions, Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL);
|
||||
}
|
||||
|
||||
public MemoryOptimizedClipboard(int width, int height, int length, int compressionLevel) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.length = length;
|
||||
this.area = width * length;
|
||||
this.volume = area * height;
|
||||
states = new byte[1 + (volume >> BLOCK_SHIFT)][];
|
||||
public MemoryOptimizedClipboard(BlockVector3 dimensions, int compressionLevel) {
|
||||
super(dimensions);
|
||||
states = new byte[1 + (getVolume() >> BLOCK_SHIFT)][];
|
||||
nbtMapLoc = new HashMap<>();
|
||||
nbtMapIndex = new HashMap<>();
|
||||
entities = new HashSet<>();
|
||||
@ -89,7 +86,7 @@ public class MemoryOptimizedClipboard extends FaweClipboard {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBiome(int x, int z, BiomeType biome) {
|
||||
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
||||
setBiome(getIndex(x, 0, z), biome);
|
||||
return true;
|
||||
}
|
||||
@ -97,19 +94,24 @@ public class MemoryOptimizedClipboard extends FaweClipboard {
|
||||
@Override
|
||||
public void setBiome(int index, BiomeType biome) {
|
||||
if (biomes == null) {
|
||||
biomes = new byte[area];
|
||||
biomes = new byte[getArea()];
|
||||
}
|
||||
biomes[index] = (byte) biome.getInternalId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void streamBiomes(NBTStreamer.ByteReader task) {
|
||||
public void streamBiomes(IntValueReader task) {
|
||||
if (!hasBiomes()) return;
|
||||
int index = 0;
|
||||
for (int z = 0; z < length; z++) {
|
||||
for (int x = 0; x < width; x++, index++) {
|
||||
task.run(index, biomes[index] & 0xFF);
|
||||
try {
|
||||
for (int z = 0; z < getLength(); z++) {
|
||||
for (int x = 0; x < getWidth(); x++, index++) {
|
||||
task.applyInt(index, biomes[index] & 0xFF);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,7 +124,7 @@ public class MemoryOptimizedClipboard extends FaweClipboard {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiome(int x, int z) {
|
||||
public BiomeType getBiomeType(int x, int z) {
|
||||
return getBiome(getIndex(x, 0, z));
|
||||
}
|
||||
|
||||
@ -131,22 +133,24 @@ public class MemoryOptimizedClipboard extends FaweClipboard {
|
||||
return nbtMapIndex.get(index);
|
||||
}
|
||||
|
||||
public int getCombinedId(int index) {
|
||||
public int getOrdinal(int index) {
|
||||
int i = index >> BLOCK_SHIFT;
|
||||
if (i == lastCombinedIdsI) {
|
||||
if (lastCombinedIds == null) {
|
||||
int li = (index & BLOCK_MASK) << 1;
|
||||
if (i != lastCombinedIdsI) {
|
||||
saveCombinedIds();
|
||||
byte[] compressed = states[lastCombinedIdsI = i];
|
||||
if (compressed != null) {
|
||||
lastCombinedIds = MainUtil.decompress(compressed, lastCombinedIds, BLOCK_SIZE, compressionLevel);
|
||||
} else {
|
||||
lastCombinedIds = null;
|
||||
return 0;
|
||||
}
|
||||
return lastCombinedIds[index & BLOCK_MASK] & 0xFF;
|
||||
}
|
||||
saveCombinedIds();
|
||||
byte[] compressed = states[lastCombinedIdsI = i];
|
||||
if (compressed == null) {
|
||||
lastCombinedIds = null;
|
||||
return BlockTypes.AIR.getInternalId();
|
||||
if (lastCombinedIds == null) {
|
||||
return 0;
|
||||
}
|
||||
lastCombinedIds = MainUtil.decompress(compressed, lastCombinedIds, BLOCK_SIZE, compressionLevel);
|
||||
return SafeUtils.readIntBE(lastCombinedIds, index & BLOCK_MASK);
|
||||
int ordinal = ((lastCombinedIds[li] << 8) + lastCombinedIds[li + 1]);
|
||||
return ordinal;
|
||||
}
|
||||
|
||||
private void saveCombinedIds() {
|
||||
@ -156,26 +160,6 @@ public class MemoryOptimizedClipboard extends FaweClipboard {
|
||||
saveCombinedIds = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDimensions(BlockVector3 dimensions) {
|
||||
width = dimensions.getBlockX();
|
||||
height = dimensions.getBlockY();
|
||||
length = dimensions.getBlockZ();
|
||||
area = width * length;
|
||||
int newVolume = area * height;
|
||||
if (newVolume != volume) {
|
||||
volume = newVolume;
|
||||
states = new byte[1 + (volume >> BLOCK_SHIFT)][];
|
||||
lastCombinedIdsI = -1;
|
||||
saveCombinedIds = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockVector3 getDimensions() {
|
||||
return BlockVector3.at(width, height, length);
|
||||
}
|
||||
|
||||
private int lastI;
|
||||
private int lastIMin;
|
||||
private int lastIMax;
|
||||
@ -207,45 +191,30 @@ public class MemoryOptimizedClipboard extends FaweClipboard {
|
||||
}
|
||||
lastCombinedIds = new byte[BLOCK_SIZE];
|
||||
}
|
||||
int li = (index & BLOCK_MASK) << 2;
|
||||
lastCombinedIds[li] = (byte) ((v >>> 24) & 0xFF);
|
||||
lastCombinedIds[li + 1] = (byte) ((v >>> 16) & 0xFF);
|
||||
lastCombinedIds[li + 2] = (byte) ((v >>> 8) & 0xFF);
|
||||
lastCombinedIds[li + 3] = (byte) ((v >>> 0) & 0xFF);
|
||||
int li = (index & BLOCK_MASK) << 1;
|
||||
lastCombinedIds[li] = (byte) ((v >>> 8) & 0xFF);
|
||||
lastCombinedIds[li + 1] = (byte) (v & 0xFF);
|
||||
saveCombinedIds = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void streamCombinedIds(NBTStreamer.ByteReader task) {
|
||||
int index = 0;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int z = 0; z < length; z++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int id = getCombinedId(index);
|
||||
task.run(index++, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CompoundTag> getTileEntities() {
|
||||
public Collection<CompoundTag> getTileEntities() {
|
||||
convertTilesToIndex();
|
||||
for (Map.Entry<Integer, CompoundTag> entry : nbtMapIndex.entrySet()) {
|
||||
int index = entry.getKey();
|
||||
CompoundTag tag = entry.getValue();
|
||||
Map<String, Tag> values = ReflectionUtils.getMap(tag.getValue());
|
||||
if (!values.containsKey("x")) {
|
||||
int y = index / area;
|
||||
index -= y * area;
|
||||
int z = index / width;
|
||||
int x = index - (z * width);
|
||||
int y = index / getArea();
|
||||
index -= y * getArea();
|
||||
int z = index / getWidth();
|
||||
int x = index - (z * getWidth());
|
||||
values.put("x", new IntTag(x));
|
||||
values.put("y", new IntTag(y));
|
||||
values.put("z", new IntTag(z));
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(nbtMapIndex.values());
|
||||
return nbtMapIndex.values();
|
||||
}
|
||||
|
||||
private int ylast;
|
||||
@ -254,52 +223,36 @@ public class MemoryOptimizedClipboard extends FaweClipboard {
|
||||
private int zlasti;
|
||||
|
||||
public int getIndex(int x, int y, int z) {
|
||||
return x + ((ylast == y) ? ylasti : (ylasti = (ylast = y) * area)) + ((zlast == z) ? zlasti : (zlasti = (zlast = z) * width));
|
||||
return x + ((ylast == y) ? ylasti : (ylasti = (ylast = y) * getArea())) + ((zlast == z) ? zlasti : (zlasti = (zlast = z) * getWidth()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getBlock(int x, int y, int z) {
|
||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
||||
int index = getIndex(x, y, z);
|
||||
return getBlock(index);
|
||||
return getFullBlock(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getBlock(int index) {
|
||||
int combinedId = getCombinedId(index);
|
||||
BlockType type = BlockTypes.getFromStateId(combinedId);
|
||||
BaseBlock base = type.withStateId(combinedId).toBaseBlock();
|
||||
if (type.getMaterial().hasContainer()) {
|
||||
public BaseBlock getFullBlock(int index) {
|
||||
BlockState block = getBlock(index);
|
||||
if (block.getMaterial().hasContainer()) {
|
||||
CompoundTag nbt = getTag(index);
|
||||
if (nbt != null) {
|
||||
return base.toBaseBlock(nbt);
|
||||
return block.toBaseBlock(nbt);
|
||||
}
|
||||
}
|
||||
return base;
|
||||
return block.toBaseBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(final BlockReader task, final boolean air) {
|
||||
if (air) {
|
||||
for (int y = 0, index = 0; y < height; y++) {
|
||||
for (int z = 0; z < length; z++) {
|
||||
for (int x = 0; x < width; x++, index++) {
|
||||
BaseBlock block = getBlock(index);
|
||||
task.run(x, y, z, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int y = 0, index = 0; y < height; y++) {
|
||||
for (int z = 0; z < length; z++) {
|
||||
for (int x = 0; x < width; x++, index++) {
|
||||
BaseBlock block = getBlock(index);
|
||||
if (!block.getMaterial().isAir()) {
|
||||
task.run(x, y, z, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public BlockState getBlock(int index) {
|
||||
int ordinal = getOrdinal(index);
|
||||
return BlockState.getFromOrdinal(ordinal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
return getBlock(getIndex(x, y, z));
|
||||
}
|
||||
|
||||
public int size() {
|
||||
@ -344,9 +297,10 @@ public class MemoryOptimizedClipboard extends FaweClipboard {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) {
|
||||
FaweClipboard.ClipboardEntity ret = new ClipboardEntity(world, x, y, z, yaw, pitch, entity);
|
||||
public Entity createEntity(Location location, BaseEntity entity) {
|
||||
BlockArrayClipboard.ClipboardEntity ret = new BlockArrayClipboard.ClipboardEntity(location, entity);
|
||||
entities.add(ret);
|
||||
return ret;
|
||||
}
|
||||
@ -357,7 +311,21 @@ public class MemoryOptimizedClipboard extends FaweClipboard {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(ClipboardEntity clipboardEntity) {
|
||||
return entities.remove(clipboardEntity);
|
||||
public void removeEntity(Entity entity) {
|
||||
this.entities.remove(entity);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public void removeEntity(int x, int y, int z, UUID uuid) {
|
||||
Iterator<BlockArrayClipboard.ClipboardEntity> iter = this.entities.iterator();
|
||||
while (iter.hasNext()) {
|
||||
BlockArrayClipboard.ClipboardEntity entity = iter.next();
|
||||
UUID entUUID = entity.getState().getNbtData().getUUID();
|
||||
if (uuid.equals(entUUID)) {
|
||||
iter.remove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,11 +35,8 @@ public class MultiClipboardHolder extends URIClipboardHolder {
|
||||
super(URI.create(""), EmptyClipboard.INSTANCE);
|
||||
holders = new ArrayList<>();
|
||||
URI uri = URI.create("");
|
||||
if (clipboard instanceof BlockArrayClipboard) {
|
||||
FaweClipboard fc = ((BlockArrayClipboard) clipboard).IMP;
|
||||
if (fc instanceof DiskOptimizedClipboard) {
|
||||
uri = ((DiskOptimizedClipboard) fc).getFile().toURI();
|
||||
}
|
||||
if (clipboard.getURI() != null) {
|
||||
uri = clipboard.getURI();
|
||||
}
|
||||
add(uri, clipboard);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user