Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-11-02 17:40:09 +01:00
Add fluid ticking and refactor post-processing a bit (#1554)
* Make postProcessSet a default method and change to void * Throwable#getMessage is nullable * Move (re-)ticking to a post-processor per "platform" - Add fluid ticking * chore: Ignore (for us) irrelevant rules * chore: Fix correct toml syntax? * Re-add removed method for API-compliance and refactor it to have a use * Switch to javax annotations * Switch to recalcBlockCounts for ticking blocks. * No need to set air count anymore either * We can still "not tick" in fast mode in 1.17.2 * update adapters * Let paper create the chunk section if biomes are null * Adjust notes to settings * 1.17.2 didn't exist * Add 1.18.2 * Don't attempt to cache plains biome ID * Use correct annotation Co-authored-by: NotMyFault <mc.cache@web.de>
Dieser Commit ist enthalten in:
Ursprung
5d18e15128
Commit
e9db749e2f
4
.lift.toml
Normale Datei
4
.lift.toml
Normale Datei
@ -0,0 +1,4 @@
|
||||
jdkVersion = "17"
|
||||
build = "gradle clean build -x test"
|
||||
tools = ["findsecbugs", "ErrorProne", "Semgrep", "Detekt", "ESLint", "Infer"]
|
||||
ignoreRules = ["CatchAndPrintStackTrace", "ReferenceEquality", "FallThrough", "FutureReturnValueIgnored"]
|
@ -6,6 +6,7 @@ import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||
@ -694,4 +695,9 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBatchProcessor getTickingPostProcessor() {
|
||||
return new PaperweightPostProcessor();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -410,7 +410,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
@SuppressWarnings("rawtypes")
|
||||
public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finalizer) {
|
||||
forceLoadSections = false;
|
||||
copy = createCopy ? new PaperweightGetBlocks_Copy(serverLevel) : null;
|
||||
copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null;
|
||||
try {
|
||||
ServerLevel nmsWorld = serverLevel;
|
||||
LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ);
|
||||
@ -461,6 +461,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
|
||||
bitMask |= 1 << layer;
|
||||
|
||||
// Changes may still be written to chunk SET
|
||||
char[] tmp = set.load(layerNo);
|
||||
char[] setArr = new char[4096];
|
||||
System.arraycopy(tmp, 0, setArr, 0, 4096);
|
||||
@ -477,6 +478,12 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
|
||||
LevelChunkSection newSection;
|
||||
LevelChunkSection existingSection = levelChunkSections[layer];
|
||||
// Don't attempt to tick section whilst we're editing
|
||||
if (existingSection != null) {
|
||||
PaperweightPlatformAdapter.clearCounts(existingSection);
|
||||
existingSection.tickingList.clear();
|
||||
}
|
||||
|
||||
if (existingSection == null) {
|
||||
newSection = PaperweightPlatformAdapter.newChunkSection(layerNo, setArr, fastmode, adapter);
|
||||
if (PaperweightPlatformAdapter.setSectionAtomic(levelChunkSections, null, newSection, layer)) {
|
||||
@ -492,10 +499,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
}
|
||||
}
|
||||
PaperweightPlatformAdapter.fieldTickingBlockCount.set(existingSection, (short) 0);
|
||||
|
||||
//ensure that the server doesn't try to tick the chunksection while we're editing it.
|
||||
//ensure that the server doesn't try to tick the chunksection while we're editing it (again).
|
||||
DelegateSemaphore lock = PaperweightPlatformAdapter.applyLock(existingSection);
|
||||
PaperweightPlatformAdapter.clearCounts(existingSection);
|
||||
existingSection.tickingList.clear();
|
||||
|
||||
synchronized (lock) {
|
||||
// lock.acquire();
|
||||
|
@ -19,6 +19,7 @@ import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.ChunkBiomeContainer;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
@ -35,13 +36,15 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
private final char[][] blocks;
|
||||
private final int minHeight;
|
||||
private final int maxHeight;
|
||||
private final ServerLevel serverLevel;
|
||||
final ServerLevel serverLevel;
|
||||
final LevelChunk levelChunk;
|
||||
private ChunkBiomeContainer chunkBiomeContainer;
|
||||
|
||||
protected PaperweightGetBlocks_Copy(ServerLevel world) {
|
||||
this.serverLevel = world;
|
||||
this.minHeight = world.getMinBuildHeight();
|
||||
this.maxHeight = world.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive.
|
||||
protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) {
|
||||
this.levelChunk = levelChunk;
|
||||
this.serverLevel = levelChunk.level;
|
||||
this.minHeight = serverLevel.getMinBuildHeight();
|
||||
this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive.
|
||||
this.blocks = new char[getSectionCount()][];
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
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.BlockState;
|
||||
@ -54,9 +53,7 @@ import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Semaphore;
|
||||
@ -71,9 +68,9 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
public static final Field fieldBitsPerEntry;
|
||||
|
||||
public static final Field fieldTickingFluidContent;
|
||||
public static final Field fieldTickingBlockCount;
|
||||
public static final Field fieldNonEmptyBlockCount;
|
||||
private static final Field fieldTickingFluidContent;
|
||||
private static final Field fieldTickingBlockCount;
|
||||
private static final Field fieldNonEmptyBlockCount;
|
||||
|
||||
private static final Field fieldBiomes;
|
||||
|
||||
@ -271,15 +268,20 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
NMS conversion
|
||||
*/
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer, final char[] blocks, boolean fastmode,
|
||||
final int layer,
|
||||
final char[] blocks,
|
||||
boolean fastMode,
|
||||
CachedBukkitAdapter adapter
|
||||
) {
|
||||
return newChunkSection(layer, null, blocks, fastmode, adapter);
|
||||
return newChunkSection(layer, null, blocks, fastMode, adapter);
|
||||
}
|
||||
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer, final Function<Integer, char[]> get, char[] set,
|
||||
boolean fastmode, CachedBukkitAdapter adapter
|
||||
final int layer,
|
||||
final Function<Integer, char[]> get,
|
||||
char[] set,
|
||||
boolean fastMode,
|
||||
CachedBukkitAdapter adapter
|
||||
) {
|
||||
if (set == null) {
|
||||
return newChunkSection(layer);
|
||||
@ -289,19 +291,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
final long[] blockStates = FaweCache.INSTANCE.BLOCK_STATES.get();
|
||||
final int[] blocksCopy = FaweCache.INSTANCE.SECTION_BLOCKS.get();
|
||||
try {
|
||||
int[] num_palette_buffer = new int[1];
|
||||
Map<BlockVector3, Integer> ticking_blocks = new HashMap<>();
|
||||
int air;
|
||||
int num_palette;
|
||||
if (get == null) {
|
||||
air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer,
|
||||
set, ticking_blocks, fastmode, adapter
|
||||
num_palette = createPalette(blockToPalette, paletteToBlock, blocksCopy, set, adapter
|
||||
);
|
||||
} else {
|
||||
air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy,
|
||||
num_palette_buffer, get, set, ticking_blocks, fastmode, adapter
|
||||
);
|
||||
num_palette = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, get, set, adapter);
|
||||
}
|
||||
int num_palette = num_palette_buffer[0];
|
||||
// BlockStates
|
||||
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
|
||||
if (Settings.settings().PROTOCOL_SUPPORT_FIX || num_palette != 1) {
|
||||
@ -364,17 +360,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
fieldStorage.set(dataPaletteBlocks, nmsBits);
|
||||
fieldPalette.set(dataPaletteBlocks, blockStatePalettedContainer);
|
||||
fieldBits.set(dataPaletteBlocks, bitsPerEntry);
|
||||
setCount(ticking_blocks.size(), 4096 - air, levelChunkSection);
|
||||
if (!fastmode) {
|
||||
ticking_blocks.forEach((pos, ordinal) -> levelChunkSection
|
||||
.setBlockState(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ(),
|
||||
Block.stateById(ordinal)
|
||||
));
|
||||
}
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (!fastMode) {
|
||||
levelChunkSection.recalcBlockCounts();
|
||||
}
|
||||
return levelChunkSection;
|
||||
} catch (final Throwable e) {
|
||||
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
|
||||
@ -386,11 +378,9 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
return new LevelChunkSection(layer);
|
||||
}
|
||||
|
||||
public static void setCount(final int tickingBlockCount, final int nonEmptyBlockCount, final LevelChunkSection section) throws
|
||||
IllegalAccessException {
|
||||
fieldTickingFluidContent.setShort(section, (short) 0); // TODO FIXME
|
||||
fieldTickingBlockCount.setShort(section, (short) tickingBlockCount);
|
||||
fieldNonEmptyBlockCount.setShort(section, (short) nonEmptyBlockCount);
|
||||
public static void clearCounts(final LevelChunkSection section) throws IllegalAccessException {
|
||||
fieldTickingFluidContent.setShort(section, (short) 0);
|
||||
fieldTickingBlockCount.setShort(section, (short) 0);
|
||||
}
|
||||
|
||||
public static Biome[] getBiomeArray(ChunkBiomeContainer chunkBiomeContainer) {
|
||||
|
@ -0,0 +1,175 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunk;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.fastasyncworldedit.core.registry.state.PropertyKey;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PaperweightPostProcessor implements IBatchProcessor {
|
||||
|
||||
@Override
|
||||
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
return set;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final IChunkSet iChunkSet) {
|
||||
boolean tickFluid = Settings.settings().EXPERIMENTAL.ALLOW_TICK_FLUIDS;
|
||||
// The PostProcessor shouldn't be added, but just in case
|
||||
if (!tickFluid) {
|
||||
return;
|
||||
}
|
||||
PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet;
|
||||
layer:
|
||||
for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) {
|
||||
char[] set = iChunkSet.loadIfPresent(layer);
|
||||
if (set == null) {
|
||||
// No edit means no need to process
|
||||
continue;
|
||||
}
|
||||
char[] get = null;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char ordinal = set[i];
|
||||
char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
boolean fromGet = false; // Used for liquids
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
// If this is null, then it's because we're loading a layer in the range of 0->15, but blocks aren't
|
||||
// actually being set
|
||||
if (get == null) {
|
||||
continue layer;
|
||||
}
|
||||
fromGet = true;
|
||||
ordinal = replacedOrdinal = get[i];
|
||||
}
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
continue;
|
||||
} else if (!fromGet) { // if fromGet, don't do the same again
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
replacedOrdinal = get[i];
|
||||
}
|
||||
boolean ticking = BlockTypesCache.ticking[ordinal];
|
||||
boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal];
|
||||
boolean replacedWasLiquid = false;
|
||||
BlockState replacedState = null;
|
||||
if (!ticking) {
|
||||
// If the block being replaced was not ticking, it cannot be a liquid
|
||||
if (!replacedWasTicking) {
|
||||
continue;
|
||||
}
|
||||
// If the block being replaced is not fluid, we do not need to worry
|
||||
if (!(replacedWasLiquid =
|
||||
(replacedState = BlockState.getFromOrdinal(replacedOrdinal)).getMaterial().isLiquid())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
BlockState state = BlockState.getFromOrdinal(ordinal);
|
||||
boolean liquid = state.getMaterial().isLiquid();
|
||||
int x = i & 15;
|
||||
int y = (i >> 8) & 15;
|
||||
int z = (i >> 4) & 15;
|
||||
BlockPos position = new BlockPos((chunk.getX() << 4) + x, (layer << 4) + y, (chunk.getZ() << 4) + z);
|
||||
if (liquid || replacedWasLiquid) {
|
||||
if (liquid) {
|
||||
addFluid(getBlocks.serverLevel, state, position);
|
||||
continue;
|
||||
}
|
||||
// If the replaced fluid (is?) adjacent to water. Do not bother to check adjacent chunks(sections) as this
|
||||
// may be time consuming. Chances are any fluid blocks in adjacent chunks are being replaced or will end up
|
||||
// being ticked anyway. We only need it to be "hit" once.
|
||||
if (!wasAdjacentToWater(get, set, i, x, y, z)) {
|
||||
continue;
|
||||
}
|
||||
addFluid(getBlocks.serverLevel, replacedState, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Extent construct(final Extent child) {
|
||||
throw new UnsupportedOperationException("Processing only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessorScope getScope() {
|
||||
return ProcessorScope.READING_SET_BLOCKS;
|
||||
}
|
||||
|
||||
private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) {
|
||||
if (set == null || get == null) {
|
||||
return false;
|
||||
}
|
||||
char ordinal;
|
||||
char reserved = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
if (x > 0 && set[i - 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (x < 15 && set[i + 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z > 0 && set[i - 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z < 15 && set[i + 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y > 0 && set[i - 256] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y < 15 && set[i + 256] != reserved) {
|
||||
return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private boolean isFluid(char ordinal) {
|
||||
return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) {
|
||||
Fluid type;
|
||||
if (replacedState.getBlockType() == BlockTypes.LAVA) {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.LAVA : Fluids.FLOWING_LAVA;
|
||||
} else {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.WATER : Fluids.FLOWING_WATER;
|
||||
}
|
||||
serverLevel.getLiquidTicks().scheduleTick(
|
||||
position,
|
||||
type,
|
||||
type.getTickDelay(serverLevel)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -6,6 +6,7 @@ import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||
@ -686,4 +687,9 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBatchProcessor getTickingPostProcessor() {
|
||||
return new PaperweightPostProcessor();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -398,11 +398,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
@SuppressWarnings("rawtypes")
|
||||
public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finalizer) {
|
||||
forceLoadSections = false;
|
||||
copy = createCopy ? new PaperweightGetBlocks_Copy(serverLevel) : null;
|
||||
copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null;
|
||||
try {
|
||||
ServerLevel nmsWorld = serverLevel;
|
||||
LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ);
|
||||
boolean fastmode = set.isFastMode() && Settings.settings().QUEUE.NO_TICK_FASTMODE;
|
||||
|
||||
// Remove existing tiles. Create a copy so that we can remove blocks
|
||||
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
|
||||
@ -470,7 +469,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection(
|
||||
layerNo,
|
||||
new char[4096],
|
||||
fastmode,
|
||||
adapter,
|
||||
biomeRegistry,
|
||||
biomeData
|
||||
@ -498,6 +496,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
|
||||
bitMask |= 1 << getSectionIndex;
|
||||
|
||||
// Changes may still be written to chunk SET
|
||||
char[] tmp = set.load(layerNo);
|
||||
char[] setArr = new char[4096];
|
||||
System.arraycopy(tmp, 0, setArr, 0, 4096);
|
||||
@ -508,6 +507,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
|
||||
LevelChunkSection newSection;
|
||||
LevelChunkSection existingSection = levelChunkSections[getSectionIndex];
|
||||
// Don't attempt to tick section whilst we're editing
|
||||
if (existingSection != null) {
|
||||
PaperweightPlatformAdapter.clearCounts(existingSection);
|
||||
existingSection.tickingList.clear();
|
||||
}
|
||||
|
||||
if (createCopy) {
|
||||
char[] tmpLoad = loadPrivately(layerNo);
|
||||
@ -529,7 +533,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
newSection = PaperweightPlatformAdapter.newChunkSection(
|
||||
layerNo,
|
||||
setArr,
|
||||
fastmode,
|
||||
adapter,
|
||||
biomeRegistry,
|
||||
biomeData
|
||||
@ -547,9 +550,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
}
|
||||
}
|
||||
PaperweightPlatformAdapter.fieldTickingBlockCount.set(existingSection, (short) 0);
|
||||
|
||||
//ensure that the server doesn't try to tick the chunksection while we're editing it.
|
||||
//ensure that the server doesn't try to tick the chunksection while we're editing it. (Again)
|
||||
PaperweightPlatformAdapter.clearCounts(existingSection);
|
||||
existingSection.tickingList.clear();
|
||||
DelegateSemaphore lock = PaperweightPlatformAdapter.applyLock(existingSection);
|
||||
|
||||
synchronized (lock) {
|
||||
@ -583,7 +587,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
layerNo,
|
||||
this::loadPrivately,
|
||||
setArr,
|
||||
fastmode,
|
||||
adapter,
|
||||
biomeRegistry,
|
||||
biomeData
|
||||
@ -1052,7 +1055,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
biomeRegistry.get(ResourceLocation.tryParse(biomeType.getId()))
|
||||
biomeRegistry.getOptional(ResourceLocation.tryParse(biomeType.getId())).orElseThrow()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -35,13 +36,15 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
private final char[][] blocks;
|
||||
private final int minHeight;
|
||||
private final int maxHeight;
|
||||
private final ServerLevel serverLevel;
|
||||
final ServerLevel serverLevel;
|
||||
final LevelChunk levelChunk;
|
||||
private PalettedContainer<Biome>[] biomes = null;
|
||||
|
||||
protected PaperweightGetBlocks_Copy(ServerLevel world) {
|
||||
this.serverLevel = world;
|
||||
this.minHeight = world.getMinBuildHeight();
|
||||
this.maxHeight = world.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive.
|
||||
protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) {
|
||||
this.levelChunk = levelChunk;
|
||||
this.serverLevel = levelChunk.level;
|
||||
this.minHeight = serverLevel.getMinBuildHeight();
|
||||
this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive.
|
||||
this.blocks = new char[getSectionCount()][];
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@ import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
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.BlockState;
|
||||
@ -85,9 +84,9 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
public static final Field fieldPalette;
|
||||
|
||||
|
||||
public static final Field fieldTickingFluidCount;
|
||||
public static final Field fieldTickingBlockCount;
|
||||
public static final Field fieldNonEmptyBlockCount;
|
||||
private static final Field fieldTickingFluidCount;
|
||||
private static final Field fieldTickingBlockCount;
|
||||
private static final Field fieldNonEmptyBlockCount;
|
||||
|
||||
private static final MethodHandle methodGetVisibleChunk;
|
||||
|
||||
@ -301,16 +300,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
NMS conversion
|
||||
*/
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer, final char[] blocks, boolean fastmode,
|
||||
CachedBukkitAdapter adapter, Registry<Biome> biomeRegistry,
|
||||
final int layer,
|
||||
final char[] blocks,
|
||||
CachedBukkitAdapter adapter,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Biome> biomes
|
||||
) {
|
||||
return newChunkSection(layer, null, blocks, fastmode, adapter, biomeRegistry, biomes);
|
||||
return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes);
|
||||
}
|
||||
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer, final Function<Integer, char[]> get, char[] set,
|
||||
boolean fastmode, CachedBukkitAdapter adapter, Registry<Biome> biomeRegistry,
|
||||
final int layer,
|
||||
final Function<Integer, char[]> get,
|
||||
char[] set,
|
||||
CachedBukkitAdapter adapter,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Biome> biomes
|
||||
) {
|
||||
if (set == null) {
|
||||
@ -321,24 +325,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
final long[] blockStates = FaweCache.INSTANCE.BLOCK_STATES.get();
|
||||
final int[] blocksCopy = FaweCache.INSTANCE.SECTION_BLOCKS.get();
|
||||
try {
|
||||
int[] num_palette_buffer = new int[1];
|
||||
Map<BlockVector3, Integer> ticking_blocks = new HashMap<>();
|
||||
int air;
|
||||
int num_palette;
|
||||
if (get == null) {
|
||||
air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer,
|
||||
set, ticking_blocks, fastmode, adapter
|
||||
);
|
||||
num_palette = createPalette(blockToPalette, paletteToBlock, blocksCopy, set, adapter);
|
||||
} else {
|
||||
air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy,
|
||||
num_palette_buffer, get, set, ticking_blocks, fastmode, adapter
|
||||
);
|
||||
num_palette = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, get, set, adapter);
|
||||
}
|
||||
int num_palette = num_palette_buffer[0];
|
||||
// BlockStates
|
||||
|
||||
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
|
||||
Object configuration =
|
||||
PalettedContainer.Strategy.SECTION_STATES.getConfiguration(new FakeIdMapBlock(num_palette), bitsPerEntry);
|
||||
if (bitsPerEntry > 0 && bitsPerEntry < 5) {
|
||||
bitsPerEntry = 4;
|
||||
} else if (bitsPerEntry > 8) {
|
||||
@ -365,7 +359,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
} else {
|
||||
nmsBits = new SimpleBitStorage(bitsPerEntry, 4096, bits);
|
||||
}
|
||||
final Palette<net.minecraft.world.level.block.state.BlockState> blockStatePalette;
|
||||
List<net.minecraft.world.level.block.state.BlockState> palette;
|
||||
if (bitsPerEntry < 9) {
|
||||
palette = new ArrayList<>();
|
||||
@ -390,9 +383,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
palette
|
||||
);
|
||||
LevelChunkSection levelChunkSection;
|
||||
try {
|
||||
//fieldStorage.set(dataPaletteBlocks, nmsBits);
|
||||
//fieldPalette.set(dataPaletteBlocks, blockStatePalettedContainer);
|
||||
if (biomes == null) {
|
||||
biomes = new PalettedContainer<>(
|
||||
biomeRegistry,
|
||||
@ -402,18 +392,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
);
|
||||
}
|
||||
levelChunkSection = new LevelChunkSection(layer, blockStatePalettedContainer, biomes);
|
||||
setCount(ticking_blocks.size(), 4096 - air, levelChunkSection);
|
||||
if (!fastmode) {
|
||||
ticking_blocks.forEach((pos, ordinal) -> levelChunkSection.setBlockState(
|
||||
pos.getBlockX(),
|
||||
pos.getBlockY(),
|
||||
pos.getBlockZ(),
|
||||
Block.stateById(ordinal)
|
||||
));
|
||||
}
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return levelChunkSection;
|
||||
} catch (final Throwable e) {
|
||||
@ -422,23 +400,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // Only deprecated in paper
|
||||
private static LevelChunkSection newChunkSection(
|
||||
int layer, Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Biome> biomes
|
||||
) {
|
||||
if (biomes == null) {
|
||||
return new LevelChunkSection(layer, biomeRegistry);
|
||||
}
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> dataPaletteBlocks = new PalettedContainer<>(
|
||||
Block.BLOCK_STATE_REGISTRY,
|
||||
Blocks.AIR.defaultBlockState(),
|
||||
PalettedContainer.Strategy.SECTION_STATES,
|
||||
null
|
||||
);
|
||||
PalettedContainer<Biome> biomesPalette = biomes != null ? biomes : new PalettedContainer<>(
|
||||
biomeRegistry,
|
||||
biomeRegistry.getOrThrow(Biomes.PLAINS),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES,
|
||||
null
|
||||
);
|
||||
return new LevelChunkSection(layer, dataPaletteBlocks, biomesPalette);
|
||||
return new LevelChunkSection(layer, dataPaletteBlocks, biomes);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -541,11 +517,9 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
return biomePalettedContainer;
|
||||
}
|
||||
|
||||
public static void setCount(final int tickingBlockCount, final int nonEmptyBlockCount, final LevelChunkSection section) throws
|
||||
IllegalAccessException {
|
||||
fieldTickingFluidCount.setShort(section, (short) 0); // TODO FIXME
|
||||
fieldTickingBlockCount.setShort(section, (short) tickingBlockCount);
|
||||
fieldNonEmptyBlockCount.setShort(section, (short) nonEmptyBlockCount);
|
||||
public static void clearCounts(final LevelChunkSection section) throws IllegalAccessException {
|
||||
fieldTickingFluidCount.setShort(section, (short) 0);
|
||||
fieldTickingBlockCount.setShort(section, (short) 0);
|
||||
}
|
||||
|
||||
public static BiomeType adapt(Biome biome, LevelAccessor levelAccessor) {
|
||||
|
@ -0,0 +1,175 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R1;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunk;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.fastasyncworldedit.core.registry.state.PropertyKey;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PaperweightPostProcessor implements IBatchProcessor {
|
||||
|
||||
@Override
|
||||
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
return set;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final IChunkSet iChunkSet) {
|
||||
boolean tickFluid = Settings.settings().EXPERIMENTAL.ALLOW_TICK_FLUIDS;
|
||||
// The PostProcessor shouldn't be added, but just in case
|
||||
if (!tickFluid) {
|
||||
return;
|
||||
}
|
||||
PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet;
|
||||
layer:
|
||||
for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) {
|
||||
char[] set = iChunkSet.loadIfPresent(layer);
|
||||
if (set == null) {
|
||||
// No edit means no need to process
|
||||
continue;
|
||||
}
|
||||
char[] get = null;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char ordinal = set[i];
|
||||
char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
boolean fromGet = false; // Used for liquids
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
// If this is null, then it's because we're loading a layer in the range of 0->15, but blocks aren't
|
||||
// actually being set
|
||||
if (get == null) {
|
||||
continue layer;
|
||||
}
|
||||
fromGet = true;
|
||||
ordinal = replacedOrdinal = get[i];
|
||||
}
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
continue;
|
||||
} else if (!fromGet) { // if fromGet, don't do the same again
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
replacedOrdinal = get[i];
|
||||
}
|
||||
boolean ticking = BlockTypesCache.ticking[ordinal];
|
||||
boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal];
|
||||
boolean replacedWasLiquid = false;
|
||||
BlockState replacedState = null;
|
||||
if (!ticking) {
|
||||
// If the block being replaced was not ticking, it cannot be a liquid
|
||||
if (!replacedWasTicking) {
|
||||
continue;
|
||||
}
|
||||
// If the block being replaced is not fluid, we do not need to worry
|
||||
if (!(replacedWasLiquid =
|
||||
(replacedState = BlockState.getFromOrdinal(replacedOrdinal)).getMaterial().isLiquid())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
BlockState state = BlockState.getFromOrdinal(ordinal);
|
||||
boolean liquid = state.getMaterial().isLiquid();
|
||||
int x = i & 15;
|
||||
int y = (i >> 8) & 15;
|
||||
int z = (i >> 4) & 15;
|
||||
BlockPos position = new BlockPos((chunk.getX() << 4) + x, (layer << 4) + y, (chunk.getZ() << 4) + z);
|
||||
if (liquid || replacedWasLiquid) {
|
||||
if (liquid) {
|
||||
addFluid(getBlocks.serverLevel, state, position);
|
||||
continue;
|
||||
}
|
||||
// If the replaced fluid (is?) adjacent to water. Do not bother to check adjacent chunks(sections) as this
|
||||
// may be time consuming. Chances are any fluid blocks in adjacent chunks are being replaced or will end up
|
||||
// being ticked anyway. We only need it to be "hit" once.
|
||||
if (!wasAdjacentToWater(get, set, i, x, y, z)) {
|
||||
continue;
|
||||
}
|
||||
addFluid(getBlocks.serverLevel, replacedState, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Extent construct(final Extent child) {
|
||||
throw new UnsupportedOperationException("Processing only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessorScope getScope() {
|
||||
return ProcessorScope.READING_SET_BLOCKS;
|
||||
}
|
||||
|
||||
private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) {
|
||||
if (set == null || get == null) {
|
||||
return false;
|
||||
}
|
||||
char ordinal;
|
||||
char reserved = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
if (x > 0 && set[i - 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (x < 15 && set[i + 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z > 0 && set[i - 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z < 15 && set[i + 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y > 0 && set[i - 256] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y < 15 && set[i + 256] != reserved) {
|
||||
return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private boolean isFluid(char ordinal) {
|
||||
return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) {
|
||||
Fluid type;
|
||||
if (replacedState.getBlockType() == BlockTypes.LAVA) {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.LAVA : Fluids.FLOWING_LAVA;
|
||||
} else {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.WATER : Fluids.FLOWING_WATER;
|
||||
}
|
||||
serverLevel.scheduleTick(
|
||||
position,
|
||||
type,
|
||||
type.getTickDelay(serverLevel)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -6,6 +6,7 @@ import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||
@ -690,4 +691,9 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBatchProcessor getTickingPostProcessor() {
|
||||
return new PaperweightPostProcessor();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -99,7 +99,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
private final int maxHeight;
|
||||
private final int minSectionPosition;
|
||||
private final int maxSectionPosition;
|
||||
private final IdMap<Holder<Biome>> biomeRegistry;
|
||||
private final Registry<Biome> biomeRegistry;
|
||||
private final IdMap<Holder<Biome>> biomeHolderIdMap;
|
||||
private LevelChunkSection[] sections;
|
||||
private LevelChunk levelChunk;
|
||||
private DataLayer[] blockLight;
|
||||
@ -124,7 +125,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
this.maxSectionPosition = maxHeight >> 4;
|
||||
this.skyLight = new DataLayer[getSectionCount()];
|
||||
this.blockLight = new DataLayer[getSectionCount()];
|
||||
this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY).asHolderIdMap();
|
||||
this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
|
||||
this.biomeHolderIdMap = biomeRegistry.asHolderIdMap();
|
||||
}
|
||||
|
||||
public int getChunkX() {
|
||||
@ -399,11 +401,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
@SuppressWarnings("rawtypes")
|
||||
public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finalizer) {
|
||||
forceLoadSections = false;
|
||||
copy = createCopy ? new PaperweightGetBlocks_Copy(serverLevel) : null;
|
||||
copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null;
|
||||
try {
|
||||
ServerLevel nmsWorld = serverLevel;
|
||||
LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ);
|
||||
boolean fastmode = set.isFastMode() && Settings.settings().QUEUE.NO_TICK_FASTMODE;
|
||||
|
||||
// Remove existing tiles. Create a copy so that we can remove blocks
|
||||
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
|
||||
@ -466,12 +467,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
if (existingSection == null) {
|
||||
PalettedContainer<Holder<Biome>> biomeData = PaperweightPlatformAdapter.getBiomePalettedContainer(
|
||||
biomes[setSectionIndex],
|
||||
biomeRegistry
|
||||
biomeHolderIdMap
|
||||
);
|
||||
LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection(
|
||||
layerNo,
|
||||
new char[4096],
|
||||
fastmode,
|
||||
adapter,
|
||||
biomeRegistry,
|
||||
biomeData
|
||||
@ -527,19 +527,18 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
|
||||
if (existingSection == null) {
|
||||
PalettedContainer<Holder<Biome>> biomeData = biomes == null ? new PalettedContainer<>(
|
||||
biomeRegistry,
|
||||
biomeRegistry.byIdOrThrow(WorldEditPlugin
|
||||
biomeHolderIdMap,
|
||||
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(
|
||||
BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES,
|
||||
null
|
||||
) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeRegistry);
|
||||
) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap);
|
||||
newSection = PaperweightPlatformAdapter.newChunkSection(
|
||||
layerNo,
|
||||
setArr,
|
||||
fastmode,
|
||||
adapter,
|
||||
biomeRegistry,
|
||||
biomeData
|
||||
@ -562,9 +561,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
}
|
||||
}
|
||||
PaperweightPlatformAdapter.fieldTickingBlockCount.set(existingSection, (short) 0);
|
||||
|
||||
//ensure that the server doesn't try to tick the chunksection while we're editing it.
|
||||
//ensure that the server doesn't try to tick the chunksection while we're editing it. (Again)
|
||||
PaperweightPlatformAdapter.clearCounts(existingSection);
|
||||
existingSection.tickingList.clear();
|
||||
DelegateSemaphore lock = PaperweightPlatformAdapter.applyLock(existingSection);
|
||||
|
||||
synchronized (lock) {
|
||||
@ -601,7 +601,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
layerNo,
|
||||
this::loadPrivately,
|
||||
setArr,
|
||||
fastmode,
|
||||
adapter,
|
||||
biomeRegistry,
|
||||
biomeData
|
||||
@ -1070,7 +1069,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
biomeRegistry.byIdOrThrow(WorldEditPlugin
|
||||
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(biomeType))
|
||||
|
@ -20,6 +20,7 @@ import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -37,13 +38,15 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
private final char[][] blocks;
|
||||
private final int minHeight;
|
||||
private final int maxHeight;
|
||||
private final ServerLevel serverLevel;
|
||||
final ServerLevel serverLevel;
|
||||
final LevelChunk levelChunk;
|
||||
private PalettedContainer<Holder<Biome>>[] biomes = null;
|
||||
|
||||
protected PaperweightGetBlocks_Copy(ServerLevel world) {
|
||||
this.serverLevel = world;
|
||||
this.minHeight = world.getMinBuildHeight();
|
||||
this.maxHeight = world.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive.
|
||||
protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) {
|
||||
this.levelChunk = levelChunk;
|
||||
this.serverLevel = levelChunk.level;
|
||||
this.minHeight = serverLevel.getMinBuildHeight();
|
||||
this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive.
|
||||
this.blocks = new char[getSectionCount()][];
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@ import com.mojang.datafixers.util.Either;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
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.BlockState;
|
||||
@ -84,10 +83,9 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
public static final Field fieldStorage;
|
||||
public static final Field fieldPalette;
|
||||
|
||||
|
||||
public static final Field fieldTickingFluidCount;
|
||||
public static final Field fieldTickingBlockCount;
|
||||
public static final Field fieldNonEmptyBlockCount;
|
||||
private static final Field fieldTickingFluidCount;
|
||||
private static final Field fieldTickingBlockCount;
|
||||
private static final Field fieldNonEmptyBlockCount;
|
||||
|
||||
private static final MethodHandle methodGetVisibleChunk;
|
||||
|
||||
@ -303,16 +301,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
NMS conversion
|
||||
*/
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer, final char[] blocks, boolean fastmode,
|
||||
CachedBukkitAdapter adapter, IdMap<Holder<Biome>> biomeRegistry,
|
||||
final int layer,
|
||||
final char[] blocks,
|
||||
CachedBukkitAdapter adapter,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
return newChunkSection(layer, null, blocks, fastmode, adapter, biomeRegistry, biomes);
|
||||
return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes);
|
||||
}
|
||||
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer, final Function<Integer, char[]> get, char[] set,
|
||||
boolean fastmode, CachedBukkitAdapter adapter, IdMap<Holder<Biome>> biomeRegistry,
|
||||
final int layer,
|
||||
final Function<Integer, char[]> get,
|
||||
char[] set,
|
||||
CachedBukkitAdapter adapter,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
if (set == null) {
|
||||
@ -323,24 +326,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
final long[] blockStates = FaweCache.INSTANCE.BLOCK_STATES.get();
|
||||
final int[] blocksCopy = FaweCache.INSTANCE.SECTION_BLOCKS.get();
|
||||
try {
|
||||
int[] num_palette_buffer = new int[1];
|
||||
Map<BlockVector3, Integer> ticking_blocks = new HashMap<>();
|
||||
int air;
|
||||
int num_palette;
|
||||
if (get == null) {
|
||||
air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer,
|
||||
set, ticking_blocks, fastmode, adapter
|
||||
);
|
||||
num_palette = createPalette(blockToPalette, paletteToBlock, blocksCopy, set, adapter);
|
||||
} else {
|
||||
air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy,
|
||||
num_palette_buffer, get, set, ticking_blocks, fastmode, adapter
|
||||
);
|
||||
num_palette = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, get, set, adapter);
|
||||
}
|
||||
int num_palette = num_palette_buffer[0];
|
||||
// BlockStates
|
||||
|
||||
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
|
||||
Object configuration =
|
||||
PalettedContainer.Strategy.SECTION_STATES.getConfiguration(new FakeIdMapBlock(num_palette), bitsPerEntry);
|
||||
if (bitsPerEntry > 0 && bitsPerEntry < 5) {
|
||||
bitsPerEntry = 4;
|
||||
} else if (bitsPerEntry > 8) {
|
||||
@ -367,7 +360,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
} else {
|
||||
nmsBits = new SimpleBitStorage(bitsPerEntry, 4096, bits);
|
||||
}
|
||||
final Palette<net.minecraft.world.level.block.state.BlockState> blockStatePalette;
|
||||
List<net.minecraft.world.level.block.state.BlockState> palette;
|
||||
if (bitsPerEntry < 9) {
|
||||
palette = new ArrayList<>();
|
||||
@ -391,62 +383,43 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
nmsBits,
|
||||
palette
|
||||
);
|
||||
LevelChunkSection levelChunkSection;
|
||||
try {
|
||||
//fieldStorage.set(dataPaletteBlocks, nmsBits);
|
||||
//fieldPalette.set(dataPaletteBlocks, blockStatePalettedContainer);
|
||||
if (biomes == null) {
|
||||
IdMap<Holder<Biome>> biomeHolderIdMap = biomeRegistry.asHolderIdMap();
|
||||
biomes = new PalettedContainer<>(
|
||||
biomeRegistry,
|
||||
biomeRegistry.byIdOrThrow(WorldEditPlugin
|
||||
biomeHolderIdMap,
|
||||
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(BiomeTypes.PLAINS)),
|
||||
.getInternalBiomeId(
|
||||
BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES,
|
||||
null
|
||||
);
|
||||
}
|
||||
levelChunkSection = new LevelChunkSection(layer, blockStatePalettedContainer, biomes);
|
||||
setCount(ticking_blocks.size(), 4096 - air, levelChunkSection);
|
||||
if (!fastmode) {
|
||||
ticking_blocks.forEach((pos, ordinal) -> levelChunkSection.setBlockState(
|
||||
pos.getBlockX(),
|
||||
pos.getBlockY(),
|
||||
pos.getBlockZ(),
|
||||
Block.stateById(ordinal)
|
||||
));
|
||||
}
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return levelChunkSection;
|
||||
return new LevelChunkSection(layer, blockStatePalettedContainer, biomes);
|
||||
} catch (final Throwable e) {
|
||||
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // Only deprecated in paper
|
||||
private static LevelChunkSection newChunkSection(
|
||||
int layer, IdMap<Holder<Biome>> biomeRegistry,
|
||||
int layer,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
if (biomes == null) {
|
||||
return new LevelChunkSection(layer, biomeRegistry);
|
||||
}
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> dataPaletteBlocks = new PalettedContainer<>(
|
||||
Block.BLOCK_STATE_REGISTRY,
|
||||
Blocks.AIR.defaultBlockState(),
|
||||
PalettedContainer.Strategy.SECTION_STATES,
|
||||
null
|
||||
);
|
||||
PalettedContainer<Holder<Biome>> biomesPalette = biomes != null ? biomes : new PalettedContainer<>(
|
||||
biomeRegistry,
|
||||
biomeRegistry.byIdOrThrow(WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES,
|
||||
null
|
||||
);
|
||||
return new LevelChunkSection(layer, dataPaletteBlocks, biomesPalette);
|
||||
return new LevelChunkSection(layer, dataPaletteBlocks, biomes);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -556,11 +529,9 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
return biomePalettedContainer;
|
||||
}
|
||||
|
||||
public static void setCount(final int tickingBlockCount, final int nonEmptyBlockCount, final LevelChunkSection section) throws
|
||||
IllegalAccessException {
|
||||
fieldTickingFluidCount.setShort(section, (short) 0); // TODO FIXME
|
||||
fieldTickingBlockCount.setShort(section, (short) tickingBlockCount);
|
||||
fieldNonEmptyBlockCount.setShort(section, (short) nonEmptyBlockCount);
|
||||
public static void clearCounts(final LevelChunkSection section) throws IllegalAccessException {
|
||||
fieldTickingFluidCount.setShort(section, (short) 0);
|
||||
fieldTickingBlockCount.setShort(section, (short) 0);
|
||||
}
|
||||
|
||||
public static BiomeType adapt(Holder<Biome> biome, LevelAccessor levelAccessor) {
|
||||
|
@ -0,0 +1,175 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunk;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.fastasyncworldedit.core.registry.state.PropertyKey;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PaperweightPostProcessor implements IBatchProcessor {
|
||||
|
||||
@Override
|
||||
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
return set;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final IChunkSet iChunkSet) {
|
||||
boolean tickFluid = Settings.settings().EXPERIMENTAL.ALLOW_TICK_FLUIDS;
|
||||
// The PostProcessor shouldn't be added, but just in case
|
||||
if (!tickFluid) {
|
||||
return;
|
||||
}
|
||||
PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet;
|
||||
layer:
|
||||
for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) {
|
||||
char[] set = iChunkSet.loadIfPresent(layer);
|
||||
if (set == null) {
|
||||
// No edit means no need to process
|
||||
continue;
|
||||
}
|
||||
char[] get = null;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char ordinal = set[i];
|
||||
char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
boolean fromGet = false; // Used for liquids
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
// If this is null, then it's because we're loading a layer in the range of 0->15, but blocks aren't
|
||||
// actually being set
|
||||
if (get == null) {
|
||||
continue layer;
|
||||
}
|
||||
fromGet = true;
|
||||
ordinal = replacedOrdinal = get[i];
|
||||
}
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
continue;
|
||||
} else if (!fromGet) { // if fromGet, don't do the same again
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
replacedOrdinal = get[i];
|
||||
}
|
||||
boolean ticking = BlockTypesCache.ticking[ordinal];
|
||||
boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal];
|
||||
boolean replacedWasLiquid = false;
|
||||
BlockState replacedState = null;
|
||||
if (!ticking) {
|
||||
// If the block being replaced was not ticking, it cannot be a liquid
|
||||
if (!replacedWasTicking) {
|
||||
continue;
|
||||
}
|
||||
// If the block being replaced is not fluid, we do not need to worry
|
||||
if (!(replacedWasLiquid =
|
||||
(replacedState = BlockState.getFromOrdinal(replacedOrdinal)).getMaterial().isLiquid())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
BlockState state = BlockState.getFromOrdinal(ordinal);
|
||||
boolean liquid = state.getMaterial().isLiquid();
|
||||
int x = i & 15;
|
||||
int y = (i >> 8) & 15;
|
||||
int z = (i >> 4) & 15;
|
||||
BlockPos position = new BlockPos((chunk.getX() << 4) + x, (layer << 4) + y, (chunk.getZ() << 4) + z);
|
||||
if (liquid || replacedWasLiquid) {
|
||||
if (liquid) {
|
||||
addFluid(getBlocks.serverLevel, state, position);
|
||||
continue;
|
||||
}
|
||||
// If the replaced fluid (is?) adjacent to water. Do not bother to check adjacent chunks(sections) as this
|
||||
// may be time consuming. Chances are any fluid blocks in adjacent chunks are being replaced or will end up
|
||||
// being ticked anyway. We only need it to be "hit" once.
|
||||
if (!wasAdjacentToWater(get, set, i, x, y, z)) {
|
||||
continue;
|
||||
}
|
||||
addFluid(getBlocks.serverLevel, replacedState, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Extent construct(final Extent child) {
|
||||
throw new UnsupportedOperationException("Processing only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessorScope getScope() {
|
||||
return ProcessorScope.READING_SET_BLOCKS;
|
||||
}
|
||||
|
||||
private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) {
|
||||
if (set == null || get == null) {
|
||||
return false;
|
||||
}
|
||||
char ordinal;
|
||||
char reserved = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
if (x > 0 && set[i - 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (x < 15 && set[i + 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z > 0 && set[i - 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z < 15 && set[i + 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y > 0 && set[i - 256] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y < 15 && set[i + 256] != reserved) {
|
||||
return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private boolean isFluid(char ordinal) {
|
||||
return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) {
|
||||
Fluid type;
|
||||
if (replacedState.getBlockType() == BlockTypes.LAVA) {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.LAVA : Fluids.FLOWING_LAVA;
|
||||
} else {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.WATER : Fluids.FLOWING_WATER;
|
||||
}
|
||||
serverLevel.scheduleTick(
|
||||
position,
|
||||
type,
|
||||
type.getTickDelay(serverLevel)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
Binäre Datei nicht angezeigt.
@ -1,25 +1,21 @@
|
||||
package com.fastasyncworldedit.bukkit.adapter;
|
||||
|
||||
import com.fastasyncworldedit.core.FAWEPlatformAdapterImpl;
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class NMSAdapter implements FAWEPlatformAdapterImpl {
|
||||
|
||||
public static int createPalette(
|
||||
int[] blockToPalette, int[] paletteToBlock, int[] blocksCopy,
|
||||
int[] num_palette_buffer, char[] set, Map<BlockVector3, Integer> ticking_blocks, boolean fastmode,
|
||||
int[] blockToPalette,
|
||||
int[] paletteToBlock,
|
||||
int[] blocksCopy,
|
||||
char[] set,
|
||||
CachedBukkitAdapter adapter
|
||||
) {
|
||||
int air = 0;
|
||||
int num_palette = 0;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char ordinal = set[i];
|
||||
@ -42,52 +38,26 @@ public class NMSAdapter implements FAWEPlatformAdapterImpl {
|
||||
}
|
||||
System.arraycopy(adapter.getOrdinalToIbdID(), 0, blockToPalette, 0, adapter.getOrdinalToIbdID().length);
|
||||
}
|
||||
char lastOrdinal = 0;
|
||||
boolean lastticking = false;
|
||||
boolean tick_placed = Settings.settings().EXPERIMENTAL.ALLOW_TICK_PLACED;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char ordinal = set[i];
|
||||
switch (ordinal) {
|
||||
case BlockTypesCache.ReservedIDs.__RESERVED__:
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
ordinal = BlockTypesCache.ReservedIDs.AIR;
|
||||
case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR:
|
||||
air++;
|
||||
break;
|
||||
default:
|
||||
if (!fastmode && !tick_placed) {
|
||||
boolean ticking;
|
||||
if (ordinal != lastOrdinal) {
|
||||
ticking = BlockTypesCache.ticking[ordinal];
|
||||
lastOrdinal = ordinal;
|
||||
lastticking = ticking;
|
||||
} else {
|
||||
ticking = lastticking;
|
||||
}
|
||||
if (ticking) {
|
||||
BlockState state = BlockState.getFromOrdinal(ordinal);
|
||||
ticking_blocks
|
||||
.put(
|
||||
BlockVector3.at(i & 15, (i >> 8) & 15, (i >> 4) & 15),
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter()
|
||||
.getInternalBlockStateId(state).orElse(0)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
int palette = blockToPalette[ordinal];
|
||||
blocksCopy[i] = palette;
|
||||
}
|
||||
num_palette_buffer[0] = num_palette;
|
||||
return air;
|
||||
return num_palette;
|
||||
}
|
||||
|
||||
public static int createPalette(
|
||||
int layer, int[] blockToPalette, int[] paletteToBlock,
|
||||
int[] blocksCopy, int[] num_palette_buffer, Function<Integer, char[]> get, char[] set,
|
||||
Map<BlockVector3, Integer> ticking_blocks, boolean fastmode,
|
||||
int layer,
|
||||
int[] blockToPalette,
|
||||
int[] paletteToBlock,
|
||||
int[] blocksCopy,
|
||||
Function<Integer, char[]> get,
|
||||
char[] set,
|
||||
CachedBukkitAdapter adapter
|
||||
) {
|
||||
int air = 0;
|
||||
int num_palette = 0;
|
||||
char[] getArr = null;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
@ -117,73 +87,21 @@ public class NMSAdapter implements FAWEPlatformAdapterImpl {
|
||||
}
|
||||
System.arraycopy(adapter.getOrdinalToIbdID(), 0, blockToPalette, 0, adapter.getOrdinalToIbdID().length);
|
||||
}
|
||||
char lastOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
boolean lastticking = false;
|
||||
boolean tick_placed = Settings.settings().EXPERIMENTAL.ALLOW_TICK_PLACED;
|
||||
boolean tick_existing = Settings.settings().EXPERIMENTAL.ALLOW_TICK_EXISTING;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char ordinal = set[i];
|
||||
switch (ordinal) {
|
||||
case BlockTypesCache.ReservedIDs.__RESERVED__ -> {
|
||||
char ordinal= set[i];
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
if (getArr == null) {
|
||||
getArr = get.apply(layer);
|
||||
}
|
||||
set[i] = switch (ordinal = getArr[i]) {
|
||||
case BlockTypesCache.ReservedIDs.__RESERVED__:
|
||||
if ((ordinal = getArr[i]) == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
ordinal = BlockTypesCache.ReservedIDs.AIR;
|
||||
case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR:
|
||||
air++;
|
||||
yield ordinal;
|
||||
default:
|
||||
if (!fastmode && !tick_placed && tick_existing) {
|
||||
boolean ticking;
|
||||
if (ordinal != lastOrdinal) {
|
||||
ticking = BlockTypesCache.ticking[ordinal];
|
||||
lastOrdinal = ordinal;
|
||||
lastticking = ticking;
|
||||
} else {
|
||||
ticking = lastticking;
|
||||
}
|
||||
if (ticking) {
|
||||
BlockState state = BlockState.getFromOrdinal(ordinal);
|
||||
ticking_blocks.put(BlockVector3.at(i & 15, (i >> 8) & 15, (i >> 4) & 15),
|
||||
WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBlockStateId(state)
|
||||
.orElse(0)
|
||||
);
|
||||
}
|
||||
}
|
||||
yield ordinal;
|
||||
};
|
||||
}
|
||||
case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR -> air++;
|
||||
}
|
||||
if (!fastmode && tick_placed) {
|
||||
boolean ticking;
|
||||
if (ordinal != lastOrdinal) {
|
||||
ticking = BlockTypesCache.ticking[ordinal];
|
||||
lastOrdinal = ordinal;
|
||||
lastticking = ticking;
|
||||
} else {
|
||||
ticking = lastticking;
|
||||
}
|
||||
if (ticking) {
|
||||
BlockState state = BlockState.getFromOrdinal(ordinal);
|
||||
ticking_blocks.put(
|
||||
BlockVector3.at(i & 15, (i >> 8) & 15, (i >> 4) & 15),
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter()
|
||||
.getInternalBlockStateId(state).orElse(0)
|
||||
);
|
||||
}
|
||||
}
|
||||
int palette = blockToPalette[ordinal];
|
||||
blocksCopy[i] = palette;
|
||||
}
|
||||
|
||||
num_palette_buffer[0] = num_palette;
|
||||
return air;
|
||||
return num_palette;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -20,7 +20,9 @@
|
||||
package com.sk89q.worldedit.bukkit;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.util.MinecraftVersion;
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.sk89q.bukkit.util.CommandInfo;
|
||||
import com.sk89q.bukkit.util.CommandRegistration;
|
||||
@ -289,5 +291,17 @@ public class BukkitServerInterface extends AbstractPlatform implements MultiUser
|
||||
public int versionMaxY() {
|
||||
return MinecraftVersion.getCurrent().isEqualOrHigherThan(MinecraftVersion.CAVES_18) ? 319 : 255;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBatchProcessor getPlatformPostProcessor(boolean fastMode) {
|
||||
boolean tickFluid = Settings.settings().EXPERIMENTAL.ALLOW_TICK_FLUIDS;
|
||||
if (!tickFluid) {
|
||||
return null;
|
||||
}
|
||||
if (Settings.settings().QUEUE.NO_TICK_FASTMODE && fastMode) {
|
||||
return null;
|
||||
}
|
||||
return this.plugin.getBukkitImplAdapter().getTickingPostProcessor();
|
||||
}
|
||||
//FAWE end
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import com.fastasyncworldedit.bukkit.adapter.IBukkitAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
||||
import com.sk89q.jnbt.AdventureNBTConverter;
|
||||
@ -350,5 +351,15 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
|
||||
default Map<String, List<Property<?>>> getAllProperties() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link IBatchProcessor} instance for post-processing of chunks to sort ticking of placed/existing blocks and
|
||||
* fluids if the plugin is configured to do so
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
default IBatchProcessor getTickingPostProcessor() {
|
||||
return null;
|
||||
}
|
||||
//FAWE end
|
||||
}
|
||||
|
@ -604,7 +604,7 @@ public enum FaweCache implements Trimable {
|
||||
} else if (throwable.getCause() instanceof FaweException) {
|
||||
handleFaweException((FaweException) throwable.getCause());
|
||||
} else {
|
||||
int hash = throwable.getMessage().hashCode();
|
||||
int hash = throwable.getMessage() != null ? throwable.getMessage().hashCode() : 0;
|
||||
if (hash != lastException) {
|
||||
lastException = hash;
|
||||
LOGGER.catching(throwable);
|
||||
|
@ -567,7 +567,8 @@ public class Settings extends Config {
|
||||
public int DISCARD_AFTER_MS = 60000;
|
||||
|
||||
@Comment({
|
||||
"When using fastmode also do not bother to fix existing ticking blocks"
|
||||
"When using fastmode do not bother to tick existing/placed blocks/fluids",
|
||||
"Only works in versions up to 1.17.1"
|
||||
})
|
||||
public boolean NO_TICK_FASTMODE = true;
|
||||
|
||||
@ -625,16 +626,11 @@ public class Settings extends Config {
|
||||
public boolean OTHER = false;
|
||||
|
||||
@Comment({
|
||||
"Allow blocks placed by WorldEdit to tick. This could cause the big lags.",
|
||||
"This has no effect on existing blocks one way or the other."
|
||||
"Allow fluids placed by FAWE to tick (flow). This could cause the big lags.",
|
||||
"This has no effect on existing blocks one way or the other.",
|
||||
"Changes due to fluid flow will not be tracked by history, thus may have unintended consequences"
|
||||
})
|
||||
public boolean ALLOW_TICK_PLACED = false;
|
||||
|
||||
@Comment({
|
||||
"Force re-ticking of existing blocks not edited by FAWE.",
|
||||
"This will increase time taken slightly."
|
||||
})
|
||||
public boolean ALLOW_TICK_EXISTING = true;
|
||||
public boolean ALLOW_TICK_FLUIDS = false;
|
||||
|
||||
@Comment({
|
||||
"Sets a maximum limit (in kb) for the size of a player's schematics directory (per-player mode only)",
|
||||
|
@ -167,11 +167,6 @@ public class DisallowedBlocksExtent extends AbstractDelegateExtent implements IB
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<IChunkSet> postProcessSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
return CompletableFuture.completedFuture(set);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Extent construct(final Extent child) {
|
||||
|
@ -56,9 +56,4 @@ public class HeightBoundExtent extends FaweRegionExtent {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
return CompletableFuture.completedFuture(set);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -178,8 +178,13 @@ public class MultiRegionExtent extends FaweRegionExtent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
public Future<?> postProcessSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
return intersection.postProcessSet(chunk, get, set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
intersection.postProcess(chunk, get, set);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -342,7 +342,12 @@ public class NullExtent extends FaweRegionExtent implements IBatchProcessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
public Future<?> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
throw reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
throw reason;
|
||||
}
|
||||
|
||||
|
@ -46,10 +46,17 @@ public class SingleRegionExtent extends FaweRegionExtent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
public Future<?> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
// Most likely will do nothing, but perhaps people will find some fun way of using this via API (though doubtful)
|
||||
return region.postProcessSet(chunk, get, set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
// Most likely will do nothing, but perhaps people will find some fun way of using this via API (though doubtful)
|
||||
region.postProcess(chunk, get, set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processGet(int chunkX, int chunkZ) {
|
||||
return region.containsChunk(chunkX, chunkZ);
|
||||
|
@ -143,11 +143,6 @@ public class StripNBTExtent extends AbstractDelegateExtent implements IBatchProc
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<IChunkSet> postProcessSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
return CompletableFuture.completedFuture(set);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Extent construct(final Extent child) {
|
||||
|
@ -28,10 +28,15 @@ public class BatchProcessorHolder implements IBatchProcessorHolder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
public Future<?> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
return getPostProcessor().postProcessSet(chunk, get, set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
getPostProcessor().postProcess(chunk, get, set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
getProcessor().flush();
|
||||
|
@ -29,13 +29,6 @@ public final class EmptyBatchProcessor implements IBatchProcessor {
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public Future<IChunkSet> postProcessSet(@Nullable IChunk chunk, @Nullable IChunkGet get, @Nullable IChunkSet set) {
|
||||
// Doesn't need to do anything
|
||||
return CompletableFuture.completedFuture(set);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public IBatchProcessor join(@Nullable IBatchProcessor other) {
|
||||
return other;
|
||||
|
@ -31,10 +31,15 @@ public interface IBatchProcessorHolder extends IBatchProcessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
default Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
default Future<?> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
return getPostProcessor().postProcessSet(chunk, get, set);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
getPostProcessor().postProcess(chunk, get, set);
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean processGet(int chunkX, int chunkZ) {
|
||||
return getProcessor().processGet(chunkX, chunkZ);
|
||||
|
@ -9,6 +9,7 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunk;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.fastasyncworldedit.core.util.MultiFuture;
|
||||
import com.fastasyncworldedit.core.util.StringMan;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
@ -24,7 +25,6 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Supplier;
|
||||
@ -129,19 +129,15 @@ public class MultiBatchProcessor implements IBatchProcessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
try {
|
||||
public Future<?> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
List<Future<?>> futures = new ArrayList<>();
|
||||
for (IBatchProcessor processor : processors) {
|
||||
try {
|
||||
// We do NOT want to edit blocks in post processing
|
||||
if (processor.getScope() != ProcessorScope.READING_SET_BLOCKS) {
|
||||
continue;
|
||||
}
|
||||
set = processor.postProcessSet(chunk, get, set).get();
|
||||
if (set == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return CompletableFuture.completedFuture(set);
|
||||
futures.add(processor.postProcessSet(chunk, get, set));
|
||||
} catch (Throwable e) {
|
||||
if (e instanceof FaweException) {
|
||||
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) e, LOGGER);
|
||||
@ -149,7 +145,7 @@ public class MultiBatchProcessor implements IBatchProcessor {
|
||||
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) e.getCause(), LOGGER);
|
||||
} else {
|
||||
String message = e.getMessage();
|
||||
int hash = message.hashCode();
|
||||
int hash = message != null ? message.hashCode() : 0;
|
||||
if (lastException != hash) {
|
||||
lastException = hash;
|
||||
exceptionCount = 0;
|
||||
@ -159,7 +155,38 @@ public class MultiBatchProcessor implements IBatchProcessor {
|
||||
LOGGER.warn(message);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return new MultiFuture(futures);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
for (IBatchProcessor processor : processors) {
|
||||
try {
|
||||
// We do NOT want to edit blocks in post processing
|
||||
if (processor.getScope() != ProcessorScope.READING_SET_BLOCKS) {
|
||||
continue;
|
||||
}
|
||||
processor.postProcess(chunk, get, set);
|
||||
} catch (Throwable e) {
|
||||
if (e instanceof FaweException) {
|
||||
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) e, LOGGER);
|
||||
} else if (e.getCause() instanceof FaweException) {
|
||||
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) e.getCause(), LOGGER);
|
||||
} else {
|
||||
String message = e.getMessage();
|
||||
int hash = message != null ? message.hashCode() : 0;
|
||||
if (lastException != hash) {
|
||||
lastException = hash;
|
||||
exceptionCount = 0;
|
||||
LOGGER.catching(e);
|
||||
} else if (exceptionCount < Settings.settings().QUEUE.PARALLEL_THREADS) {
|
||||
exceptionCount++;
|
||||
LOGGER.warn(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,11 +24,6 @@ public final class NullProcessor implements IBatchProcessor {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Future<IChunkSet> postProcessSet(@Nonnull IChunk chunk, @Nonnull IChunkGet get, @Nonnull IChunkSet set) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Extent construct(@Nonnull Extent child) {
|
||||
return new NullExtent();
|
||||
|
@ -136,11 +136,6 @@ public class HeightmapProcessor implements IBatchProcessor {
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
return CompletableFuture.completedFuture(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Extent construct(Extent child) {
|
||||
|
@ -46,11 +46,6 @@ public class RelightProcessor implements IBatchProcessor {
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
return CompletableFuture.completedFuture(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable
|
||||
Extent construct(Extent child) {
|
||||
|
@ -228,8 +228,13 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<IChunkSet> postProcessSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
return (Future<IChunkSet>) addWriteTask(() -> processSet(chunk, get, set));
|
||||
public void postProcess(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
addWriteTask(() -> processSet(chunk, get, set));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> postProcessSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
return addWriteTask(() -> processSet(chunk, get, set));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -6,13 +6,12 @@ import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
|
||||
@ -23,7 +22,27 @@ public interface IBatchProcessor {
|
||||
*/
|
||||
IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set);
|
||||
|
||||
Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set);
|
||||
/**
|
||||
* Post-process a chunk that has been edited. Set should NOT be modified here, changes will NOT be flushed to the world,
|
||||
* but MAY be flushed to history. Defaults to nothing as most Processors will not use it. Post-processors that are not
|
||||
* technically blocking should override this method to allow post-processors to become blocking if required.
|
||||
*/
|
||||
default Future<?> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
// Do not need to default to below method. FAWE itself by default will only call the method below.
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process a chunk that has been edited. Set should NOT be modified here, changes will NOT be flushed to the world,
|
||||
* but MAY be flushed to history. Defaults to nothing as most Processors will not use it. If the post-processor will run
|
||||
* tasks asynchronously/not be blocking, use {@link IBatchProcessor#postProcessSet} to return a Future.
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
default void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
// Default to above for compatibility and to ensure whatever method is overridden by child classes is called
|
||||
postProcessSet(chunk, get, set);
|
||||
}
|
||||
|
||||
default boolean processGet(int chunkX, int chunkZ) {
|
||||
return true;
|
||||
|
@ -145,7 +145,7 @@ public class ParallelQueueExtent extends PassthroughExtent {
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
String message = e.getMessage();
|
||||
int hash = message.hashCode();
|
||||
int hash = message != null ? message.hashCode() : 0;
|
||||
if (lastException != hash) {
|
||||
lastException = hash;
|
||||
exceptionCount = 0;
|
||||
|
@ -401,7 +401,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
|
||||
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) e.getCause(), LOGGER);
|
||||
} else {
|
||||
String message = e.getMessage();
|
||||
int hash = message.hashCode();
|
||||
int hash = message != null ? message.hashCode() : 0;
|
||||
if (lastException != hash) {
|
||||
lastException = hash;
|
||||
exceptionCount = 0;
|
||||
@ -441,7 +441,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
|
||||
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) e.getCause(), LOGGER);
|
||||
} else {
|
||||
String message = e.getMessage();
|
||||
int hash = message.hashCode();
|
||||
int hash = message != null ? message.hashCode() : 0;
|
||||
if (lastException != hash) {
|
||||
lastException = hash;
|
||||
exceptionCount = 0;
|
||||
|
@ -1010,7 +1010,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
|
||||
return get.call(set, finalize);
|
||||
} finally {
|
||||
if (postProcess) {
|
||||
getExtent().postProcessSet(this, get.getCopy(), set);
|
||||
getExtent().postProcess(this, get.getCopy(), set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
package com.fastasyncworldedit.core.util;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class MultiFuture implements Future<Object[]> {
|
||||
|
||||
private final List<Future<?>> futures;
|
||||
|
||||
public MultiFuture(List<Future<?>> futures) {
|
||||
this.futures = futures;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(final boolean mayInterruptIfRunning) {
|
||||
return futures.stream().allMatch(f -> f.cancel(mayInterruptIfRunning));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return futures.stream().allMatch(Future::isCancelled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return futures.stream().allMatch(Future::isDone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] get() {
|
||||
return futures.stream().map(f -> {
|
||||
try {
|
||||
return f.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
return e;
|
||||
}
|
||||
}).toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] get(final long timeout, @Nonnull final TimeUnit unit) {
|
||||
return futures.stream().map(f -> {
|
||||
try {
|
||||
return f.get(timeout / futures.size(), unit);
|
||||
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
return e;
|
||||
}
|
||||
}).toArray();
|
||||
}
|
||||
|
||||
}
|
@ -45,6 +45,7 @@ import com.fastasyncworldedit.core.history.changeset.BlockBagChangeSet;
|
||||
import com.fastasyncworldedit.core.history.changeset.NullChangeSet;
|
||||
import com.fastasyncworldedit.core.limit.FaweLimit;
|
||||
import com.fastasyncworldedit.core.limit.PropertyRemap;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent;
|
||||
@ -560,7 +561,7 @@ public final class EditSessionBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
// There's no need to do lighting (and it'll also just be a pain to implement) if we're not placing chunks
|
||||
// There's no need to do the below (and it'll also just be a pain to implement) if we're not placing chunks
|
||||
if (placeChunks) {
|
||||
if (((relightMode != null && relightMode != RelightMode.NONE) || (relightMode == null && Settings.settings().LIGHTING.MODE > 0))) {
|
||||
relighter = WorldEdit.getInstance().getPlatformManager()
|
||||
@ -569,6 +570,22 @@ public final class EditSessionBuilder {
|
||||
extent.addProcessor(new RelightProcessor(relighter));
|
||||
}
|
||||
extent.addProcessor(new HeightmapProcessor(world.getMinY(), world.getMaxY()));
|
||||
IBatchProcessor platformProcessor = WorldEdit
|
||||
.getInstance()
|
||||
.getPlatformManager()
|
||||
.queryCapability(Capability.WORLD_EDITING)
|
||||
.getPlatformProcessor(fastMode);
|
||||
if (platformProcessor != null) {
|
||||
extent.addProcessor(platformProcessor);
|
||||
}
|
||||
IBatchProcessor platformPostProcessor = WorldEdit
|
||||
.getInstance()
|
||||
.getPlatformManager()
|
||||
.queryCapability(Capability.WORLD_EDITING)
|
||||
.getPlatformPostProcessor(fastMode);
|
||||
if (platformPostProcessor != null) {
|
||||
extent.addPostProcessor(platformPostProcessor);
|
||||
}
|
||||
} else {
|
||||
relighter = NullRelighter.INSTANCE;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ package com.sk89q.worldedit.extension.platform;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.sk89q.worldedit.LocalConfiguration;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
@ -248,5 +249,24 @@ public interface Platform extends Keyed {
|
||||
* @since 2.0.0
|
||||
*/
|
||||
int versionMaxY();
|
||||
|
||||
/**
|
||||
* Get a {@link IBatchProcessor} to be used in edit processing. Null if not required.
|
||||
* @since TODO
|
||||
*/
|
||||
@Nullable
|
||||
default IBatchProcessor getPlatformProcessor(boolean fastMode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link IBatchProcessor} to be used in edit post-processing. Used for things such as tick-placed and tick fluids.
|
||||
* Null if not required.
|
||||
* @since TODO
|
||||
*/
|
||||
@Nullable
|
||||
default IBatchProcessor getPlatformPostProcessor(boolean fastMode) {
|
||||
return null;
|
||||
}
|
||||
//FAWE end
|
||||
}
|
||||
|
@ -111,12 +111,6 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce
|
||||
return filter.filter(chunk, get, set, MaskingExtent.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
// This should not do anything otherwise dangerous...
|
||||
return CompletableFuture.completedFuture(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBlock(final FilterBlock block) {
|
||||
if (!this.mask.test(block)) {
|
||||
|
@ -471,12 +471,6 @@ public interface Region extends Iterable<BlockVector3>, Cloneable, IBatchProcess
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
default Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||
// Doesn't need to do anything
|
||||
return CompletableFuture.completedFuture(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
default Extent construct(Extent child) {
|
||||
if (isGlobal()) {
|
||||
|
@ -23,6 +23,7 @@ import com.fastasyncworldedit.core.configuration.Caption;
|
||||
import com.fastasyncworldedit.core.queue.IChunk;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.fastasyncworldedit.core.util.MultiFuture;
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
@ -35,6 +36,7 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@ -172,6 +174,15 @@ public class RegionIntersection extends AbstractRegion {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> postProcessSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
final ArrayList<Future<?>> futures = new ArrayList<>();
|
||||
for (Region region : regions) {
|
||||
futures.add(region.postProcessSet(chunk, get, set));
|
||||
}
|
||||
return new MultiFuture(futures);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set, boolean asBlacklist) {
|
||||
if (!asBlacklist) {
|
||||
|
@ -198,6 +198,9 @@ public class BlockTypesCache {
|
||||
|
||||
public static final BlockType[] values;
|
||||
public static final BlockState[] states;
|
||||
/**
|
||||
* Array of blockstates in order of ordinal indicating if the block ticks, e.g. leaves, water
|
||||
*/
|
||||
public static final boolean[] ticking;
|
||||
private static final Map<String, List<Property<?>>> allProperties = new HashMap<>();
|
||||
|
||||
@ -283,7 +286,8 @@ public class BlockTypesCache {
|
||||
String enumName = (typeName.startsWith("minecraft:") ? typeName.substring(10) : typeName).toUpperCase(Locale.ROOT);
|
||||
int oldsize = states.size();
|
||||
BlockType existing = new BlockType(id, internalId, states);
|
||||
tickList.addAll(Collections.nCopies(states.size() - oldsize, existing.getMaterial().isTicksRandomly()));
|
||||
tickList.addAll(Collections.nCopies(states.size() - oldsize,
|
||||
existing.getMaterial().isTicksRandomly() || existing.getMaterial().isLiquid()));
|
||||
// register states
|
||||
BlockType.REGISTRY.register(typeName, existing);
|
||||
String nameSpace = typeName.substring(0, typeName.indexOf(':'));
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren