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:
Jordan 2022-03-10 14:27:25 +00:00 committet von GitHub
Ursprung 5d18e15128
Commit e9db749e2f
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 4AEE18F83AFDEB23
47 geänderte Dateien mit 975 neuen und 399 gelöschten Zeilen

4
.lift.toml Normale Datei
Datei anzeigen

@ -0,0 +1,4 @@
jdkVersion = "17"
build = "gradle clean build -x test"
tools = ["findsecbugs", "ErrorProne", "Semgrep", "Detekt", "ESLint", "Infer"]
ignoreRules = ["CatchAndPrintStackTrace", "ReferenceEquality", "FallThrough", "FutureReturnValueIgnored"]

Datei anzeigen

@ -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();
}
}

Datei anzeigen

@ -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();

Datei anzeigen

@ -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()][];
}

Datei anzeigen

@ -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) {

Datei anzeigen

@ -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)
);
}
}

Datei anzeigen

@ -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();
}
}

Datei anzeigen

@ -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()
);
}
}

Datei anzeigen

@ -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()][];
}

Datei anzeigen

@ -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,30 +383,15 @@ 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,
biomeRegistry.getOrThrow(Biomes.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);
if (biomes == null) {
biomes = new PalettedContainer<>(
biomeRegistry,
biomeRegistry.getOrThrow(Biomes.PLAINS),
PalettedContainer.Strategy.SECTION_BIOMES,
null
);
}
levelChunkSection = new LevelChunkSection(layer, blockStatePalettedContainer, biomes);
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) {

Datei anzeigen

@ -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)
);
}
}

Datei anzeigen

@ -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();
}
}

Datei anzeigen

@ -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))

Datei anzeigen

@ -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()][];
}

Datei anzeigen

@ -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) {
biomes = new PalettedContainer<>(
biomeRegistry,
biomeRegistry.byIdOrThrow(WorldEditPlugin
.getInstance()
.getBukkitImplAdapter()
.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);
if (biomes == null) {
IdMap<Holder<Biome>> biomeHolderIdMap = biomeRegistry.asHolderIdMap();
biomes = new PalettedContainer<>(
biomeHolderIdMap,
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
.getInstance()
.getBukkitImplAdapter()
.getInternalBiomeId(
BiomeTypes.PLAINS)),
PalettedContainer.Strategy.SECTION_BIOMES,
null
);
}
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) {

Datei anzeigen

@ -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)
);
}
}

Datei anzeigen

@ -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__:
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)
);
}
}
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
ordinal = BlockTypesCache.ReservedIDs.AIR;
}
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__ -> {
if (getArr == null) {
getArr = get.apply(layer);
}
set[i] = switch (ordinal = getArr[i]) {
case 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;
};
char ordinal= set[i];
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
if (getArr == null) {
getArr = get.apply(layer);
}
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)
);
if ((ordinal = getArr[i]) == BlockTypesCache.ReservedIDs.__RESERVED__) {
ordinal = BlockTypesCache.ReservedIDs.AIR;
}
}
int palette = blockToPalette[ordinal];
blocksCopy[i] = palette;
}
num_palette_buffer[0] = num_palette;
return air;
return num_palette;
}
@Override

Datei anzeigen

@ -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
}

Datei anzeigen

@ -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
}

Datei anzeigen

@ -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);

Datei anzeigen

@ -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)",

Datei anzeigen

@ -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) {

Datei anzeigen

@ -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);
}
}

Datei anzeigen

@ -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);
}
}

Datei anzeigen

@ -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;
}

Datei anzeigen

@ -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);

Datei anzeigen

@ -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) {

Datei anzeigen

@ -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();

Datei anzeigen

@ -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;

Datei anzeigen

@ -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);

Datei anzeigen

@ -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,37 +129,64 @@ public class MultiBatchProcessor implements IBatchProcessor {
}
@Override
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
try {
for (IBatchProcessor processor : processors) {
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;
futures.add(processor.postProcessSet(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);
}
}
}
return CompletableFuture.completedFuture(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.hashCode();
if (lastException != hash) {
lastException = hash;
exceptionCount = 0;
LOGGER.catching(e);
} else if (exceptionCount < Settings.settings().QUEUE.PARALLEL_THREADS) {
exceptionCount++;
LOGGER.warn(message);
}
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);
}
}
}
return null;
}
}

Datei anzeigen

@ -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();

Datei anzeigen

@ -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) {

Datei anzeigen

@ -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) {

Datei anzeigen

@ -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

Datei anzeigen

@ -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;

Datei anzeigen

@ -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;

Datei anzeigen

@ -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;

Datei anzeigen

@ -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);
}
}
}

Datei anzeigen

@ -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();
}
}

Datei anzeigen

@ -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;
}

Datei anzeigen

@ -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
}

Datei anzeigen

@ -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)) {

Datei anzeigen

@ -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()) {

Datei anzeigen

@ -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) {

Datei anzeigen

@ -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(':'));