3
0
Mirror von https://github.com/IntellectualSites/FastAsyncWorldEdit.git synchronisiert 2024-12-25 18:40:05 +01:00

feat: implement command to fix block connections (#2746)

* feat: implement command to fix block connections
 - closes #313

* set SETUP to true on setup

* Add (untested) processing capability and second passes where appropriate

* Minor refactor, add various javadocs

* Minor adjustments

* Better chest handling

* Utilise thread extends when able

* Add strings, changes to fastmode/side effect handling

* Cleanup

* Add for 1.21

* Adjustment for tall flowers - add capability to forcefully override block updates

* Move to Fawe/LinTag

* fix: adjustments, fix certain upside-down blocks

* Add for 1.21.3
Dieser Commit ist enthalten in:
Jordan 2024-12-15 19:59:40 +00:00 committet von GitHub
Ursprung afec252dca
Commit 632b6934a8
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: B5690EEEBB952194
69 geänderte Dateien mit 4663 neuen und 124 gelöschten Zeilen

Datei anzeigen

@ -857,12 +857,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
}
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.NEIGHBORS,
SideEffect.LIGHTING,
SideEffect.VALIDATION,
SideEffect.ENTITY_AI,
SideEffect.EVENTS,
SideEffect.UPDATE
//FAWE start - FAWE-supported side effects
SideEffect.HISTORY,
SideEffect.HEIGHTMAPS,
SideEffect.LIGHTING,
SideEffect.NEIGHBORS
//FAWE end
);
@Override

Datei anzeigen

@ -0,0 +1,237 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.PaperweightFaweAdapter;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.world.block.BlockTypes;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
public class PaperweightServerLevelDelegateProxy implements InvocationHandler {
private static final Logger LOGGER = LogManagerCompat.getLogger();
// FAWE start - extent not EditSession
private final Extent editSession;
//FAWE end
private final ServerLevel serverLevel;
//FAWE start - use FAWE adapter
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
//FAWE end
//FAWE start - force error if method not caught by this instance
private final boolean errorOnPassthrough;
//FAWE end
private PaperweightServerLevelDelegateProxy(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
this.editSession = editSession;
this.serverLevel = serverLevel;
//FAWE start
this.errorOnPassthrough = false;
//FAWE end
}
public static WorldGenLevel newInstance(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
return (WorldGenLevel) Proxy.newProxyInstance(
serverLevel.getClass().getClassLoader(),
serverLevel.getClass().getInterfaces(),
new PaperweightServerLevelDelegateProxy(editSession, serverLevel, adapter)
);
}
//FAWE start - force error if method not caught by this instance
private PaperweightServerLevelDelegateProxy(Extent extent, ServerLevel serverLevel, boolean errorOnPassthrough) {
this.editSession = extent;
this.serverLevel = serverLevel;
this.errorOnPassthrough = errorOnPassthrough;
}
public static WorldGenLevel newInstance(Extent extent, ServerLevel serverLevel, boolean errorOnPassthrough) {
return (WorldGenLevel) Proxy.newProxyInstance(
serverLevel.getClass().getClassLoader(),
serverLevel.getClass().getInterfaces(),
new PaperweightServerLevelDelegateProxy(extent, serverLevel, errorOnPassthrough)
);
}
//FAWE end
@Nullable
private BlockEntity getBlockEntity(BlockPos blockPos) {
BlockEntity tileEntity = this.serverLevel.getChunkAt(blockPos).getBlockEntity(blockPos);
if (tileEntity == null) {
return null;
}
BlockEntity newEntity = tileEntity.getType().create(blockPos, getBlockState(blockPos));
newEntity.load((CompoundTag) adapter.fromNativeLin(this.editSession.getFullBlock(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
).getNbtReference().getValue()));
return newEntity;
}
private BlockState getBlockState(BlockPos blockPos) {
return adapter.adapt(this.editSession.getBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
}
private boolean setBlock(BlockPos blockPos, BlockState blockState) {
try {
return editSession.setBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ(), adapter.adapt(blockState));
} catch (MaxChangedBlocksException e) {
throw new RuntimeException(e);
}
}
private boolean removeBlock(BlockPos blockPos, boolean bl) {
try {
return editSession.setBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ(), BlockTypes.AIR.getDefaultState());
} catch (MaxChangedBlocksException e) {
throw new RuntimeException(e);
}
}
private FluidState getFluidState(BlockPos pos) {
return getBlockState(pos).getFluidState();
}
private boolean isWaterAt(BlockPos pos) {
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//FAWE start - cannot use switch where method names are equal
String methodName = method.getName();
if (Refraction.pickName("getBlockState", "a_").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
// getBlockState
return getBlockState(blockPos);
}
}
if (Refraction.pickName("getBlockEntity", "c_").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
// getBlockEntity
return getBlockEntity(blockPos);
}
}
if ("a".equals(methodName) || "setBlock".equals(methodName) || "removeBlock".equals(methodName) || "destroyBlock".equals(
methodName)) {
if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) {
// setBlock
return setBlock(blockPos, blockState);
} else if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) {
// removeBlock (and also matches destroyBlock)
return removeBlock(blockPos, bl);
}
}
//FAWE start
if (Refraction.pickName("getFluidState", "b_").equals(methodName)) { //net.minecraft.world.level.BlockGetter
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
return getFluidState(blockPos);
}
}
if (Refraction.pickName("isWaterAt", "z").equals(methodName)) { //net.minecraft.world.level.LevelReader
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
return isWaterAt(blockPos);
}
}
if (Refraction.pickName("getEntities", "a_").equals(methodName)) { //net.minecraft.world.level.EntityGetter
if (args.length == 2 && args[0] instanceof Entity && args[1] instanceof AABB) {
return new ArrayList<>();
}
}
// Specific passthroughs that we want to allow
// net.minecraft.world.level.BlockAndTintGetter
if (Refraction.pickName("getRawBrightness", "b").equals(methodName)) {
return method.invoke(this.serverLevel, args);
}
// net.minecraft.world.level.LevelHeightAccessor
if (Refraction.pickName("getMaxBuildHeight", "al").equals(methodName)) {
if (args.length == 0) {
return method.invoke(this.serverLevel, args);
}
}
// net.minecraft.world.level.SignalGetter
if (Refraction.pickName("hasNeighborSignal", "C").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getSignal", "c").equals(methodName)) {
if (args.length == 2 && args[0] instanceof BlockPos && args[1] instanceof Direction) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getControlInputSignal", "a").equals(methodName)) {
if (args.length == 3 && args[0] instanceof BlockPos && args[1] instanceof Direction && args[2] instanceof Boolean) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getDirectSignal", "a").equals(methodName)) {
if (args.length == 2 && args[0] instanceof BlockPos && args[1] instanceof Direction) {
return method.invoke(this.serverLevel, args);
}
}
//FAWE start - force error if method not caught by this instance
if (errorOnPassthrough) {
LOGGER.error(
"""
Attempted passthough of method {}.
Method argument types: {}
Method argument values: {}
""",
method.getName(),
Arrays.stream(args).map(a -> a.getClass().getName()).toList(),
Arrays.stream(args).map(Object::toString).toList()
);
throw new FaweException("Method required passthrough.");
}
//FAWE end
return method.invoke(this.serverLevel, args);
}
}

Datei anzeigen

@ -4,6 +4,7 @@ import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
@ -13,6 +14,7 @@ import com.fastasyncworldedit.core.util.NbtUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
@ -21,6 +23,7 @@ import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt.PaperweightLazy
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.regen.PaperweightRegen;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
@ -33,7 +36,6 @@ import com.sk89q.worldedit.registry.state.IntegerProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.world.RegenOptions;
import com.sk89q.worldedit.world.biome.BiomeType;
@ -284,9 +286,16 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return state.toBaseBlock();
}
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.HISTORY,
SideEffect.HEIGHTMAPS,
SideEffect.LIGHTING,
SideEffect.NEIGHBORS
);
@Override
public Set<SideEffect> getSupportedSideEffects() {
return SideEffectSet.defaults().getSideEffectsToApply();
return SUPPORTED_SIDE_EFFECTS;
}
@Override
@ -434,6 +443,10 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return material.getCraftBlockData();
}
public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockState) {
return Block.stateById(getOrdinalToIbdID()[blockState.getOrdinal()]);
}
@Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = getServerLevel(world);
@ -602,6 +615,11 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return new PaperweightPostProcessor();
}
@Override
public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, Region region) {
return new PaperweightPlacementStateProcessor(extent, mask, region);
}
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
try {

Datei anzeigen

@ -0,0 +1,142 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightFaweMutableBlockPlaceContext extends BlockPlaceContext {
private static final BlockHitResult DEFAULT_BLOCK_HIT = new BlockHitResult(
new Vec3(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE),
Direction.NORTH,
new BlockPos(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE),
false
);
private final ServerLevel level;
private BlockHitResult hitResult = null;
private Direction direction = null;
private BlockPos relativePos;
@SuppressWarnings("DataFlowIssue")
public PaperweightFaweMutableBlockPlaceContext(ServerLevel level) {
super(
level,
null,
null,
null,
DEFAULT_BLOCK_HIT
);
this.level = level;
this.replaceClicked = false;
}
public PaperweightFaweMutableBlockPlaceContext withSetting(BlockHitResult hitResult, Direction direction) {
this.hitResult = hitResult;
this.direction = direction;
this.relativePos = hitResult.getBlockPos().relative(hitResult.getDirection());
return this;
}
@Override
@Nonnull
public BlockPos getClickedPos() {
return this.relativePos;
}
@Override
@Nonnull
public Direction getClickedFace() {
return this.hitResult.getDirection();
}
@Override
@Nonnull
public Vec3 getClickLocation() {
return this.hitResult.getLocation();
}
@Override
public boolean isInside() {
return this.hitResult.isInside();
}
@Override
@SuppressWarnings("NullableProblems")
public ItemStack getItemInHand() {
return ItemStack.EMPTY;
}
@Nullable
@Override
public Player getPlayer() {
return null;
}
@Override
@SuppressWarnings("NullableProblems")
public InteractionHand getHand() {
return null;
}
@Override
@Nonnull
public Level getLevel() {
return this.level;
}
@Override
@Nonnull
public Direction getHorizontalDirection() {
return this.direction.getAxis() == Direction.Axis.Y ? Direction.NORTH : this.direction;
}
@Override
public boolean isSecondaryUseActive() {
return false;
}
@Override
public float getRotation() {
return (float) (this.direction.get2DDataValue() * 90);
}
@Override
public boolean canPlace() {
return this.getLevel().getBlockState(this.getClickedPos()).canBeReplaced(this);
}
@Override
public boolean replacingClickedOnBlock() {
return false;
}
@Override
@Nonnull
public Direction getNearestLookingDirection() {
return direction;
}
@Override
@Nonnull
public Direction getNearestLookingVerticalDirection() {
return direction.getAxis() == Direction.Axis.Y ? Direction.UP : Direction.DOWN;
}
@Override
@Nonnull
public Direction[] getNearestLookingDirections() {
return new Direction[]{direction};
}
}

Datei anzeigen

@ -21,6 +21,7 @@ import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
@ -840,7 +841,9 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
nmsChunk.mustNotSave = false;
nmsChunk.setUnsaved(true);
// send to player
if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) {
if (!set
.getSideEffectSet()
.shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) {
this.send();
}
if (finalizer != null) {

Datei anzeigen

@ -0,0 +1,116 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import org.enginehub.linbus.tree.LinCompoundTag;
import sun.misc.Unsafe;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightLevelProxy extends ServerLevel {
protected ServerLevel serverLevel;
private PaperweightFaweAdapter adapter;
private PaperweightPlacementStateProcessor processor;
@SuppressWarnings("DataFlowIssue")
private PaperweightLevelProxy() {
super(null, null, null, null, null, null, null, true, 0L, null, true, null, null, null, null);
throw new IllegalStateException("Cannot be instantiated");
}
public static PaperweightLevelProxy getInstance(ServerLevel serverLevel, PaperweightPlacementStateProcessor processor) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PaperweightLevelProxy newLevel;
try {
newLevel = (PaperweightLevelProxy) unsafe.allocateInstance(PaperweightLevelProxy.class);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
newLevel.processor = processor;
newLevel.adapter = ((PaperweightFaweAdapter) WorldEditPlugin.getInstance().getBukkitImplAdapter());
newLevel.serverLevel = serverLevel;
return newLevel;
}
@Nullable
@Override
public BlockEntity getBlockEntity(@Nonnull BlockPos blockPos) {
if (blockPos.getX() == Integer.MAX_VALUE) {
return null;
}
LinCompoundTag tag = processor.getTileAt(blockPos.getX(), blockPos.getY(), blockPos.getZ());
if (tag == null) {
return null;
}
BlockState state = adapter.adapt(processor.getBlockStateAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
if (!(state.getBlock() instanceof EntityBlock entityBlock)) {
return null;
}
BlockEntity tileEntity = entityBlock.newBlockEntity(blockPos, state);
tileEntity.load((CompoundTag) adapter.fromNativeLin(tag));
return tileEntity;
}
@Override
@Nonnull
public BlockState getBlockState(@Nonnull BlockPos blockPos) {
if (blockPos.getX() == Integer.MAX_VALUE) {
return Blocks.AIR.defaultBlockState();
}
com.sk89q.worldedit.world.block.BlockState state = processor.getBlockStateAt(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
);
return adapter.adapt(state);
}
@SuppressWarnings("unused")
@Override
@Nonnull
public FluidState getFluidState(@Nonnull BlockPos pos) {
if (pos.getX() == Integer.MAX_VALUE) {
return Fluids.EMPTY.defaultFluidState();
}
return getBlockState(pos).getFluidState();
}
@SuppressWarnings("unused")
@Override
public boolean isWaterAt(@Nonnull BlockPos pos) {
if (pos.getX() == Integer.MAX_VALUE) {
return false;
}
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
@Override
public int getHeight() {
return serverLevel.getHeight();
}
@Override
public int getMinBuildHeight() {
return serverLevel.getMinBuildHeight();
}
@Override
public WorldBorder getWorldBorder() {
return serverLevel.getWorldBorder();
}
}

Datei anzeigen

@ -0,0 +1,112 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.fastasyncworldedit.core.wrappers.WorldWrapper;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class PaperweightPlacementStateProcessor extends PlacementStateProcessor {
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
private final PaperweightFaweMutableBlockPlaceContext mutableBlockPlaceContext;
private final PaperweightLevelProxy proxyLevel;
public PaperweightPlacementStateProcessor(Extent extent, BlockTypeMask mask, Region region) {
super(extent, mask, region);
World world = ExtentTraverser.getWorldFromExtent(extent);
if (world == null) {
throw new UnsupportedOperationException(
"World is required for PlacementStateProcessor but none found in given extent.");
}
BukkitWorld bukkitWorld;
if (world instanceof WorldWrapper wrapper) {
bukkitWorld = (BukkitWorld) wrapper.getParent();
} else {
bukkitWorld = (BukkitWorld) world;
}
this.proxyLevel = PaperweightLevelProxy.getInstance(((CraftWorld) bukkitWorld.getWorld()).getHandle(), this);
this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel);
}
private PaperweightPlacementStateProcessor(
Extent extent,
BlockTypeMask mask,
Map<SecondPass, Character> crossChunkSecondPasses,
ThreadLocal<PlacementStateProcessor> threadProcessors,
Region region,
AtomicBoolean finished
) {
super(extent, mask, crossChunkSecondPasses, threadProcessors, region, finished);
World world = ExtentTraverser.getWorldFromExtent(extent);
if (world == null) {
throw new UnsupportedOperationException(
"World is required for PlacementStateProcessor but none found in given extent.");
}
BukkitWorld bukkitWorld;
if (world instanceof WorldWrapper wrapper) {
bukkitWorld = (BukkitWorld) wrapper.getParent();
} else {
bukkitWorld = (BukkitWorld) world;
}
this.proxyLevel = PaperweightLevelProxy.getInstance(((CraftWorld) bukkitWorld.getWorld()).getHandle(), this);
this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel);
}
@Override
protected char getStateAtFor(
int x,
int y,
int z,
BlockState state,
Vector3 clickPos,
Direction clickedFaceDirection,
BlockVector3 clickedBlock
) {
Block block = ((PaperweightBlockMaterial) state.getMaterial()).getBlock();
Vec3 pos = new Vec3(clickPos.x(), clickPos.y(), clickPos.z());
net.minecraft.core.Direction side = net.minecraft.core.Direction.valueOf(clickedFaceDirection.toString());
BlockPos blockPos = new BlockPos(clickedBlock.x(), clickedBlock.y(), clickedBlock.z());
net.minecraft.world.level.block.state.BlockState newState = block.getStateForPlacement(mutableBlockPlaceContext.withSetting(
new BlockHitResult(pos, side, blockPos, false),
side.getOpposite()
));
return newState == null ? BlockTypesCache.ReservedIDs.AIR : adapter.ibdIDToOrdinal(Block.BLOCK_STATE_REGISTRY.getId(
newState));
}
@Override
@Nullable
public Extent construct(Extent child) {
if (child == getExtent()) {
return this;
}
return new PaperweightPlacementStateProcessor(child, mask, region);
}
@Override
public PlacementStateProcessor fork() {
return new PaperweightPlacementStateProcessor(extent, mask, postCompleteSecondPasses, threadProcessors, region, finished);
}
}

Datei anzeigen

@ -856,12 +856,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
}
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.NEIGHBORS,
SideEffect.LIGHTING,
SideEffect.VALIDATION,
SideEffect.ENTITY_AI,
SideEffect.EVENTS,
SideEffect.UPDATE
//FAWE start - FAWE-supported side effects
SideEffect.HISTORY,
SideEffect.HEIGHTMAPS,
SideEffect.LIGHTING,
SideEffect.NEIGHBORS
//FAWE end
);
@Override

Datei anzeigen

@ -0,0 +1,237 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.PaperweightFaweAdapter;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.world.block.BlockTypes;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
public class PaperweightServerLevelDelegateProxy implements InvocationHandler {
private static final Logger LOGGER = LogManagerCompat.getLogger();
// FAWE start - extent not EditSession
private final Extent editSession;
//FAWE end
private final ServerLevel serverLevel;
//FAWE start - use FAWE adapter
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
//FAWE end
//FAWE start - force error if method not caught by this instance
private final boolean errorOnPassthrough;
//FAWE end
private PaperweightServerLevelDelegateProxy(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
this.editSession = editSession;
this.serverLevel = serverLevel;
//FAWE start
this.errorOnPassthrough = false;
//FAWE end
}
public static WorldGenLevel newInstance(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
return (WorldGenLevel) Proxy.newProxyInstance(
serverLevel.getClass().getClassLoader(),
serverLevel.getClass().getInterfaces(),
new PaperweightServerLevelDelegateProxy(editSession, serverLevel, adapter)
);
}
//FAWE start - force error if method not caught by this instance
private PaperweightServerLevelDelegateProxy(Extent extent, ServerLevel serverLevel, boolean errorOnPassthrough) {
this.editSession = extent;
this.serverLevel = serverLevel;
this.errorOnPassthrough = errorOnPassthrough;
}
public static WorldGenLevel newInstance(Extent extent, ServerLevel serverLevel, boolean errorOnPassthrough) {
return (WorldGenLevel) Proxy.newProxyInstance(
serverLevel.getClass().getClassLoader(),
serverLevel.getClass().getInterfaces(),
new PaperweightServerLevelDelegateProxy(extent, serverLevel, errorOnPassthrough)
);
}
//FAWE end
@Nullable
private BlockEntity getBlockEntity(BlockPos blockPos) {
BlockEntity tileEntity = this.serverLevel.getChunkAt(blockPos).getBlockEntity(blockPos);
if (tileEntity == null) {
return null;
}
BlockEntity newEntity = tileEntity.getType().create(blockPos, getBlockState(blockPos));
newEntity.load((CompoundTag) adapter.fromNativeLin(this.editSession.getFullBlock(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
).getNbtReference().getValue()));
return newEntity;
}
private BlockState getBlockState(BlockPos blockPos) {
return adapter.adapt(this.editSession.getBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
}
private boolean setBlock(BlockPos blockPos, BlockState blockState) {
try {
return editSession.setBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ(), adapter.adapt(blockState));
} catch (MaxChangedBlocksException e) {
throw new RuntimeException(e);
}
}
private boolean removeBlock(BlockPos blockPos, boolean bl) {
try {
return editSession.setBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ(), BlockTypes.AIR.getDefaultState());
} catch (MaxChangedBlocksException e) {
throw new RuntimeException(e);
}
}
private FluidState getFluidState(BlockPos pos) {
return getBlockState(pos).getFluidState();
}
private boolean isWaterAt(BlockPos pos) {
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//FAWE start - cannot use switch where method names are equal
String methodName = method.getName();
if (Refraction.pickName("getBlockState", "a_").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
// getBlockState
return getBlockState(blockPos);
}
}
if (Refraction.pickName("getBlockEntity", "c_").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
// getBlockEntity
return getBlockEntity(blockPos);
}
}
if ("a".equals(methodName) || "setBlock".equals(methodName) || "removeBlock".equals(methodName) || "destroyBlock".equals(
methodName)) {
if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) {
// setBlock
return setBlock(blockPos, blockState);
} else if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) {
// removeBlock (and also matches destroyBlock)
return removeBlock(blockPos, bl);
}
}
//FAWE start
if (Refraction.pickName("getFluidState", "b_").equals(methodName)) { //net.minecraft.world.level.BlockGetter
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
return getFluidState(blockPos);
}
}
if (Refraction.pickName("isWaterAt", "z").equals(methodName)) { //net.minecraft.world.level.LevelReader
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
return isWaterAt(blockPos);
}
}
if (Refraction.pickName("getEntities", "a_").equals(methodName)) { //net.minecraft.world.level.EntityGetter
if (args.length == 2 && args[0] instanceof Entity && args[1] instanceof AABB) {
return new ArrayList<>();
}
}
// Specific passthroughs that we want to allow
// net.minecraft.world.level.BlockAndTintGetter
if (Refraction.pickName("getRawBrightness", "b").equals(methodName)) {
return method.invoke(this.serverLevel, args);
}
// net.minecraft.world.level.LevelHeightAccessor
if (Refraction.pickName("getMaxBuildHeight", "al").equals(methodName)) {
if (args.length == 0) {
return method.invoke(this.serverLevel, args);
}
}
// net.minecraft.world.level.SignalGetter
if (Refraction.pickName("hasNeighborSignal", "C").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getSignal", "c").equals(methodName)) {
if (args.length == 2 && args[0] instanceof BlockPos && args[1] instanceof Direction) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getControlInputSignal", "a").equals(methodName)) {
if (args.length == 3 && args[0] instanceof BlockPos && args[1] instanceof Direction && args[2] instanceof Boolean) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getDirectSignal", "a").equals(methodName)) {
if (args.length == 2 && args[0] instanceof BlockPos && args[1] instanceof Direction) {
return method.invoke(this.serverLevel, args);
}
}
//FAWE start - force error if method not caught by this instance
if (errorOnPassthrough) {
LOGGER.error(
"""
Attempted passthough of method {}.
Method argument types: {}
Method argument values: {}
""",
method.getName(),
Arrays.stream(args).map(a -> a.getClass().getName()).toList(),
Arrays.stream(args).map(Object::toString).toList()
);
throw new FaweException("Method required passthrough.");
}
//FAWE end
return method.invoke(this.serverLevel, args);
}
}

Datei anzeigen

@ -4,6 +4,7 @@ import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
@ -13,6 +14,7 @@ import com.fastasyncworldedit.core.util.NbtUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
@ -21,7 +23,7 @@ import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazy
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.regen.PaperweightRegen;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
import com.sk89q.worldedit.math.BlockVector3;
@ -33,7 +35,6 @@ import com.sk89q.worldedit.registry.state.IntegerProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.world.RegenOptions;
import com.sk89q.worldedit.world.biome.BiomeType;
@ -284,9 +285,16 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return state.toBaseBlock();
}
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.HISTORY,
SideEffect.HEIGHTMAPS,
SideEffect.LIGHTING,
SideEffect.NEIGHBORS
);
@Override
public Set<SideEffect> getSupportedSideEffects() {
return SideEffectSet.defaults().getSideEffectsToApply();
return SUPPORTED_SIDE_EFFECTS;
}
@Override
@ -434,6 +442,10 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return material.getCraftBlockData();
}
public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockState) {
return Block.stateById(getOrdinalToIbdID()[blockState.getOrdinal()]);
}
@Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = getServerLevel(world);
@ -471,8 +483,7 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
@Override
public boolean canPlaceAt(org.bukkit.World world, BlockVector3 blockVector3, BlockState blockState) {
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(getOrdinalToIbdID()[blockState.getOrdinal()]);
return blockState1.hasPostProcess(
getServerLevel(world),
new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z())
@ -602,6 +613,11 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return new PaperweightPostProcessor();
}
@Override
public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, Region region) {
return new PaperweightPlacementStateProcessor(extent, mask, region);
}
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
try {

Datei anzeigen

@ -0,0 +1,137 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightFaweMutableBlockPlaceContext extends BlockPlaceContext {
private static final BlockHitResult DEFAULT_BLOCK_HIT = new BlockHitResult(
new Vec3(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE),
Direction.NORTH,
new BlockPos(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE),
false
);
private final ServerLevel level;
private BlockHitResult hitResult = null;
private Direction direction = null;
private BlockPos relativePos;
@SuppressWarnings("DataFlowIssue")
public PaperweightFaweMutableBlockPlaceContext(ServerLevel level) {
super(level, null, null, null, DEFAULT_BLOCK_HIT
);
this.level = level;
this.replaceClicked = false;
}
public PaperweightFaweMutableBlockPlaceContext withSetting(BlockHitResult hitResult, Direction direction) {
this.hitResult = hitResult;
this.direction = direction;
this.relativePos = hitResult.getBlockPos().relative(hitResult.getDirection());
return this;
}
@Override
@Nonnull
public BlockPos getClickedPos() {
return this.relativePos;
}
@Override
@Nonnull
public Direction getClickedFace() {
return this.hitResult.getDirection();
}
@Override
@Nonnull
public Vec3 getClickLocation() {
return this.hitResult.getLocation();
}
@Override
public boolean isInside() {
return this.hitResult.isInside();
}
@Override
@SuppressWarnings("NullableProblems")
public ItemStack getItemInHand() {
return ItemStack.EMPTY;
}
@Nullable
@Override
public Player getPlayer() {
return null;
}
@Override
@SuppressWarnings("NullableProblems")
public InteractionHand getHand() {
return null;
}
@Override
@Nonnull
public Level getLevel() {
return this.level;
}
@Override
@Nonnull
public Direction getHorizontalDirection() {
return this.direction.getAxis() == Direction.Axis.Y ? Direction.NORTH : this.direction;
}
@Override
public boolean isSecondaryUseActive() {
return false;
}
@Override
public float getRotation() {
return (float) (this.direction.get2DDataValue() * 90);
}
@Override
public boolean canPlace() {
return this.getLevel().getBlockState(this.getClickedPos()).canBeReplaced(this);
}
@Override
public boolean replacingClickedOnBlock() {
return false;
}
@Override
@Nonnull
public Direction getNearestLookingDirection() {
return direction;
}
@Override
@Nonnull
public Direction getNearestLookingVerticalDirection() {
return direction.getAxis() == Direction.Axis.Y ? Direction.UP : Direction.DOWN;
}
@Override
@Nonnull
public Direction[] getNearestLookingDirections() {
return new Direction[]{direction};
}
}

Datei anzeigen

@ -21,6 +21,7 @@ import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
@ -840,7 +841,9 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
nmsChunk.mustNotSave = false;
nmsChunk.setUnsaved(true);
// send to player
if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) {
if (!set
.getSideEffectSet()
.shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) {
this.send();
}
if (finalizer != null) {

Datei anzeigen

@ -0,0 +1,116 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import org.enginehub.linbus.tree.LinCompoundTag;
import sun.misc.Unsafe;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightLevelProxy extends ServerLevel {
protected ServerLevel serverLevel;
private PaperweightFaweAdapter adapter;
private PaperweightPlacementStateProcessor processor;
@SuppressWarnings("DataFlowIssue")
private PaperweightLevelProxy() {
super(null, null, null, null, null, null, null, true, 0L, null, true, null, null, null, null);
throw new IllegalStateException("Cannot be instantiated");
}
public static PaperweightLevelProxy getInstance(ServerLevel serverLevel, PaperweightPlacementStateProcessor processor) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PaperweightLevelProxy newLevel;
try {
newLevel = (PaperweightLevelProxy) unsafe.allocateInstance(PaperweightLevelProxy.class);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
newLevel.processor = processor;
newLevel.adapter = ((PaperweightFaweAdapter) WorldEditPlugin.getInstance().getBukkitImplAdapter());
newLevel.serverLevel = serverLevel;
return newLevel;
}
@Nullable
@Override
public BlockEntity getBlockEntity(@Nonnull BlockPos blockPos) {
if (blockPos.getY() == Integer.MAX_VALUE) {
return null;
}
LinCompoundTag tag = processor.getTileAt(blockPos.getX(), blockPos.getY(), blockPos.getZ());
if (tag == null) {
return null;
}
BlockState state = adapter.adapt(processor.getBlockStateAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
if (!(state.getBlock() instanceof EntityBlock entityBlock)) {
return null;
}
BlockEntity tileEntity = entityBlock.newBlockEntity(blockPos, state);
tileEntity.load((CompoundTag) adapter.fromNativeLin(tag));
return tileEntity;
}
@Override
@Nonnull
public BlockState getBlockState(@Nonnull BlockPos blockPos) {
if (blockPos.getY() == Integer.MAX_VALUE) {
return Blocks.AIR.defaultBlockState();
}
com.sk89q.worldedit.world.block.BlockState state = processor.getBlockStateAt(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
);
return adapter.adapt(state);
}
@SuppressWarnings("unused")
@Override
@Nonnull
public FluidState getFluidState(@Nonnull BlockPos pos) {
if (pos.getY() == Integer.MAX_VALUE) {
return Fluids.EMPTY.defaultFluidState();
}
return getBlockState(pos).getFluidState();
}
@SuppressWarnings("unused")
@Override
public boolean isWaterAt(@Nonnull BlockPos pos) {
if (pos.getY() == Integer.MAX_VALUE) {
return false;
}
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
@Override
public int getHeight() {
return serverLevel.getHeight();
}
@Override
public int getMinBuildHeight() {
return serverLevel.getMinBuildHeight();
}
@Override
public WorldBorder getWorldBorder() {
return serverLevel.getWorldBorder();
}
}

Datei anzeigen

@ -0,0 +1,112 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.fastasyncworldedit.core.wrappers.WorldWrapper;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class PaperweightPlacementStateProcessor extends PlacementStateProcessor {
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
private final PaperweightFaweMutableBlockPlaceContext mutableBlockPlaceContext;
private final PaperweightLevelProxy proxyLevel;
public PaperweightPlacementStateProcessor(Extent extent, BlockTypeMask mask, Region region) {
super(extent, mask, region);
World world = ExtentTraverser.getWorldFromExtent(extent);
if (world == null) {
throw new UnsupportedOperationException(
"World is required for PlacementStateProcessor but none found in given extent.");
}
BukkitWorld bukkitWorld;
if (world instanceof WorldWrapper wrapper) {
bukkitWorld = (BukkitWorld) wrapper.getParent();
} else {
bukkitWorld = (BukkitWorld) world;
}
this.proxyLevel = PaperweightLevelProxy.getInstance(((CraftWorld) bukkitWorld.getWorld()).getHandle(), this);
this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel);
}
private PaperweightPlacementStateProcessor(
Extent extent,
BlockTypeMask mask,
Map<SecondPass, Character> crossChunkSecondPasses,
ThreadLocal<PlacementStateProcessor> threadProcessors,
Region region,
AtomicBoolean finished
) {
super(extent, mask, crossChunkSecondPasses, threadProcessors, region, finished);
World world = ExtentTraverser.getWorldFromExtent(extent);
if (world == null) {
throw new UnsupportedOperationException(
"World is required for PlacementStateProcessor but none found in given extent.");
}
BukkitWorld bukkitWorld;
if (world instanceof WorldWrapper wrapper) {
bukkitWorld = (BukkitWorld) wrapper.getParent();
} else {
bukkitWorld = (BukkitWorld) world;
}
this.proxyLevel = PaperweightLevelProxy.getInstance(((CraftWorld) bukkitWorld.getWorld()).getHandle(), this);
this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel);
}
@Override
protected char getStateAtFor(
int x,
int y,
int z,
BlockState state,
Vector3 clickPos,
Direction clickedFaceDirection,
BlockVector3 clickedBlock
) {
Block block = ((PaperweightBlockMaterial) state.getMaterial()).getBlock();
Vec3 pos = new Vec3(clickPos.x(), clickPos.y(), clickPos.z());
net.minecraft.core.Direction side = net.minecraft.core.Direction.valueOf(clickedFaceDirection.toString());
BlockPos blockPos = new BlockPos(clickedBlock.x(), clickedBlock.y(), clickedBlock.z());
net.minecraft.world.level.block.state.BlockState newState = block.getStateForPlacement(mutableBlockPlaceContext.withSetting(
new BlockHitResult(pos, side, blockPos, false),
side.getOpposite()
));
return newState == null ? BlockTypesCache.ReservedIDs.AIR : adapter.ibdIDToOrdinal(Block.BLOCK_STATE_REGISTRY.getId(
newState));
}
@Override
@Nullable
public Extent construct(Extent child) {
if (child == getExtent()) {
return this;
}
return new PaperweightPlacementStateProcessor(child, mask, region);
}
@Override
public PlacementStateProcessor fork() {
return new PaperweightPlacementStateProcessor(extent, mask, postCompleteSecondPasses, threadProcessors, region, finished);
}
}

Datei anzeigen

@ -880,12 +880,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
}
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.NEIGHBORS,
SideEffect.LIGHTING,
SideEffect.VALIDATION,
SideEffect.ENTITY_AI,
SideEffect.EVENTS,
SideEffect.UPDATE
//FAWE start - FAWE-supported side effects
SideEffect.HISTORY,
SideEffect.HEIGHTMAPS,
SideEffect.LIGHTING,
SideEffect.NEIGHBORS
//FAWE end
);
@Override

Datei anzeigen

@ -0,0 +1,240 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R4;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4.PaperweightFaweAdapter;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.world.block.BlockTypes;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
public class PaperweightServerLevelDelegateProxy implements InvocationHandler {
private static final Logger LOGGER = LogManagerCompat.getLogger();
// FAWE start - extent not EditSession
private final Extent editSession;
//FAWE end
private final ServerLevel serverLevel;
//FAWE start - use FAWE adapter
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
//FAWE end
//FAWE start - force error if method not caught by this instance
private final boolean errorOnPassthrough;
//FAWE end
private PaperweightServerLevelDelegateProxy(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
this.editSession = editSession;
this.serverLevel = serverLevel;
//FAWE start
this.errorOnPassthrough = false;
//FAWE end
}
public static WorldGenLevel newInstance(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
return (WorldGenLevel) Proxy.newProxyInstance(
serverLevel.getClass().getClassLoader(),
serverLevel.getClass().getInterfaces(),
new PaperweightServerLevelDelegateProxy(editSession, serverLevel, adapter)
);
}
//FAWE start - force error if method not caught by this instance
private PaperweightServerLevelDelegateProxy(Extent extent, ServerLevel serverLevel, boolean errorOnPassthrough) {
this.editSession = extent;
this.serverLevel = serverLevel;
this.errorOnPassthrough = errorOnPassthrough;
}
public static WorldGenLevel newInstance(Extent extent, ServerLevel serverLevel, boolean errorOnPassthrough) {
return (WorldGenLevel) Proxy.newProxyInstance(
serverLevel.getClass().getClassLoader(),
serverLevel.getClass().getInterfaces(),
new PaperweightServerLevelDelegateProxy(extent, serverLevel, errorOnPassthrough)
);
}
//FAWE end
@Nullable
private BlockEntity getBlockEntity(BlockPos blockPos) {
BlockEntity tileEntity = this.serverLevel.getChunkAt(blockPos).getBlockEntity(blockPos);
if (tileEntity == null) {
return null;
}
BlockEntity newEntity = tileEntity.getType().create(blockPos, getBlockState(blockPos));
newEntity.loadWithComponents(
(CompoundTag) adapter.fromNativeLin(this.editSession.getFullBlock(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
).getNbtReference().getValue()),
this.serverLevel.registryAccess()
);
return newEntity;
}
private BlockState getBlockState(BlockPos blockPos) {
return adapter.adapt(this.editSession.getBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
}
private boolean setBlock(BlockPos blockPos, BlockState blockState) {
try {
return editSession.setBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ(), adapter.adapt(blockState));
} catch (MaxChangedBlocksException e) {
throw new RuntimeException(e);
}
}
private boolean removeBlock(BlockPos blockPos, boolean bl) {
try {
return editSession.setBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ(), BlockTypes.AIR.getDefaultState());
} catch (MaxChangedBlocksException e) {
throw new RuntimeException(e);
}
}
private FluidState getFluidState(BlockPos pos) {
return getBlockState(pos).getFluidState();
}
private boolean isWaterAt(BlockPos pos) {
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//FAWE start - cannot use switch where method names are equal
String methodName = method.getName();
if (Refraction.pickName("getBlockState", "a_").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
// getBlockState
return getBlockState(blockPos);
}
}
if (Refraction.pickName("getBlockEntity", "c_").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
// getBlockEntity
return getBlockEntity(blockPos);
}
}
if ("a".equals(methodName) || "setBlock".equals(methodName) || "removeBlock".equals(methodName) || "destroyBlock".equals(
methodName)) {
if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) {
// setBlock
return setBlock(blockPos, blockState);
} else if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) {
// removeBlock (and also matches destroyBlock)
return removeBlock(blockPos, bl);
}
}
//FAWE start
if (Refraction.pickName("getFluidState", "b_").equals(methodName)) { //net.minecraft.world.level.BlockGetter
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
return getFluidState(blockPos);
}
}
if (Refraction.pickName("isWaterAt", "z").equals(methodName)) { //net.minecraft.world.level.LevelReader
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
return isWaterAt(blockPos);
}
}
if (Refraction.pickName("getEntities", "a_").equals(methodName)) { //net.minecraft.world.level.EntityGetter
if (args.length == 2 && args[0] instanceof Entity && args[1] instanceof AABB) {
return new ArrayList<>();
}
}
// Specific passthroughs that we want to allow
// net.minecraft.world.level.BlockAndTintGetter
if (Refraction.pickName("getRawBrightness", "b").equals(methodName)) {
return method.invoke(this.serverLevel, args);
}
// net.minecraft.world.level.LevelHeightAccessor
if (Refraction.pickName("getMaxBuildHeight", "al").equals(methodName)) {
if (args.length == 0) {
return method.invoke(this.serverLevel, args);
}
}
// net.minecraft.world.level.SignalGetter
if (Refraction.pickName("hasNeighborSignal", "C").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getSignal", "c").equals(methodName)) {
if (args.length == 2 && args[0] instanceof BlockPos && args[1] instanceof Direction) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getControlInputSignal", "a").equals(methodName)) {
if (args.length == 3 && args[0] instanceof BlockPos && args[1] instanceof Direction && args[2] instanceof Boolean) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getDirectSignal", "a").equals(methodName)) {
if (args.length == 2 && args[0] instanceof BlockPos && args[1] instanceof Direction) {
return method.invoke(this.serverLevel, args);
}
}
//FAWE start - force error if method not caught by this instance
if (errorOnPassthrough) {
LOGGER.error(
"""
Attempted passthough of method {}.
Method argument types: {}
Method argument values: {}
""",
method.getName(),
Arrays.stream(args).map(a -> a.getClass().getName()).toList(),
Arrays.stream(args).map(Object::toString).toList()
);
throw new FaweException("Method required passthrough.");
}
//FAWE end
return method.invoke(this.serverLevel, args);
}
}

Datei anzeigen

@ -4,6 +4,7 @@ import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
@ -13,6 +14,7 @@ import com.fastasyncworldedit.core.util.NbtUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.mojang.serialization.Codec;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseItemStack;
@ -22,6 +24,7 @@ import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4.nbt.PaperweightLazy
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4.regen.PaperweightRegen;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
@ -34,7 +37,6 @@ import com.sk89q.worldedit.registry.state.IntegerProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.concurrency.LazyReference;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.world.RegenOptions;
@ -293,9 +295,16 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return state.toBaseBlock();
}
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.HISTORY,
SideEffect.HEIGHTMAPS,
SideEffect.LIGHTING,
SideEffect.NEIGHBORS
);
@Override
public Set<SideEffect> getSupportedSideEffects() {
return SideEffectSet.defaults().getSideEffectsToApply();
return SUPPORTED_SIDE_EFFECTS;
}
@Override
@ -443,6 +452,10 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return material.getCraftBlockData();
}
public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockState) {
return Block.stateById(getOrdinalToIbdID()[blockState.getOrdinal()]);
}
@Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = getServerLevel(world);
@ -624,6 +637,11 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return new PaperweightPostProcessor();
}
@Override
public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, Region region) {
return new PaperweightPlacementStateProcessor(extent, mask, region);
}
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
try {

Datei anzeigen

@ -0,0 +1,143 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightFaweMutableBlockPlaceContext extends BlockPlaceContext {
private static final BlockHitResult DEFAULT_BLOCK_HIT = new BlockHitResult(
new Vec3(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE),
Direction.NORTH,
new BlockPos(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE),
false
);
private final ServerLevel level;
private BlockHitResult hitResult = null;
private Direction direction = null;
private BlockPos relativePos;
@SuppressWarnings("DataFlowIssue")
public PaperweightFaweMutableBlockPlaceContext(ServerLevel level) {
super(
level,
null,
null,
null,
DEFAULT_BLOCK_HIT
);
this.level = level;
this.replaceClicked = false;
}
public PaperweightFaweMutableBlockPlaceContext withSetting(BlockHitResult hitResult, Direction direction) {
this.hitResult = hitResult;
this.direction = direction;
this.relativePos = hitResult.getBlockPos().relative(hitResult.getDirection());
return this;
}
@Override
@Nonnull
public BlockPos getClickedPos() {
return this.relativePos;
}
@Override
@Nonnull
public Direction getClickedFace() {
return this.hitResult.getDirection();
}
@Override
@Nonnull
public Vec3 getClickLocation() {
return this.hitResult.getLocation();
}
@Override
public boolean isInside() {
return this.hitResult.isInside();
}
@Override
@SuppressWarnings("NullableProblems")
public ItemStack getItemInHand() {
return ItemStack.EMPTY;
}
@Nullable
@Override
public Player getPlayer() {
return null;
}
@Override
@SuppressWarnings("NullableProblems")
public InteractionHand getHand() {
return null;
}
@Override
@Nonnull
public Level getLevel() {
return this.level;
}
@Override
@Nonnull
public Direction getHorizontalDirection() {
return this.direction.getAxis() == Direction.Axis.Y ? Direction.NORTH : this.direction;
}
@Override
public boolean isSecondaryUseActive() {
return false;
}
@Override
public float getRotation() {
return (float) (this.direction.get2DDataValue() * 90);
}
@Override
public boolean canPlace() {
return this.getLevel().getBlockState(this.getClickedPos()).canBeReplaced(this);
}
@Override
public boolean replacingClickedOnBlock() {
return false;
}
@Override
@Nonnull
public Direction getNearestLookingDirection() {
return direction;
}
@Override
@Nonnull
public Direction getNearestLookingVerticalDirection() {
return direction.getAxis() == Direction.Axis.Y ? Direction.UP : Direction.DOWN;
}
@Override
@Nonnull
public Direction[] getNearestLookingDirections() {
return new Direction[]{direction};
}
}

Datei anzeigen

@ -21,6 +21,7 @@ import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
@ -841,7 +842,9 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
nmsChunk.mustNotSave = false;
nmsChunk.setUnsaved(true);
// send to player
if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) {
if (!set
.getSideEffectSet()
.shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) {
this.send();
}
if (finalizer != null) {

Datei anzeigen

@ -0,0 +1,116 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import org.enginehub.linbus.tree.LinCompoundTag;
import sun.misc.Unsafe;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightLevelProxy extends ServerLevel {
protected ServerLevel serverLevel;
private PaperweightPlacementStateProcessor processor;
private PaperweightFaweAdapter adapter;
@SuppressWarnings("DataFlowIssue")
private PaperweightLevelProxy() {
super(null, null, null, null, null, null, null, true, 0L, null, true, null, null, null, null);
throw new IllegalStateException("Cannot be instantiated");
}
public static PaperweightLevelProxy getInstance(ServerLevel serverLevel, PaperweightPlacementStateProcessor processor) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PaperweightLevelProxy newLevel;
try {
newLevel = (PaperweightLevelProxy) unsafe.allocateInstance(PaperweightLevelProxy.class);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
newLevel.processor = processor;
newLevel.adapter = ((PaperweightFaweAdapter) WorldEditPlugin.getInstance().getBukkitImplAdapter());
newLevel.serverLevel = serverLevel;
return newLevel;
}
@Nullable
@Override
public BlockEntity getBlockEntity(@Nonnull BlockPos blockPos) {
if (blockPos.getX() == Integer.MAX_VALUE) {
return null;
}
LinCompoundTag tag = processor.getTileAt(blockPos.getX(), blockPos.getY(), blockPos.getZ());
if (tag == null) {
return null;
}
BlockState state = adapter.adapt(processor.getBlockStateAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
if (!(state.getBlock() instanceof EntityBlock entityBlock)) {
return null;
}
BlockEntity tileEntity = entityBlock.newBlockEntity(blockPos, state);
tileEntity.loadWithComponents((CompoundTag) adapter.fromNativeLin(tag), serverLevel.registryAccess());
return tileEntity;
}
@Override
@Nonnull
public BlockState getBlockState(@Nonnull BlockPos blockPos) {
if (blockPos.getX() == Integer.MAX_VALUE) {
return Blocks.AIR.defaultBlockState();
}
com.sk89q.worldedit.world.block.BlockState state = processor.getBlockStateAt(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
);
return adapter.adapt(state);
}
@SuppressWarnings("unused")
@Override
@Nonnull
public FluidState getFluidState(@Nonnull BlockPos pos) {
if (pos.getX() == Integer.MAX_VALUE) {
return Fluids.EMPTY.defaultFluidState();
}
return getBlockState(pos).getFluidState();
}
@SuppressWarnings("unused")
@Override
public boolean isWaterAt(@Nonnull BlockPos pos) {
if (pos.getX() == Integer.MAX_VALUE) {
return false;
}
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
@Override
public int getHeight() {
return serverLevel.getHeight();
}
@Override
public int getMinBuildHeight() {
return serverLevel.getMinBuildHeight();
}
@Override
public WorldBorder getWorldBorder() {
return serverLevel.getWorldBorder();
}
}

Datei anzeigen

@ -0,0 +1,111 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.fastasyncworldedit.core.wrappers.WorldWrapper;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import org.bukkit.craftbukkit.CraftWorld;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class PaperweightPlacementStateProcessor extends PlacementStateProcessor {
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
private final PaperweightLevelProxy proxyLevel;
private final PaperweightFaweMutableBlockPlaceContext mutableBlockPlaceContext;
public PaperweightPlacementStateProcessor(Extent extent, BlockTypeMask mask, Region region) {
super(extent, mask, region);
World world = ExtentTraverser.getWorldFromExtent(extent);
if (world == null) {
throw new UnsupportedOperationException(
"World is required for PlacementStateProcessor but none found in given extent.");
}
BukkitWorld bukkitWorld;
if (world instanceof WorldWrapper wrapper) {
bukkitWorld = (BukkitWorld) wrapper.getParent();
} else {
bukkitWorld = (BukkitWorld) world;
}
this.proxyLevel = PaperweightLevelProxy.getInstance(((CraftWorld) bukkitWorld.getWorld()).getHandle(), this);
this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel);
}
private PaperweightPlacementStateProcessor(
Extent extent,
BlockTypeMask mask,
Map<SecondPass, Character> crossChunkSecondPasses,
ServerLevel serverLevel,
ThreadLocal<PlacementStateProcessor> threadProcessors,
Region region,
AtomicBoolean finished
) {
super(extent, mask, crossChunkSecondPasses, threadProcessors, region, finished);
this.proxyLevel = PaperweightLevelProxy.getInstance(serverLevel, this);
this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel);
}
@Override
protected char getStateAtFor(
int x,
int y,
int z,
BlockState state,
Vector3 clickPos,
Direction clickedFaceDirection,
BlockVector3 clickedBlock
) {
Block block = ((PaperweightBlockMaterial) state.getMaterial()).getBlock();
Vec3 pos = new Vec3(clickPos.x(), clickPos.y(), clickPos.z());
net.minecraft.core.Direction side = net.minecraft.core.Direction.valueOf(clickedFaceDirection.toString());
BlockPos blockPos = new BlockPos(clickedBlock.x(), clickedBlock.y(), clickedBlock.z());
net.minecraft.world.level.block.state.BlockState newState = block.getStateForPlacement(mutableBlockPlaceContext.withSetting(
new BlockHitResult(pos, side, blockPos, false),
side.getOpposite()
));
return newState == null ? BlockTypesCache.ReservedIDs.AIR : adapter.ibdIDToOrdinal(Block.BLOCK_STATE_REGISTRY.getId(
newState));
}
@Override
@Nullable
public Extent construct(Extent child) {
if (child == getExtent()) {
return this;
}
return new PaperweightPlacementStateProcessor(child, mask, region);
}
@Override
public PlacementStateProcessor fork() {
return new PaperweightPlacementStateProcessor(
extent,
mask,
postCompleteSecondPasses,
proxyLevel.serverLevel,
threadProcessors,
region,
finished
);
}
}

Datei anzeigen

@ -886,12 +886,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
}
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.NEIGHBORS,
SideEffect.LIGHTING,
SideEffect.VALIDATION,
SideEffect.ENTITY_AI,
SideEffect.EVENTS,
SideEffect.UPDATE
//FAWE start - FAWE-supported side effects
SideEffect.HISTORY,
SideEffect.HEIGHTMAPS,
SideEffect.LIGHTING,
SideEffect.NEIGHBORS
//FAWE end
);
@Override

Datei anzeigen

@ -0,0 +1,240 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_R1;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.PaperweightFaweAdapter;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.world.block.BlockTypes;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
public class PaperweightServerLevelDelegateProxy implements InvocationHandler {
private static final Logger LOGGER = LogManagerCompat.getLogger();
// FAWE start - extent not EditSession
private final Extent editSession;
//FAWE end
private final ServerLevel serverLevel;
//FAWE start - use FAWE adapter
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
//FAWE end
//FAWE start - force error if method not caught by this instance
private final boolean errorOnPassthrough;
//FAWE end
private PaperweightServerLevelDelegateProxy(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
this.editSession = editSession;
this.serverLevel = serverLevel;
//FAWE start
this.errorOnPassthrough = false;
//FAWE end
}
public static WorldGenLevel newInstance(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
return (WorldGenLevel) Proxy.newProxyInstance(
serverLevel.getClass().getClassLoader(),
serverLevel.getClass().getInterfaces(),
new PaperweightServerLevelDelegateProxy(editSession, serverLevel, adapter)
);
}
//FAWE start - force error if method not caught by this instance
private PaperweightServerLevelDelegateProxy(Extent extent, ServerLevel serverLevel, boolean errorOnPassthrough) {
this.editSession = extent;
this.serverLevel = serverLevel;
this.errorOnPassthrough = errorOnPassthrough;
}
public static WorldGenLevel newInstance(Extent extent, ServerLevel serverLevel, boolean errorOnPassthrough) {
return (WorldGenLevel) Proxy.newProxyInstance(
serverLevel.getClass().getClassLoader(),
serverLevel.getClass().getInterfaces(),
new PaperweightServerLevelDelegateProxy(extent, serverLevel, errorOnPassthrough)
);
}
//FAWE end
@Nullable
private BlockEntity getBlockEntity(BlockPos blockPos) {
BlockEntity tileEntity = this.serverLevel.getChunkAt(blockPos).getBlockEntity(blockPos);
if (tileEntity == null) {
return null;
}
BlockEntity newEntity = tileEntity.getType().create(blockPos, getBlockState(blockPos));
newEntity.loadWithComponents(
(CompoundTag) adapter.fromNativeLin(this.editSession.getFullBlock(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
).getNbtReference().getValue()),
this.serverLevel.registryAccess()
);
return newEntity;
}
private BlockState getBlockState(BlockPos blockPos) {
return adapter.adapt(this.editSession.getBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
}
private boolean setBlock(BlockPos blockPos, BlockState blockState) {
try {
return editSession.setBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ(), adapter.adapt(blockState));
} catch (MaxChangedBlocksException e) {
throw new RuntimeException(e);
}
}
private boolean removeBlock(BlockPos blockPos, boolean bl) {
try {
return editSession.setBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ(), BlockTypes.AIR.getDefaultState());
} catch (MaxChangedBlocksException e) {
throw new RuntimeException(e);
}
}
private FluidState getFluidState(BlockPos pos) {
return getBlockState(pos).getFluidState();
}
private boolean isWaterAt(BlockPos pos) {
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//FAWE start - cannot use switch where method names are equal
String methodName = method.getName();
if (Refraction.pickName("getBlockState", "a_").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
// getBlockState
return getBlockState(blockPos);
}
}
if (Refraction.pickName("getBlockEntity", "c_").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
// getBlockEntity
return getBlockEntity(blockPos);
}
}
if ("a".equals(methodName) || "setBlock".equals(methodName) || "removeBlock".equals(methodName) || "destroyBlock".equals(
methodName)) {
if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) {
// setBlock
return setBlock(blockPos, blockState);
} else if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) {
// removeBlock (and also matches destroyBlock)
return removeBlock(blockPos, bl);
}
}
//FAWE start
if (Refraction.pickName("getFluidState", "b_").equals(methodName)) { //net.minecraft.world.level.BlockGetter
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
return getFluidState(blockPos);
}
}
if (Refraction.pickName("isWaterAt", "z").equals(methodName)) { //net.minecraft.world.level.LevelReader
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
return isWaterAt(blockPos);
}
}
if (Refraction.pickName("getEntities", "a_").equals(methodName)) { //net.minecraft.world.level.EntityGetter
if (args.length == 2 && args[0] instanceof Entity && args[1] instanceof AABB) {
return new ArrayList<>();
}
}
// Specific passthroughs that we want to allow
// net.minecraft.world.level.BlockAndTintGetter
if (Refraction.pickName("getRawBrightness", "b").equals(methodName)) {
return method.invoke(this.serverLevel, args);
}
// net.minecraft.world.level.LevelHeightAccessor
if (Refraction.pickName("getMaxBuildHeight", "al").equals(methodName)) {
if (args.length == 0) {
return method.invoke(this.serverLevel, args);
}
}
// net.minecraft.world.level.SignalGetter
if (Refraction.pickName("hasNeighborSignal", "C").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getSignal", "c").equals(methodName)) {
if (args.length == 2 && args[0] instanceof BlockPos && args[1] instanceof Direction) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getControlInputSignal", "a").equals(methodName)) {
if (args.length == 3 && args[0] instanceof BlockPos && args[1] instanceof Direction && args[2] instanceof Boolean) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getDirectSignal", "a").equals(methodName)) {
if (args.length == 2 && args[0] instanceof BlockPos && args[1] instanceof Direction) {
return method.invoke(this.serverLevel, args);
}
}
//FAWE start - force error if method not caught by this instance
if (errorOnPassthrough) {
LOGGER.error(
"""
Attempted passthough of method {}.
Method argument types: {}
Method argument values: {}
""",
method.getName(),
Arrays.stream(args).map(a -> a.getClass().getName()).toList(),
Arrays.stream(args).map(Object::toString).toList()
);
throw new FaweException("Method required passthrough.");
}
//FAWE end
return method.invoke(this.serverLevel, args);
}
}

Datei anzeigen

@ -4,6 +4,7 @@ import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
@ -13,6 +14,7 @@ import com.fastasyncworldedit.core.util.NbtUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.mojang.serialization.Codec;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseItemStack;
@ -22,6 +24,7 @@ import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.nbt.PaperweightLazy
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.regen.PaperweightRegen;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
@ -34,7 +37,6 @@ import com.sk89q.worldedit.registry.state.IntegerProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.concurrency.LazyReference;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.world.RegenOptions;
@ -293,9 +295,16 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return state.toBaseBlock();
}
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.HISTORY,
SideEffect.HEIGHTMAPS,
SideEffect.LIGHTING,
SideEffect.NEIGHBORS
);
@Override
public Set<SideEffect> getSupportedSideEffects() {
return SideEffectSet.defaults().getSideEffectsToApply();
return SUPPORTED_SIDE_EFFECTS;
}
@Override
@ -443,6 +452,10 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return material.getCraftBlockData();
}
public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockState) {
return Block.stateById(getOrdinalToIbdID()[blockState.getOrdinal()]);
}
@Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = getServerLevel(world);
@ -624,6 +637,11 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return new PaperweightPostProcessor();
}
@Override
public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, Region region) {
return new PaperweightPlacementStateProcessor(extent, mask, region);
}
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
if (PaperLib.isPaper()) { // Papers new chunk system has no related replacement - therefor we assume true.
return true;

Datei anzeigen

@ -0,0 +1,143 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightFaweMutableBlockPlaceContext extends BlockPlaceContext {
private static final BlockHitResult DEFAULT_BLOCK_HIT = new BlockHitResult(
new Vec3(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE),
Direction.NORTH,
new BlockPos(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE),
false
);
private final ServerLevel level;
private BlockHitResult hitResult = null;
private Direction direction = null;
private BlockPos relativePos;
@SuppressWarnings("DataFlowIssue")
public PaperweightFaweMutableBlockPlaceContext(ServerLevel level) {
super(
level,
null,
null,
null,
DEFAULT_BLOCK_HIT
);
this.level = level;
this.replaceClicked = false;
}
public PaperweightFaweMutableBlockPlaceContext withSetting(BlockHitResult hitResult, Direction direction) {
this.hitResult = hitResult;
this.direction = direction;
this.relativePos = hitResult.getBlockPos().relative(hitResult.getDirection());
return this;
}
@Override
@Nonnull
public BlockPos getClickedPos() {
return this.relativePos;
}
@Override
@Nonnull
public Direction getClickedFace() {
return this.hitResult.getDirection();
}
@Override
@Nonnull
public Vec3 getClickLocation() {
return this.hitResult.getLocation();
}
@Override
public boolean isInside() {
return this.hitResult.isInside();
}
@Override
@SuppressWarnings("NullableProblems")
public ItemStack getItemInHand() {
return ItemStack.EMPTY;
}
@Nullable
@Override
public Player getPlayer() {
return null;
}
@Override
@SuppressWarnings("NullableProblems")
public InteractionHand getHand() {
return null;
}
@Override
@Nonnull
public Level getLevel() {
return this.level;
}
@Override
@Nonnull
public Direction getHorizontalDirection() {
return this.direction.getAxis() == Direction.Axis.Y ? Direction.NORTH : this.direction;
}
@Override
public boolean isSecondaryUseActive() {
return false;
}
@Override
public float getRotation() {
return (float) (this.direction.get2DDataValue() * 90);
}
@Override
public boolean canPlace() {
return this.getLevel().getBlockState(this.getClickedPos()).canBeReplaced(this);
}
@Override
public boolean replacingClickedOnBlock() {
return false;
}
@Override
@Nonnull
public Direction getNearestLookingDirection() {
return direction;
}
@Override
@Nonnull
public Direction getNearestLookingVerticalDirection() {
return direction.getAxis() == Direction.Axis.Y ? Direction.UP : Direction.DOWN;
}
@Override
@Nonnull
public Direction[] getNearestLookingDirections() {
return new Direction[]{direction};
}
}

Datei anzeigen

@ -21,6 +21,7 @@ import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
@ -836,7 +837,9 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
nmsChunk.mustNotSave = false;
nmsChunk.setUnsaved(true);
// send to player
if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) {
if (!set
.getSideEffectSet()
.shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) {
this.send();
}
if (finalizer != null) {

Datei anzeigen

@ -0,0 +1,115 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import org.enginehub.linbus.tree.LinCompoundTag;
import sun.misc.Unsafe;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightLevelProxy extends ServerLevel {
protected ServerLevel serverLevel;
private PaperweightPlacementStateProcessor processor;
private PaperweightFaweAdapter adapter;
@SuppressWarnings("DataFlowIssue")
private PaperweightLevelProxy() {
super(null, null, null, null, null, null, null, true, 0L, null, true, null, null, null, null);
throw new IllegalStateException("Cannot be instantiated");
}
public static PaperweightLevelProxy getInstance(ServerLevel serverLevel, PaperweightPlacementStateProcessor processor) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PaperweightLevelProxy newLevel;
try {
newLevel = (PaperweightLevelProxy) unsafe.allocateInstance(PaperweightLevelProxy.class);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
newLevel.processor = processor;
newLevel.adapter = ((PaperweightFaweAdapter) WorldEditPlugin.getInstance().getBukkitImplAdapter());
newLevel.serverLevel = serverLevel;
return newLevel;
}
@Nullable
@Override
public BlockEntity getBlockEntity(@Nonnull BlockPos blockPos) {
if (blockPos.getX() == Integer.MAX_VALUE) {
return null;
}
LinCompoundTag tag = processor.getTileAt(blockPos.getX(), blockPos.getY(), blockPos.getZ());
if (tag == null) {
return null;
}
BlockState state = adapter.adapt(processor.getBlockStateAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
if (!(state.getBlock() instanceof EntityBlock entityBlock)) {
return null;
}
BlockEntity tileEntity = entityBlock.newBlockEntity(blockPos, state);
tileEntity.loadWithComponents((CompoundTag) adapter.fromNativeLin(tag), serverLevel.registryAccess());
return tileEntity;
}
@Override
@Nonnull
public BlockState getBlockState(@Nonnull BlockPos blockPos) {
if (blockPos.getX() == Integer.MAX_VALUE) {
return Blocks.AIR.defaultBlockState();
}
com.sk89q.worldedit.world.block.BlockState state = processor.getBlockStateAt(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
);
return adapter.adapt(state);
}
@SuppressWarnings("unused")
@Override
@Nonnull
public FluidState getFluidState(@Nonnull BlockPos pos) {
if (pos.getX() == Integer.MAX_VALUE) {
return Fluids.EMPTY.defaultFluidState();
}
return getBlockState(pos).getFluidState();
}
@SuppressWarnings("unused")
@Override
public boolean isWaterAt(@Nonnull BlockPos pos) {
if (pos.getX() == Integer.MAX_VALUE) {
return false;
}
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
@Override
public int getHeight() {
return serverLevel.getHeight();
}
@Override
public int getMinBuildHeight() {
return serverLevel.getMinBuildHeight();
}
@Override
public WorldBorder getWorldBorder() {
return serverLevel.getWorldBorder();
}
}

Datei anzeigen

@ -0,0 +1,111 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.fastasyncworldedit.core.wrappers.WorldWrapper;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import org.bukkit.craftbukkit.CraftWorld;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class PaperweightPlacementStateProcessor extends PlacementStateProcessor {
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
private final PaperweightFaweMutableBlockPlaceContext mutableBlockPlaceContext;
private final PaperweightLevelProxy proxyLevel;
public PaperweightPlacementStateProcessor(Extent extent, BlockTypeMask mask, Region region) {
super(extent, mask, region);
World world = ExtentTraverser.getWorldFromExtent(extent);
if (world == null) {
throw new UnsupportedOperationException(
"World is required for PlacementStateProcessor but none found in given extent.");
}
BukkitWorld bukkitWorld;
if (world instanceof WorldWrapper wrapper) {
bukkitWorld = (BukkitWorld) wrapper.getParent();
} else {
bukkitWorld = (BukkitWorld) world;
}
this.proxyLevel = PaperweightLevelProxy.getInstance(((CraftWorld) bukkitWorld.getWorld()).getHandle(), this);
this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel);
}
private PaperweightPlacementStateProcessor(
Extent extent,
BlockTypeMask mask,
Map<SecondPass, Character> crossChunkSecondPasses,
ServerLevel serverLevel,
ThreadLocal<PlacementStateProcessor> threadProcessors,
Region region,
AtomicBoolean finished
) {
super(extent, mask, crossChunkSecondPasses, threadProcessors, region, finished);
this.proxyLevel = PaperweightLevelProxy.getInstance(serverLevel, this);
this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel);
}
@Override
protected char getStateAtFor(
int x,
int y,
int z,
BlockState state,
Vector3 clickPos,
Direction clickedFaceDirection,
BlockVector3 clickedBlock
) {
Block block = ((PaperweightBlockMaterial) state.getMaterial()).getBlock();
Vec3 pos = new Vec3(clickPos.x(), clickPos.y(), clickPos.z());
net.minecraft.core.Direction side = net.minecraft.core.Direction.valueOf(clickedFaceDirection.toString());
BlockPos blockPos = new BlockPos(clickedBlock.x(), clickedBlock.y(), clickedBlock.z());
net.minecraft.world.level.block.state.BlockState newState = block.getStateForPlacement(mutableBlockPlaceContext.withSetting(
new BlockHitResult(pos, side, blockPos, false),
side.getOpposite()
));
return newState == null ? BlockTypesCache.ReservedIDs.AIR : adapter.ibdIDToOrdinal(Block.BLOCK_STATE_REGISTRY.getId(
newState));
}
@Override
@Nullable
public Extent construct(Extent child) {
if (child == getExtent()) {
return this;
}
return new PaperweightPlacementStateProcessor(child, mask, region);
}
@Override
public PlacementStateProcessor fork() {
return new PaperweightPlacementStateProcessor(
extent,
mask,
postCompleteSecondPasses,
proxyLevel.serverLevel,
threadProcessors,
region,
finished
);
}
}

Datei anzeigen

@ -849,12 +849,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
}
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.NEIGHBORS,
//FAWE start - FAWE-supported side effects
SideEffect.HISTORY,
SideEffect.HEIGHTMAPS,
SideEffect.LIGHTING,
SideEffect.VALIDATION,
SideEffect.ENTITY_AI,
SideEffect.EVENTS,
SideEffect.UPDATE
SideEffect.NEIGHBORS
//FAWE end
);
@Override

Datei anzeigen

@ -0,0 +1,240 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_3;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_3.PaperweightFaweAdapter;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.world.block.BlockTypes;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
public class PaperweightServerLevelDelegateProxy implements InvocationHandler {
private static final Logger LOGGER = LogManagerCompat.getLogger();
// FAWE start - extent not EditSession
private final Extent editSession;
//FAWE end
private final ServerLevel serverLevel;
//FAWE start - use FAWE adapter
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
//FAWE end
//FAWE start - force error if method not caught by this instance
private final boolean errorOnPassthrough;
//FAWE end
private PaperweightServerLevelDelegateProxy(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
this.editSession = editSession;
this.serverLevel = serverLevel;
//FAWE start
this.errorOnPassthrough = false;
//FAWE end
}
public static WorldGenLevel newInstance(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
return (WorldGenLevel) Proxy.newProxyInstance(
serverLevel.getClass().getClassLoader(),
serverLevel.getClass().getInterfaces(),
new PaperweightServerLevelDelegateProxy(editSession, serverLevel, adapter)
);
}
//FAWE start - force error if method not caught by this instance
private PaperweightServerLevelDelegateProxy(Extent extent, ServerLevel serverLevel, boolean errorOnPassthrough) {
this.editSession = extent;
this.serverLevel = serverLevel;
this.errorOnPassthrough = errorOnPassthrough;
}
public static WorldGenLevel newInstance(Extent extent, ServerLevel serverLevel, boolean errorOnPassthrough) {
return (WorldGenLevel) Proxy.newProxyInstance(
serverLevel.getClass().getClassLoader(),
serverLevel.getClass().getInterfaces(),
new PaperweightServerLevelDelegateProxy(extent, serverLevel, errorOnPassthrough)
);
}
//FAWE end
@Nullable
private BlockEntity getBlockEntity(BlockPos blockPos) {
BlockEntity tileEntity = this.serverLevel.getChunkAt(blockPos).getBlockEntity(blockPos);
if (tileEntity == null) {
return null;
}
BlockEntity newEntity = tileEntity.getType().create(blockPos, getBlockState(blockPos));
newEntity.loadWithComponents(
(CompoundTag) adapter.fromNativeLin(this.editSession.getFullBlock(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
).getNbtReference().getValue()),
this.serverLevel.registryAccess()
);
return newEntity;
}
private BlockState getBlockState(BlockPos blockPos) {
return adapter.adapt(this.editSession.getBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
}
private boolean setBlock(BlockPos blockPos, BlockState blockState) {
try {
return editSession.setBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ(), adapter.adapt(blockState));
} catch (MaxChangedBlocksException e) {
throw new RuntimeException(e);
}
}
private boolean removeBlock(BlockPos blockPos, boolean bl) {
try {
return editSession.setBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ(), BlockTypes.AIR.getDefaultState());
} catch (MaxChangedBlocksException e) {
throw new RuntimeException(e);
}
}
private FluidState getFluidState(BlockPos pos) {
return getBlockState(pos).getFluidState();
}
private boolean isWaterAt(BlockPos pos) {
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//FAWE start - cannot use switch where method names are equal
String methodName = method.getName();
if (Refraction.pickName("getBlockState", "a_").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
// getBlockState
return getBlockState(blockPos);
}
}
if (Refraction.pickName("getBlockEntity", "c_").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
// getBlockEntity
return getBlockEntity(blockPos);
}
}
if ("a".equals(methodName) || "setBlock".equals(methodName) || "removeBlock".equals(methodName) || "destroyBlock".equals(
methodName)) {
if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) {
// setBlock
return setBlock(blockPos, blockState);
} else if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) {
// removeBlock (and also matches destroyBlock)
return removeBlock(blockPos, bl);
}
}
//FAWE start
if (Refraction.pickName("getFluidState", "b_").equals(methodName)) { //net.minecraft.world.level.BlockGetter
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
return getFluidState(blockPos);
}
}
if (Refraction.pickName("isWaterAt", "z").equals(methodName)) { //net.minecraft.world.level.LevelReader
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
return isWaterAt(blockPos);
}
}
if (Refraction.pickName("getEntities", "a_").equals(methodName)) { //net.minecraft.world.level.EntityGetter
if (args.length == 2 && args[0] instanceof Entity && args[1] instanceof AABB) {
return new ArrayList<>();
}
}
// Specific passthroughs that we want to allow
// net.minecraft.world.level.BlockAndTintGetter
if (Refraction.pickName("getRawBrightness", "b").equals(methodName)) {
return method.invoke(this.serverLevel, args);
}
// net.minecraft.world.level.LevelHeightAccessor
if (Refraction.pickName("getMaxBuildHeight", "al").equals(methodName)) {
if (args.length == 0) {
return method.invoke(this.serverLevel, args);
}
}
// net.minecraft.world.level.SignalGetter
if (Refraction.pickName("hasNeighborSignal", "C").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getSignal", "c").equals(methodName)) {
if (args.length == 2 && args[0] instanceof BlockPos && args[1] instanceof Direction) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getControlInputSignal", "a").equals(methodName)) {
if (args.length == 3 && args[0] instanceof BlockPos && args[1] instanceof Direction && args[2] instanceof Boolean) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getDirectSignal", "a").equals(methodName)) {
if (args.length == 2 && args[0] instanceof BlockPos && args[1] instanceof Direction) {
return method.invoke(this.serverLevel, args);
}
}
//FAWE start - force error if method not caught by this instance
if (errorOnPassthrough) {
LOGGER.error(
"""
Attempted passthough of method {}.
Method argument types: {}
Method argument values: {}
""",
method.getName(),
Arrays.stream(args).map(a -> a.getClass().getName()).toList(),
Arrays.stream(args).map(Object::toString).toList()
);
throw new FaweException("Method required passthrough.");
}
//FAWE end
return method.invoke(this.serverLevel, args);
}
}

Datei anzeigen

@ -4,6 +4,7 @@ import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
@ -13,6 +14,7 @@ import com.fastasyncworldedit.core.util.NbtUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.mojang.serialization.Codec;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseItemStack;
@ -22,6 +24,7 @@ import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_3.PaperweightAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_3.regen.PaperweightRegen;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
@ -34,7 +37,6 @@ import com.sk89q.worldedit.registry.state.IntegerProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.concurrency.LazyReference;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.world.RegenOptions;
@ -279,9 +281,16 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return state.toBaseBlock();
}
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.HISTORY,
SideEffect.HEIGHTMAPS,
SideEffect.LIGHTING,
SideEffect.NEIGHBORS
);
@Override
public Set<SideEffect> getSupportedSideEffects() {
return SideEffectSet.defaults().getSideEffectsToApply();
return SUPPORTED_SIDE_EFFECTS;
}
@Override
@ -425,6 +434,10 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return material.getCraftBlockData();
}
public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockState) {
return Block.stateById(getOrdinalToIbdID()[blockState.getOrdinal()]);
}
@Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = getServerLevel(world);
@ -605,6 +618,11 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return new PaperweightPostProcessor();
}
@Override
public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, Region region) {
return new PaperweightPlacementStateProcessor(extent, mask, region);
}
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
if (PaperLib.isPaper()) { // Papers new chunk system has no related replacement - therefor we assume true.
return true;

Datei anzeigen

@ -0,0 +1,143 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_3;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightFaweMutableBlockPlaceContext extends BlockPlaceContext {
private static final BlockHitResult DEFAULT_BLOCK_HIT = new BlockHitResult(
new Vec3(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE),
Direction.NORTH,
new BlockPos(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE),
false
);
private final ServerLevel level;
private BlockHitResult hitResult = null;
private Direction direction = null;
private BlockPos relativePos;
@SuppressWarnings("DataFlowIssue")
public PaperweightFaweMutableBlockPlaceContext(ServerLevel level) {
super(
level,
null,
null,
null,
DEFAULT_BLOCK_HIT
);
this.level = level;
this.replaceClicked = false;
}
public PaperweightFaweMutableBlockPlaceContext withSetting(BlockHitResult hitResult, Direction direction) {
this.hitResult = hitResult;
this.direction = direction;
this.relativePos = hitResult.getBlockPos().relative(hitResult.getDirection());
return this;
}
@Override
@Nonnull
public BlockPos getClickedPos() {
return this.relativePos;
}
@Override
@Nonnull
public Direction getClickedFace() {
return this.hitResult.getDirection();
}
@Override
@Nonnull
public Vec3 getClickLocation() {
return this.hitResult.getLocation();
}
@Override
public boolean isInside() {
return this.hitResult.isInside();
}
@Override
@SuppressWarnings("NullableProblems")
public ItemStack getItemInHand() {
return ItemStack.EMPTY;
}
@Nullable
@Override
public Player getPlayer() {
return null;
}
@Override
@SuppressWarnings("NullableProblems")
public InteractionHand getHand() {
return null;
}
@Override
@Nonnull
public Level getLevel() {
return this.level;
}
@Override
@Nonnull
public Direction getHorizontalDirection() {
return this.direction.getAxis() == Direction.Axis.Y ? Direction.NORTH : this.direction;
}
@Override
public boolean isSecondaryUseActive() {
return false;
}
@Override
public float getRotation() {
return (float) (this.direction.get2DDataValue() * 90);
}
@Override
public boolean canPlace() {
return this.getLevel().getBlockState(this.getClickedPos()).canBeReplaced(this);
}
@Override
public boolean replacingClickedOnBlock() {
return false;
}
@Override
@Nonnull
public Direction getNearestLookingDirection() {
return direction;
}
@Override
@Nonnull
public Direction getNearestLookingVerticalDirection() {
return direction.getAxis() == Direction.Axis.Y ? Direction.UP : Direction.DOWN;
}
@Override
@Nonnull
public Direction[] getNearestLookingDirections() {
return new Direction[]{direction};
}
}

Datei anzeigen

@ -21,6 +21,7 @@ import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
@ -837,7 +838,9 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
nmsChunk.mustNotSave = false;
nmsChunk.markUnsaved();
// send to player
if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) {
if (!set
.getSideEffectSet()
.shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) {
this.send();
}
if (finalizer != null) {

Datei anzeigen

@ -0,0 +1,136 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_3;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import org.enginehub.linbus.tree.LinCompoundTag;
import sun.misc.Unsafe;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightLevelProxy extends ServerLevel {
protected ServerLevel serverLevel;
private PaperweightPlacementStateProcessor processor;
private PaperweightFaweAdapter adapter;
@SuppressWarnings("DataFlowIssue")
private PaperweightLevelProxy() {
super(null, null, null, null, null, null, null, true, 0L, null, true, null, null, null, null);
throw new IllegalStateException("Cannot be instantiated");
}
public static PaperweightLevelProxy getInstance(ServerLevel serverLevel, PaperweightPlacementStateProcessor processor) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PaperweightLevelProxy newLevel;
try {
newLevel = (PaperweightLevelProxy) unsafe.allocateInstance(PaperweightLevelProxy.class);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
newLevel.processor = processor;
newLevel.adapter = ((PaperweightFaweAdapter) WorldEditPlugin.getInstance().getBukkitImplAdapter());
newLevel.serverLevel = serverLevel;
return newLevel;
}
@Nullable
@Override
public BlockEntity getBlockEntity(@Nonnull BlockPos blockPos) {
if (blockPos.getX() == Integer.MAX_VALUE) {
return null;
}
LinCompoundTag tag = processor.getTileAt(blockPos.getX(), blockPos.getY(), blockPos.getZ());
if (tag == null) {
return null;
}
BlockState state = adapter.adapt(processor.getBlockStateAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
if (!(state.getBlock() instanceof EntityBlock entityBlock)) {
return null;
}
BlockEntity tileEntity = entityBlock.newBlockEntity(blockPos, state);
tileEntity.loadWithComponents((CompoundTag) adapter.fromNativeLin(tag), serverLevel.registryAccess());
return tileEntity;
}
@Override
@Nonnull
public BlockState getBlockState(@Nonnull BlockPos blockPos) {
if (blockPos.getX() == Integer.MAX_VALUE) {
return Blocks.AIR.defaultBlockState();
}
com.sk89q.worldedit.world.block.BlockState state = processor.getBlockStateAt(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
);
return adapter.adapt(state);
}
@SuppressWarnings("unused")
@Override
@Nonnull
public FluidState getFluidState(@Nonnull BlockPos pos) {
if (pos.getX() == Integer.MAX_VALUE) {
return Fluids.EMPTY.defaultFluidState();
}
return getBlockState(pos).getFluidState();
}
@SuppressWarnings("unused")
@Override
public boolean isWaterAt(@Nonnull BlockPos pos) {
if (pos.getX() == Integer.MAX_VALUE) {
return false;
}
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
@Override
public int getHeight() {
return serverLevel.getHeight();
}
@Override
public int getMinY() {
return serverLevel.getMinY();
}
@Override
public int getMaxY() {
return serverLevel.getMaxY();
}
@Override
public boolean isInsideBuildHeight(int blockY) {
return serverLevel.isInsideBuildHeight(blockY);
}
@Override
public boolean isOutsideBuildHeight(BlockPos pos) {
return serverLevel.isOutsideBuildHeight(pos);
}
@Override
public boolean isOutsideBuildHeight(int blockY) {
return serverLevel.isOutsideBuildHeight(blockY);
}
@Override
public WorldBorder getWorldBorder() {
return serverLevel.getWorldBorder();
}
}

Datei anzeigen

@ -0,0 +1,111 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_3;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.fastasyncworldedit.core.wrappers.WorldWrapper;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import org.bukkit.craftbukkit.CraftWorld;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class PaperweightPlacementStateProcessor extends PlacementStateProcessor {
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
private final PaperweightFaweMutableBlockPlaceContext mutableBlockPlaceContext;
private final PaperweightLevelProxy proxyLevel;
public PaperweightPlacementStateProcessor(Extent extent, BlockTypeMask mask, Region region) {
super(extent, mask, region);
World world = ExtentTraverser.getWorldFromExtent(extent);
if (world == null) {
throw new UnsupportedOperationException(
"World is required for PlacementStateProcessor but none found in given extent.");
}
BukkitWorld bukkitWorld;
if (world instanceof WorldWrapper wrapper) {
bukkitWorld = (BukkitWorld) wrapper.getParent();
} else {
bukkitWorld = (BukkitWorld) world;
}
this.proxyLevel = PaperweightLevelProxy.getInstance(((CraftWorld) bukkitWorld.getWorld()).getHandle(), this);
this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel);
}
private PaperweightPlacementStateProcessor(
Extent extent,
BlockTypeMask mask,
Map<SecondPass, Character> crossChunkSecondPasses,
ServerLevel serverLevel,
ThreadLocal<PlacementStateProcessor> threadProcessors,
Region region,
AtomicBoolean finished
) {
super(extent, mask, crossChunkSecondPasses, threadProcessors, region, finished);
this.proxyLevel = PaperweightLevelProxy.getInstance(serverLevel, this);
this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel);
}
@Override
protected char getStateAtFor(
int x,
int y,
int z,
BlockState state,
Vector3 clickPos,
Direction clickedFaceDirection,
BlockVector3 clickedBlock
) {
Block block = ((PaperweightBlockMaterial) state.getMaterial()).getBlock();
Vec3 pos = new Vec3(clickPos.x(), clickPos.y(), clickPos.z());
net.minecraft.core.Direction side = net.minecraft.core.Direction.valueOf(clickedFaceDirection.toString());
BlockPos blockPos = new BlockPos(clickedBlock.x(), clickedBlock.y(), clickedBlock.z());
net.minecraft.world.level.block.state.BlockState newState = block.getStateForPlacement(mutableBlockPlaceContext.withSetting(
new BlockHitResult(pos, side, blockPos, false),
side.getOpposite()
));
return newState == null ? BlockTypesCache.ReservedIDs.AIR : adapter.ibdIDToOrdinal(Block.BLOCK_STATE_REGISTRY.getId(
newState));
}
@Override
@Nullable
public Extent construct(Extent child) {
if (child == getExtent()) {
return this;
}
return new PaperweightPlacementStateProcessor(child, mask, region);
}
@Override
public PlacementStateProcessor fork() {
return new PaperweightPlacementStateProcessor(
extent,
mask,
postCompleteSecondPasses,
proxyLevel.serverLevel,
threadProcessors,
region,
finished
);
}
}

Datei anzeigen

@ -21,6 +21,7 @@ package com.sk89q.worldedit.bukkit;
import com.fastasyncworldedit.bukkit.util.MinecraftVersion;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.google.common.collect.Sets;
@ -37,7 +38,10 @@ import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extension.platform.MultiUserPlatform;
import com.sk89q.worldedit.extension.platform.Preference;
import com.sk89q.worldedit.extension.platform.Watchdog;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.lifecycle.Lifecycled;
import com.sk89q.worldedit.world.DataFixer;
@ -309,5 +313,10 @@ public class BukkitServerInterface extends AbstractPlatform implements MultiUser
}
return this.plugin.getBukkitImplAdapter().getTickingPostProcessor();
}
@Override
public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, Region region) {
return this.plugin.getBukkitImplAdapter().getPlatformPlacementProcessor(extent, mask, region);
}
//FAWE end
}

Datei anzeigen

@ -23,6 +23,7 @@ import com.fastasyncworldedit.bukkit.FaweBukkit;
import com.fastasyncworldedit.bukkit.adapter.IBukkitAdapter;
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunkGet;
@ -34,6 +35,7 @@ import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
@ -402,5 +404,13 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
default IBatchProcessor getTickingPostProcessor() {
return null;
}
/**
* Returns an {@link PlacementStateProcessor} instance for processing placed blocks to "fix" them.
* @since TODO
*/
default PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, Region region) {
return null;
}
//FAWE end
}

Datei anzeigen

@ -32,6 +32,8 @@ import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.world.World;
import org.apache.logging.log4j.Logger;
@ -355,8 +357,12 @@ public class FaweAPI {
if (unwrapped instanceof IQueueExtent) {
queue = (IQueueExtent) unwrapped;
} else if (Settings.settings().QUEUE.PARALLEL_THREADS > 1) {
ParallelQueueExtent parallel =
new ParallelQueueExtent(Fawe.instance().getQueueHandler(), world, true);
ParallelQueueExtent parallel = new ParallelQueueExtent(
Fawe.instance().getQueueHandler(),
world,
true,
SideEffectSet.none().with(SideEffect.LIGHTING)
);
queue = parallel.getExtent();
} else {
queue = Fawe.instance().getQueueHandler().getQueue(world);

Datei anzeigen

@ -0,0 +1,51 @@
package com.fastasyncworldedit.core.extension.factory.parser.mask;
import com.fastasyncworldedit.core.extension.factory.parser.RichParser;
import com.fastasyncworldedit.core.function.mask.Adjacent2DMask;
import com.fastasyncworldedit.core.function.mask.AdjacentAny2DMask;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.util.SuggestionHelper;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.function.mask.Mask;
import javax.annotation.Nonnull;
import java.util.stream.Stream;
public class Adjacent2DMaskParser extends RichParser<Mask> {
public Adjacent2DMaskParser(WorldEdit worldEdit) {
super(worldEdit, "~2d", "adjacent2d");
}
@Override
protected Stream<String> getSuggestions(String argumentInput, int index) {
if (index == 0) {
return worldEdit.getMaskFactory().getSuggestions(argumentInput).stream();
} else if (index == 1 || index == 2) {
return SuggestionHelper.suggestPositiveDoubles(argumentInput);
}
return Stream.empty();
}
@Override
protected Mask parseFromInput(@Nonnull String[] arguments, ParserContext context) throws InputParseException {
if (arguments.length == 0) {
return null;
}
Mask subMask = worldEdit.getMaskFactory().parseFromInput(arguments[0], context);
int min = arguments.length > 1 ? Integer.parseInt(arguments[1]) : -1;
int max = arguments.length > 2 ? Integer.parseInt(arguments[2]) : -1;
if (min == -1 && max == -1) {
min = 1;
max = 4;
} else if (max == -1) {
max = min;
}
if (max >= 4 && min == 1) {
return new AdjacentAny2DMask(subMask);
}
return new Adjacent2DMask(subMask, min, max);
}
}

Datei anzeigen

@ -6,16 +6,18 @@ import com.fastasyncworldedit.core.queue.Filter;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
@ -25,6 +27,7 @@ import com.sk89q.worldedit.world.block.BlockType;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public class PassthroughExtent extends AbstractDelegateExtent {
@ -194,17 +197,55 @@ public class PassthroughExtent extends AbstractDelegateExtent {
return getExtent().getBlock(position);
}
@Override
public BlockState getBlock(int x, int y, int z) {
return getExtent().getBlock(x, y, z);
}
@Override
public BaseBlock getFullBlock(BlockVector3 position) {
return getExtent().getFullBlock(position);
}
@Override
public BaseBlock getFullBlock(int x, int y, int z) {
return getExtent().getFullBlock(x, y, z);
}
@Override
public List<? extends Entity> getEntities(Region region) {
return getExtent().getEntities(region);
}
@Override
public List<? extends Entity> getEntities() {
return getExtent().getEntities();
}
@Nullable
@Override
public Entity createEntity(Location location, BaseEntity entity) {
return getExtent().createEntity(location, entity);
}
@Nullable
@Override
public Entity createEntity(Location location, BaseEntity entity, UUID uuid) {
return getExtent().createEntity(location, entity, uuid);
}
@Override
@Deprecated
public <T extends BlockStateHolder<T>> boolean setBlock(BlockVector3 position, T block) throws WorldEditException {
return getExtent().setBlock(position, block);
}
@Override
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T block) throws
WorldEditException {
return getExtent().setBlock(x, y, z, block);
}
@Override
public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException {
return getExtent().setTile(x, y, z, tile);
@ -216,9 +257,8 @@ public class PassthroughExtent extends AbstractDelegateExtent {
}
@Override
@Nullable
public Operation commit() {
return getExtent().commit();
public boolean setBiome(int x, int y, int z, BiomeType biome) {
return getExtent().setBiome(x, y, z, biome);
}
@Override
@ -226,6 +266,11 @@ public class PassthroughExtent extends AbstractDelegateExtent {
return getExtent().cancel();
}
@Override
public void removeEntity(int x, int y, int z, UUID uuid) {
getExtent().removeEntity(x, y, z, uuid);
}
@Override
public boolean isQueueEnabled() {
return getExtent().isQueueEnabled();
@ -251,6 +296,16 @@ public class PassthroughExtent extends AbstractDelegateExtent {
return getExtent().apply(region, filter, full);
}
@Override
public BiomeType getBiome(BlockVector3 position) {
return getExtent().getBiome(position);
}
@Override
public BiomeType getBiomeType(int x, int y, int z) {
return getExtent().getBiomeType(x, y, z);
}
@Override
public <T extends Filter> T apply(Iterable<BlockVector3> positions, T filter) {
return getExtent().apply(positions, filter);

Datei anzeigen

@ -0,0 +1,545 @@
package com.fastasyncworldedit.core.extent.processor;
import com.fastasyncworldedit.core.configuration.Caption;
import com.fastasyncworldedit.core.extent.NullExtent;
import com.fastasyncworldedit.core.extent.filter.block.FilterBlock;
import com.fastasyncworldedit.core.function.mask.AdjacentAny2DMask;
import com.fastasyncworldedit.core.math.BlockVector3ChunkMap;
import com.fastasyncworldedit.core.math.MutableBlockVector3;
import com.fastasyncworldedit.core.math.MutableVector3;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
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.WorldEditException;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockCategoryMask;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockCategories;
import com.sk89q.worldedit.world.block.BlockCategory;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import org.enginehub.linbus.tree.LinCompoundTag;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
/**
* Processor/pattern that uses Minecraft internal methods to determine the shape of blocks, e.g. stairs and fences
*
* @since TODO
*/
public abstract class PlacementStateProcessor extends AbstractDelegateExtent implements IBatchProcessor, Pattern {
private static final Direction[] NESW = new Direction[]{Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
private static final int CHUNK_BLOCK_POS_MASK = -1 << 4;
private static volatile boolean SETUP = false;
private static BlockTypeMask DEFAULT_MASK = null;
private static BlockTypeMask IN_FIRST_PASS = null;
private static BlockTypeMask REQUIRES_SECOND_PASS = null;
private static BlockTypeMask IN_FIRST_PASS_WITHOUT_SECOND = null;
private static AdjacentAny2DMask ADJACENT_STAIR_MASK = null;
protected final Extent extent;
protected final BlockTypeMask mask;
protected final Region region;
protected final Map<SecondPass, Character> postCompleteSecondPasses;
protected final ThreadLocal<PlacementStateProcessor> threadProcessors;
protected final AtomicBoolean finished;
private final MutableVector3 clickPos = new MutableVector3();
private final MutableBlockVector3 clickedBlock = new MutableBlockVector3();
private final MutableBlockVector3 placedBlock = new MutableBlockVector3(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
private IChunkGet processChunkGet = null;
private IChunkSet processChunkSet = null;
private int processChunkX;
private int processChunkZ;
/**
* Processor/pattern for performing block updates, e.g. stair shape and glass pane connections
*
* @param extent Extent to use
* @param mask Mask of blocks to perform updates on
* @since TODO
*/
public PlacementStateProcessor(Extent extent, BlockTypeMask mask, Region region) {
super(extent);
// Required here as child classes are located within adapters and will therefore be statically accessed on startup,
// meaning we attempt to access BlockTypes class before it is correctly initialised.
if (!SETUP) {
synchronized (PlacementStateProcessor.class) {
if (!SETUP) {
setup();
}
}
}
this.extent = extent;
this.mask = mask == null ? DEFAULT_MASK : mask;
this.region = region;
this.postCompleteSecondPasses = new ConcurrentHashMap<>();
this.threadProcessors = ThreadLocal.withInitial(this::fork);
this.finished = new AtomicBoolean();
}
protected PlacementStateProcessor(
Extent extent,
BlockTypeMask mask,
Map<SecondPass, Character> crossChunkSecondPasses,
ThreadLocal<PlacementStateProcessor> threadProcessors,
Region region,
AtomicBoolean finished
) {
super(extent);
this.extent = extent;
this.mask = mask;
this.region = region;
this.postCompleteSecondPasses = crossChunkSecondPasses;
this.threadProcessors = threadProcessors;
this.finished = finished;
}
private static void setup() {
NullExtent nullExtent = new NullExtent(
com.sk89q.worldedit.extent.NullExtent.INSTANCE,
Caption.of("PlacementStateProcessor fell through to null extent")
);
IN_FIRST_PASS = new BlockTypeMask(nullExtent);
IN_FIRST_PASS.add(
BlockTypes.CHEST,
BlockTypes.TRAPPED_CHEST
);
IN_FIRST_PASS.add(BlockCategories.STAIRS.getAll());
IN_FIRST_PASS_WITHOUT_SECOND = new BlockTypeMask(nullExtent);
IN_FIRST_PASS_WITHOUT_SECOND.add(BlockCategories.STAIRS.getAll());
REQUIRES_SECOND_PASS = new BlockTypeMask(nullExtent);
REQUIRES_SECOND_PASS.add(
BlockTypes.IRON_BARS,
BlockTypes.GLASS_PANE,
BlockTypes.BLACK_STAINED_GLASS_PANE,
BlockTypes.BLUE_STAINED_GLASS_PANE,
BlockTypes.BROWN_STAINED_GLASS_PANE,
BlockTypes.LIGHT_BLUE_STAINED_GLASS_PANE,
BlockTypes.PINK_STAINED_GLASS_PANE,
BlockTypes.LIGHT_GRAY_STAINED_GLASS_PANE,
BlockTypes.GRAY_STAINED_GLASS_PANE,
BlockTypes.CYAN_STAINED_GLASS_PANE,
BlockTypes.PURPLE_STAINED_GLASS_PANE,
BlockTypes.GREEN_STAINED_GLASS_PANE,
BlockTypes.LIME_STAINED_GLASS_PANE,
BlockTypes.MAGENTA_STAINED_GLASS_PANE,
BlockTypes.YELLOW_STAINED_GLASS_PANE,
BlockTypes.ORANGE_STAINED_GLASS_PANE,
BlockTypes.RED_STAINED_GLASS_PANE,
BlockTypes.WHITE_STAINED_GLASS_PANE,
BlockTypes.TRIPWIRE,
BlockTypes.TWISTING_VINES_PLANT,
BlockTypes.CAVE_VINES_PLANT,
BlockTypes.WEEPING_VINES_PLANT,
BlockTypes.VINE,
BlockTypes.REDSTONE_WIRE
);
BlockCategory[] categories = new BlockCategory[]{BlockCategories.FENCES, BlockCategories.FENCE_GATES, BlockCategories.WALLS, BlockCategories.CAVE_VINES};
for (BlockCategory category : categories) {
if (category != null) {
REQUIRES_SECOND_PASS.add(category.getAll());
}
}
DEFAULT_MASK = REQUIRES_SECOND_PASS.copy();
DEFAULT_MASK.add(
BlockTypes.CHORUS_PLANT,
BlockTypes.DRIPSTONE_BLOCK,
BlockTypes.POINTED_DRIPSTONE,
BlockTypes.BIG_DRIPLEAF,
BlockTypes.BIG_DRIPLEAF_STEM,
BlockTypes.CAMPFIRE,
BlockTypes.CHEST,
BlockTypes.TRAPPED_CHEST,
BlockTypes.CRAFTER,
BlockTypes.MUSHROOM_STEM,
BlockTypes.BROWN_MUSHROOM_BLOCK,
BlockTypes.RED_MUSHROOM_BLOCK
);
categories = new BlockCategory[]{BlockCategories.STAIRS, BlockCategories.BAMBOO_BLOCKS, BlockCategories.TALL_FLOWERS};
for (BlockCategory category : categories) {
if (category != null) {
DEFAULT_MASK.add(category.getAll());
}
}
ADJACENT_STAIR_MASK = new AdjacentAny2DMask(new BlockCategoryMask(nullExtent, BlockCategories.STAIRS), false);
SETUP = true;
}
@Override
public IChunkSet processSet(IChunk iChunk, IChunkGet chunkGet, IChunkSet chunkSet) {
if (finished.get()) {
return chunkSet;
}
PlacementStateProcessor threadProcessor = threadProcessors.get();
try {
threadProcessor.initProcess(iChunk, chunkGet, chunkSet);
return threadProcessor.process();
} finally {
threadProcessor.uninit();
}
}
private void initProcess(IChunk iChunk, IChunkGet chunkGet, IChunkSet chunkSet) {
this.processChunkX = iChunk.getX() << 4;
this.processChunkZ = iChunk.getZ() << 4;
this.processChunkGet = chunkGet;
this.processChunkSet = chunkSet;
}
private void uninit() {
this.processChunkGet = null;
this.processChunkSet = null;
}
private IChunkSet process() {
Map<BlockVector3, FaweCompoundTag> setTiles = processChunkSet.tiles();
for (int layer = processChunkGet.getMinSectionPosition(); layer <= processChunkGet.getMaxSectionPosition(); layer++) {
int layerY = layer << 4;
char[] set = processChunkSet.loadIfPresent(layer);
if (set == null) {
continue;
}
for (int y = 0, i = 0; y < 16; y++, i += 256) {
int blockY = layerY + y;
checkAndPerformUpdate(setTiles, set, i, blockY, true);
checkAndPerformUpdate(setTiles, set, i, blockY, false);
}
}
return processChunkSet;
}
private void checkAndPerformUpdate(
Map<BlockVector3, FaweCompoundTag> setTiles,
char[] set,
int index,
int blockY,
boolean firstPass
) {
for (int z = 0; z < 16; z++) {
int blockZ = processChunkZ + z;
for (int x = 0; x < 16; x++, index++) {
int blockX = processChunkX + x;
char ordinal = set[index];
BlockState state = BlockTypesCache.states[ordinal];
if (firstPass) {
if (!IN_FIRST_PASS.test(state)) {
continue;
}
} else if (IN_FIRST_PASS_WITHOUT_SECOND.test(state)) {
continue;
}
if (!mask.test(state)) {
continue;
}
boolean atEdge = x == 0 || x == 15 || z == 0 || z == 15;
if (!firstPass && atEdge && REQUIRES_SECOND_PASS.test(state)) {
postCompleteSecondPasses.put(new SecondPass(
blockX,
blockY,
blockZ,
setTiles.isEmpty() ? null : ((BlockVector3ChunkMap<FaweCompoundTag>) setTiles).remove(x, blockY, z)
), ordinal);
set[index] = BlockTypesCache.ReservedIDs.__RESERVED__;
continue;
}
if (state.getBlockType().equals(BlockTypes.CHEST) || state.getBlockType().equals(BlockTypes.TRAPPED_CHEST)) {
placedBlock.setComponents(blockX, blockY, blockZ);
} else {
placedBlock.setComponents(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
}
char newOrdinal = getBlockOrdinal(blockX, blockY, blockZ, state);
if (newOrdinal == ordinal) {
continue;
}
set[index] = newOrdinal;
}
}
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.CHANGING_BLOCKS;
}
@Override
public void finish() {
flush();
}
@Override
public void flush() {
finished.set(true);
for (Map.Entry<SecondPass, Character> entry : postCompleteSecondPasses.entrySet()) {
BlockState state;
char ordinal = entry.getValue();
SecondPass secondPass = entry.getKey();
if (ordinal != 0) {
state = BlockTypesCache.states[ordinal];
} else {
state = extent.getBlock(secondPass.x, secondPass.y, secondPass.z);
}
char newOrdinal = getBlockOrdinal(secondPass.x, secondPass.y, secondPass.z, state);
if (newOrdinal == state.getOrdinalChar() && ordinal == 0) {
continue;
}
if (secondPass.tile != null) {
extent.tile(secondPass.x, secondPass.y, secondPass.z, secondPass.tile);
}
extent.setBlock(secondPass.x, secondPass.y, secondPass.z, BlockTypesCache.states[newOrdinal]);
}
postCompleteSecondPasses.clear();
}
@Override
public abstract PlacementStateProcessor fork();
protected abstract char getStateAtFor(
int x,
int y,
int z,
BlockState state,
Vector3 clickPos,
Direction clickedFaceDirection,
BlockVector3 clickedBlock
);
public BlockState getBlockStateAt(int x, int y, int z) {
Character ord = postCompleteSecondPasses.get(new SecondPass(x, y, z, null));
if (ord != null && ord != 0) {
return BlockTypesCache.states[ord];
}
if (processChunkSet == null || (x & CHUNK_BLOCK_POS_MASK) != processChunkX || (z & CHUNK_BLOCK_POS_MASK) != processChunkZ) {
return extent.getBlock(x, y, z);
}
char[] set = processChunkSet.loadIfPresent(y >> 4);
if (set == null) {
return processChunkGet.getBlock(x & 15, y, z & 15);
}
char ordinal = set[(y & 15) << 8 | (z & 15) << 4 | (x & 15)];
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
return processChunkGet.getBlock(x & 15, y, z & 15);
}
BlockState state = BlockTypesCache.states[ordinal];
// "Hack" for chests as internal server methods will only accept "single" chests for joining
if (state.getBlockType().equals(BlockTypes.CHEST) || state.getBlockType().equals(BlockTypes.TRAPPED_CHEST)) {
String shape = state.getState(PropertyKey.TYPE).toString();
if (shape.equals("right")) {
Direction facing = state.getState(PropertyKey.FACING);
Direction left = facing.getLeft();
int testX = x + left.getBlockX();
int testZ = z + left.getBlockZ();
if (placedBlock.isAt(testX, y, testZ)) {
return state.with(PropertyKey.TYPE, "single");
}
} else if(shape.equals("left")) {
Direction facing = state.getState(PropertyKey.FACING);
Direction right = facing.getRight();
int testX = x + right.getBlockX();
int testZ = z + right.getBlockZ();
if (placedBlock.isAt(testX, y, testZ)) {
return state.with(PropertyKey.TYPE, "single");
}
}
}
return state;
}
public LinCompoundTag getTileAt(int x, int y, int z) {
SecondPass secondPass = new SecondPass(x, y, z, null);
Character ord = postCompleteSecondPasses.get(secondPass);
if (ord != null && ord != 0) {
// This should be rare enough...
for (SecondPass pass : postCompleteSecondPasses.keySet()) {
if (pass.hashCode() == secondPass.hashCode()) {
return pass.tile != null ? pass.tile.linTag() : BlockTypesCache.states[ord].getNbt();
}
}
return BlockTypesCache.states[ord].getNbt();
}
if (processChunkSet == null || (x & CHUNK_BLOCK_POS_MASK) != processChunkX || (z & CHUNK_BLOCK_POS_MASK) != processChunkZ) {
return extent.getFullBlock(x, y, z).getNbt();
}
FaweCompoundTag tile = processChunkSet.tile(x & 15, y, z & 15);
if (tile != null) {
return tile.linTag();
}
char[] set = processChunkSet.loadIfPresent(y >> 4);
if (set == null) {
return processChunkGet.getFullBlock(x & 15, y, z & 15).getNbt();
}
char ordinal = set[(y & 15) << 8 | (z & 15) << 4 | (x & 15)];
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
return processChunkGet.getFullBlock(x & 15, y, z & 15).getNbt();
}
return BlockTypesCache.states[ordinal].getNbt();
}
private char getBlockOrdinal(int blockX, int blockY, int blockZ, BlockState state) {
char override = getOverrideBlockOrdinal(blockX, blockY, blockZ, state);
if (override != BlockTypesCache.ReservedIDs.__RESERVED__) {
return override;
}
EnumSet<Direction> dirs = Direction.getDirections(state);
Direction clickedFaceDirection = null; // This should be always be set by the below.
Set<String> states = state.getStates().keySet().stream().map(Property::getName).collect(Collectors.toSet());
if (dirs.isEmpty() || states.contains("NORTH") && states.contains("EAST")) {
clickPos.setComponents(blockX + 0.5d, blockY, blockZ + 0.5d);
clickedFaceDirection = Direction.UP;
clickedBlock.setComponents(blockX, blockY - 1, blockZ);
} else {
boolean hadNesw = false;
for (Direction dir : NESW) {
if (dirs.contains(dir)) {
clickedFaceDirection = dir.getLeft().getLeft();
if (state.getBlockType() == BlockTypes.CHEST || state.getBlockType() == BlockTypes.TRAPPED_CHEST) {
Direction tmp = clickedFaceDirection;
clickedFaceDirection = dir;
dir = tmp;
}
clickPos.setComponents(
(double) blockX + 0.5 * (1 + dir.getBlockX()),
(double) blockY + 0.2,
(double) blockZ + 0.5 * (1 + dir.getBlockZ())
);
clickedBlock.setComponents(blockX, blockY, blockZ).add(dir.toBlockVector());
hadNesw = true;
break;
}
}
if (hadNesw) {
if (dirs.contains(Direction.UP)) {
clickPos.mutY(blockY + 0.6);
}
} else if (dirs.contains(Direction.UP)) {
clickedFaceDirection = Direction.DOWN;
clickPos.setComponents(blockX + 0.5d, blockY + 1d, blockZ + 0.5d);
clickedBlock.setComponents(blockX, blockY + 1, blockZ);
} else if (dirs.contains(Direction.DOWN)) {
clickedFaceDirection = Direction.UP;
clickPos.setComponents(blockX + 0.5d, blockY - 1d, blockZ + 0.5d);
clickedBlock.setComponents(blockX, blockY - 1, blockZ);
}
}
return getStateAtFor(blockX, blockY, blockZ, state, clickPos, clickedFaceDirection, clickedBlock);
}
protected char getOverrideBlockOrdinal(int blockX, int blockY, int blockZ, BlockState state) {
if (BlockCategories.TALL_FLOWERS.contains(state)) {
PropertyKey propertyKey = PropertyKey.HALF;
BlockState plantState = extent.getBlock(blockX, blockY - 1, blockZ).getBlockType().equals(state.getBlockType())
? state.with(propertyKey, "upper")
: state.with(propertyKey, "lower");
return plantState.getOrdinalChar();
}
return BlockTypesCache.ReservedIDs.__RESERVED__;
}
@Override
public void applyBlock(FilterBlock block) {
if (finished.get()) {
return;
}
BlockState state = BlockTypesCache.states[block.getOrdinal()];
if (!mask.test(state)) {
return;
}
if (REQUIRES_SECOND_PASS.test(block.getBlock()) && ADJACENT_STAIR_MASK.test(extent, block)) {
postCompleteSecondPasses.put(new SecondPass(block), (char) 0);
}
char ordinal = (char) block.getOrdinal();
char newOrdinal = getBlockOrdinal(block.x(), block.y(), block.z(), block.getBlock());
if (ordinal != newOrdinal) {
block.setBlock(BlockTypesCache.states[newOrdinal]);
}
}
@Override
public boolean apply(Extent orDefault, BlockVector3 get, BlockVector3 set) throws WorldEditException {
if (orDefault == null) {
orDefault = extent;
}
BaseBlock block = orDefault.getFullBlock(get);
if (!mask.test(block)) {
return false;
}
if (REQUIRES_SECOND_PASS.test(block) && ADJACENT_STAIR_MASK.test(extent, set)) {
postCompleteSecondPasses.put(new SecondPass(set), (char) 0);
return false;
}
char newOrdinal = getBlockOrdinal(set.x(), set.y(), set.z(), block.toBlockState());
if (block.getOrdinalChar() != newOrdinal) {
BlockState newState = BlockTypesCache.states[newOrdinal];
orDefault.setBlock(set.x(), set.y(), set.z(), newState);
LinCompoundTag nbt = block.getNbt();
if (nbt != null && newState.getBlockType() == block.getBlockType()) {
orDefault.tile(set.x(), set.y(), set.z(), FaweCompoundTag.of(nbt));
}
return true;
}
return false;
}
@Override
public BaseBlock applyBlock(BlockVector3 position) {
if (finished.get()) {
return null;
}
BaseBlock block = extent.getFullBlock(position);
if (!mask.test(block)) {
return null;
}
if (REQUIRES_SECOND_PASS.test(block) && ADJACENT_STAIR_MASK.test(extent, position)) {
postCompleteSecondPasses.put(new SecondPass(position), (char) 0);
return null;
}
char newOrdinal = getBlockOrdinal(position.x(), position.y(), position.z(), block.toBlockState());
if (block.getOrdinalChar() != newOrdinal) {
BlockState state = BlockTypesCache.states[newOrdinal];
LinCompoundTag nbt = block.getNbt();
if (nbt != null && state.getBlockType() == block.getBlockType()) {
state.toBaseBlock(nbt);
}
return state.toBaseBlock();
}
return null;
}
protected record SecondPass(int x, int y, int z, FaweCompoundTag tile) {
private SecondPass(BlockVector3 pos) {
this(pos.x(), pos.y(), pos.z(), null);
}
@Override
public int hashCode() {
return (x ^ (z << 12)) ^ (y << 24);
}
}
}

Datei anzeigen

@ -0,0 +1,66 @@
package com.fastasyncworldedit.core.function.mask;
import com.fastasyncworldedit.core.math.MutableBlockVector3;
import com.sk89q.worldedit.function.mask.AbstractMask;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.math.BlockVector3;
/**
* Mask that tests adjacency only in 2D/the same y-level
*
* @since TODO
*/
public class Adjacent2DMask extends AbstractMask {
private final int min;
private final int max;
private final Mask mask;
private final MutableBlockVector3 vector;
/**
* Mask that tests adjacency only in 2D/the same y-level
*
* @param mask Mask required to be adjacent
* @param requiredMin Minimum number of positive adjacency matches required
* @param requiredMax Maximum number of positive adjacency matches required
* @since TODO
*/
public Adjacent2DMask(Mask mask, int requiredMin, int requiredMax) {
this.mask = mask;
this.min = requiredMin;
this.max = requiredMax;
this.vector = new MutableBlockVector3();
}
@Override
public boolean test(BlockVector3 bv) {
vector.setComponents(bv);
double x = bv.x();
double z = bv.z();
vector.mutX(x + 1);
int count = 0;
if (mask.test(vector) && ++count == min && max >= 4) {
return true;
}
vector.mutX(x - 1);
if (mask.test(vector) && ++count == min && max >= 4) {
return true;
}
vector.mutX(x);
vector.mutZ(z + 1);
if (mask.test(vector) && ++count == min && max >= 4) {
return true;
}
vector.mutZ(z - 1);
if (mask.test(vector) && ++count == min && max >= 4) {
return true;
}
return count >= min && count <= max;
}
@Override
public Mask copy() {
return new Adjacent2DMask(mask.copy(), min, max);
}
}

Datei anzeigen

@ -0,0 +1,90 @@
package com.fastasyncworldedit.core.function.mask;
import com.fastasyncworldedit.core.math.MutableBlockVector3;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
import com.sk89q.worldedit.function.mask.AbstractMask;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.math.BlockVector3;
/**
* Optimized version of {@link Adjacent2DMask} for testing for any single adjacency
*
* @since TODO
*/
public class AdjacentAny2DMask extends AbstractMask {
private final Mask mask;
private final MutableBlockVector3 mutable;
/**
* Optimized version of {@link Adjacent2DMask} for testing for any single adjacency. Caches results of the adjacent mask
*
* @param mask Mask required to be adjacent
* @since TODO
*/
public AdjacentAny2DMask(Mask mask) {
this(mask, true);
}
/**
* Optimized version of {@link Adjacent2DMask} for testing for any single adjacency
*
* @param mask Mask required to be adjacent
* @param cache If the result of the adjacency mask should be cached
* @since TODO
*/
public AdjacentAny2DMask(Mask mask, boolean cache) {
this.mask = cache ? CachedMask.cache(mask) : mask;
mutable = new MutableBlockVector3();
}
@Override
public boolean test(BlockVector3 v) {
int x = v.x();
int y = v.y();
int z = v.z();
if (mask.test(mutable.setComponents(x + 1, y, z))) {
return true;
}
if (mask.test(mutable.setComponents(x - 1, y, z))) {
return true;
}
if (mask.test(mutable.setComponents(x, y, z + 1))) {
return true;
}
return mask.test(mutable.setComponents(x, y, z - 1));
}
/**
* Test this mask for the given extent
*
* @param extent extent to test in
* @param position position to test at
* @since TODO
*/
public boolean test(Extent extent, BlockVector3 position) {
if (!(mask instanceof AbstractExtentMask extentMask)) {
throw new UnsupportedOperationException("Adjacency mask must inherit from AbstractExtentMask");
}
int x = position.x();
int y = position.y();
int z = position.z();
if (extentMask.test(extent, mutable.setComponents(x + 1, y, z))) {
return true;
}
if (extentMask.test(extent, mutable.setComponents(x - 1, y, z))) {
return true;
}
if (extentMask.test(extent, mutable.setComponents(x, y, z + 1))) {
return true;
}
return extentMask.test(extent, mutable.setComponents(x, y, z - 1));
}
@Override
public Mask copy() {
return new AdjacentAny2DMask(mask.copy(), false);
}
}

Datei anzeigen

@ -23,7 +23,7 @@ public class AdjacentAnyMask extends AbstractMask implements ResettableMask {
}
AdjacentAnyMask(CachedMask mask, int minY, int maxY) {
this.mask = CachedMask.cache(mask);
this.mask = mask;
mutable = new MutableBlockVector3();
this.minY = minY;
this.maxY = maxY;

Datei anzeigen

@ -461,7 +461,6 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
} catch (EOFException ignored) {
} catch (Exception e) {
e.printStackTrace();
e.printStackTrace();
}
try {
is.close();

Datei anzeigen

@ -62,7 +62,7 @@ public class MutableBlockVector3 extends BlockVector3 {
}
@Override
public BlockVector3 getMinimum(BlockVector3 v2) {
public MutableBlockVector3 getMinimum(BlockVector3 v2) {
this.x = Math.min(v2.x(), x);
this.y = Math.min(v2.y(), y);
this.z = Math.min(v2.z(), z);
@ -70,7 +70,7 @@ public class MutableBlockVector3 extends BlockVector3 {
}
@Override
public BlockVector3 getMaximum(BlockVector3 v2) {
public MutableBlockVector3 getMaximum(BlockVector3 v2) {
this.x = Math.max(v2.x(), x);
this.y = Math.max(v2.y(), y);
this.z = Math.max(v2.z(), z);
@ -113,4 +113,140 @@ public class MutableBlockVector3 extends BlockVector3 {
return this;
}
@Override
public final MutableBlockVector3 withX(int x) {
this.x = x;
return this;
}
@Override
public final MutableBlockVector3 withY(int y) {
this.y = y;
return this;
}
@Override
public final MutableBlockVector3 withZ(int z) {
this.z = z;
return this;
}
@Override
public MutableBlockVector3 add(BlockVector3 other) {
return add(other.x(), other.y(), other.z());
}
@Override
public MutableBlockVector3 add(int x, int y, int z) {
this.x += x;
this.y += y;
this.z += z;
return this;
}
@Override
public MutableBlockVector3 add(BlockVector3... others) {
for (BlockVector3 other : others) {
this.x += other.x();
this.y += other.y();
this.z += other.z();
}
return this;
}
@Override
public MutableBlockVector3 subtract(BlockVector3 other) {
return subtract(other.x(), other.y(), other.z());
}
@Override
public MutableBlockVector3 subtract(int x, int y, int z) {
this.x -= x;
this.y -= y;
this.z -= z;
return this;
}
@Override
public MutableBlockVector3 subtract(BlockVector3... others) {
for (BlockVector3 other : others) {
this.x -= other.x();
this.y -= other.y();
this.z -= other.z();
}
return this;
}
@Override
public MutableBlockVector3 multiply(BlockVector3 other) {
return multiply(other.x(), other.y(), other.z());
}
@Override
public MutableBlockVector3 multiply(int x, int y, int z) {
this.x *= x;
this.y *= y;
this.z *= z;
return this;
}
@Override
public MutableBlockVector3 multiply(BlockVector3... others) {
for (BlockVector3 other : others) {
this.x *= other.x();
this.y *= other.y();
this.z *= other.z();
}
return this;
}
@Override
public MutableBlockVector3 multiply(int n) {
return multiply(n, n, n);
}
@Override
public MutableBlockVector3 divide(BlockVector3 other) {
return divide(other.x(), other.y(), other.z());
}
@Override
public MutableBlockVector3 divide(int x, int y, int z) {
this.x /= x;
this.y /= y;
this.z /= z;
return this;
}
@Override
public MutableBlockVector3 divide(int n) {
return divide(n, n, n);
}
@Override
public MutableBlockVector3 shr(int x, int y, int z) {
this.x = this.x >> x;
this.y = this.y >> y;
this.z = this.z >> z;
return this;
}
@Override
public MutableBlockVector3 shr(int n) {
return shr(n, n, n);
}
@Override
public MutableBlockVector3 shl(int x, int y, int z) {
this.x = this.x >> x;
this.y = this.y >> y;
this.z = this.z >> z;
return this;
}
@Override
public MutableBlockVector3 shl(int n) {
return shl(n, n, n);
}
}

Datei anzeigen

@ -62,4 +62,13 @@ public interface Filter {
}
/**
* Signals to the filter the edit has concluded
*
* @since TODO
*/
default void finish() {
}
}

Datei anzeigen

@ -7,6 +7,7 @@ import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.OutputExtent;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BlockStateHolder;
@ -139,4 +140,19 @@ public interface IChunkSet extends IBlocks, OutputExtent {
return this;
}
/**
* Set the side effects to be used when settings these blocks
*
* @since TODO
*/
void setSideEffectSet(@Nonnull SideEffectSet sideEffectSet);
/**
* Get the side effects to be used when settings these blocks
*
* @since TODO
*/
@Nonnull
SideEffectSet getSideEffectSet();
}

Datei anzeigen

@ -38,6 +38,11 @@ public interface IDelegateFilter extends Filter {
return this;
}
@Override
default void finish() {
getParent().finish();
}
Filter newInstance(Filter other);
}

Datei anzeigen

@ -10,6 +10,7 @@ import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.SideEffectSet;
import javax.annotation.Nullable;
import java.io.Flushable;
@ -81,6 +82,20 @@ public interface IQueueExtent<T extends IChunk> extends Flushable, Trimable, ICh
boolean isFastMode();
/**
* Set the side effects to be used with this extent
*
* @since TODO
*/
void setSideEffectSet(SideEffectSet sideEffectSet);
/**
* Get the side effects to be used with this extent
*
* @since TODO
*/
SideEffectSet getSideEffectSet();
/**
* Create a new root IChunk object. Full chunks will be reused, so a more optimized chunk can be
* returned in that case.

Datei anzeigen

@ -20,11 +20,14 @@ import com.fastasyncworldedit.core.queue.Filter;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.mask.BlockMask;
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.RunContext;
import com.sk89q.worldedit.function.pattern.BlockPattern;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
@ -32,6 +35,7 @@ import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
@ -39,6 +43,7 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@ -58,11 +63,12 @@ public class ParallelQueueExtent extends PassthroughExtent {
// not very important)
private final boolean[] faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length];
private final boolean fastmode;
private final SideEffectSet sideEffectSet;
private int changes;
private int lastException = Integer.MIN_VALUE;
private int exceptionCount = 0;
public ParallelQueueExtent(QueueHandler handler, World world, boolean fastmode) {
public ParallelQueueExtent(QueueHandler handler, World world, boolean fastmode, @Nullable SideEffectSet sideEffectSet) {
super(handler.getQueue(world, new BatchProcessorHolder(), new BatchProcessorHolder()));
this.world = world;
this.handler = handler;
@ -75,6 +81,7 @@ public class ParallelQueueExtent extends PassthroughExtent {
((MultiBatchProcessor) this.postProcessor.getProcessor()).setFaweExceptionArray(faweExceptionReasonsUsed);
}
this.fastmode = fastmode;
this.sideEffectSet = sideEffectSet == null ? SideEffectSet.defaults() : sideEffectSet;
}
/**
@ -121,7 +128,12 @@ public class ParallelQueueExtent extends PassthroughExtent {
@SuppressWarnings("rawtypes")
private IQueueExtent<IQueueChunk> getNewQueue() {
return handler.getQueue(world, this.processor, this.postProcessor);
SingleThreadQueueExtent queue = (SingleThreadQueueExtent) handler.getQueue(world, this.processor, this.postProcessor);
queue.setFastMode(fastmode);
queue.setSideEffectSet(sideEffectSet);
queue.setFaweExceptionArray(faweExceptionReasonsUsed);
enter(queue);
return queue;
}
@Override
@ -140,6 +152,8 @@ public class ParallelQueueExtent extends PassthroughExtent {
BlockVector2 pos = chunksIter.next();
block = getExtent().apply(block, filter, region, pos.x(), pos.z(), full);
}
getExtent().flush();
filter.finish();
} else {
final ForkJoinTask[] tasks = IntStream.range(0, size).mapToObj(i -> handler.submit(() -> {
try {
@ -147,13 +161,9 @@ public class ParallelQueueExtent extends PassthroughExtent {
final Region newRegion = region.clone();
// Create a chunk that we will reuse/reset for each operation
final SingleThreadQueueExtent queue = (SingleThreadQueueExtent) getNewQueue();
queue.setFastMode(fastmode);
queue.setFaweExceptionArray(faweExceptionReasonsUsed);
enter(queue);
synchronized (queue) {
try {
ChunkFilterBlock block = null;
while (true) {
// Get the next chunk posWeakChunk
final int chunkX;
@ -169,6 +179,7 @@ public class ParallelQueueExtent extends PassthroughExtent {
block = queue.apply(block, newFilter, newRegion, chunkX, chunkZ, full);
}
queue.flush();
filter.finish();
} catch (Throwable t) {
if (t instanceof FaweException) {
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) t, LOGGER);
@ -205,6 +216,24 @@ public class ParallelQueueExtent extends PassthroughExtent {
return filter;
}
@Override
protected Operation commitBefore() {
return new Operation() {
@Override
public Operation resume(final RunContext run) throws WorldEditException {
extent.commit();
processor.flush();
((IQueueExtent<IQueueChunk<?>>) extent).flush();
return null;
}
@Override
public void cancel() {
}
};
}
@Override
public int countBlocks(Region region, Mask searchMask) {
return

Datei anzeigen

@ -27,6 +27,7 @@ import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.world.World;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import org.apache.logging.log4j.Logger;
@ -68,6 +69,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
private boolean[] faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length];
private int lastException = Integer.MIN_VALUE;
private int exceptionCount = 0;
private SideEffectSet sideEffectSet = SideEffectSet.defaults();
public SingleThreadQueueExtent() {
}
@ -110,6 +112,16 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
this.fastmode = fastmode;
}
@Override
public void setSideEffectSet(SideEffectSet sideEffectSet) {
this.sideEffectSet = sideEffectSet;
}
@Override
public SideEffectSet getSideEffectSet() {
return sideEffectSet;
}
@Override
public int getMinY() {
return minY;
@ -120,6 +132,10 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
return maxY;
}
public World getWorld() {
return world;
}
/**
* Sets the cached boolean array of length {@code FaweException.Type.values().length} that determines if a thrown
* {@link FaweException} of type {@link FaweException.Type} should be output to console, rethrown to attempt to be visible
@ -278,10 +294,16 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
private ChunkHolder poolOrCreate(int chunkX, int chunkZ) {
ChunkHolder next = create(false);
next.init(this, chunkX, chunkZ);
next.setFastMode(isFastMode());
return next;
}
@Override
public IQueueChunk wrap(IQueueChunk chunk) {
chunk.setFastMode(isFastMode());
chunk.setSideEffectSet(getSideEffectSet());
return chunk;
}
@Override
public final IQueueChunk getOrCreateChunk(int x, int z) {
getChunkLock.lock();

Datei anzeigen

@ -6,6 +6,7 @@ import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.util.collection.MemBlockSet;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
@ -212,6 +213,16 @@ public class BitSetBlocks implements IChunkSet {
return false;
}
@Override
public void setSideEffectSet(SideEffectSet sideEffectSet) {
}
@Override
public SideEffectSet getSideEffectSet() {
return SideEffectSet.none();
}
@Override
public int getSectionCount() {
return layers;

Datei anzeigen

@ -9,6 +9,7 @@ import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.Pool;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypesCache;
@ -44,6 +45,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
public EnumMap<HeightMapType, int[]> heightMaps;
private boolean fastMode = false;
private int bitMask = -1;
private SideEffectSet sideEffectSet = SideEffectSet.defaults();
private CharSetBlocks() {
// Expand as we go
@ -374,10 +376,21 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
heightMaps != null ? new EnumMap<>(heightMaps) : null,
defaultOrdinal(),
fastMode,
bitMask
bitMask,
sideEffectSet
);
}
@Override
public void setSideEffectSet(SideEffectSet sideEffectSet) {
this.sideEffectSet = sideEffectSet;
}
@Override
public SideEffectSet getSideEffectSet() {
return sideEffectSet;
}
static char[][] createLightCopy(char[][] lightArr, int sectionCount) {
if (lightArr == null) {
return null;

Datei anzeigen

@ -9,6 +9,7 @@ import com.fastasyncworldedit.core.queue.IBlocks;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
@ -51,6 +52,7 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks {
private Map<HeightMapType, int[]> heightMaps;
private boolean fastMode;
private int bitMask;
private SideEffectSet sideEffectSet;
/**
* New instance given the data stored in a {@link CharSetBlocks} instance.
@ -71,7 +73,8 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks {
Map<HeightMapType, int[]> heightMaps,
char defaultOrdinal,
boolean fastMode,
int bitMask
int bitMask,
SideEffectSet sideEffectSet
) {
this.blocks = blocks;
this.minSectionPosition = minSectionPosition;
@ -87,6 +90,7 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks {
this.defaultOrdinal = defaultOrdinal;
this.fastMode = fastMode;
this.bitMask = bitMask;
this.sideEffectSet = sideEffectSet;
}
@Override
@ -480,10 +484,21 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks {
heightMaps != null ? new HashMap<>(heightMaps) : null,
defaultOrdinal,
fastMode,
bitMask
bitMask,
sideEffectSet
);
}
@Override
public void setSideEffectSet(SideEffectSet sideEffectSet) {
this.sideEffectSet = sideEffectSet;
}
@Override
public SideEffectSet getSideEffectSet() {
return sideEffectSet;
}
@Override
public boolean trim(boolean aggressive) {
return false;

Datei anzeigen

@ -15,6 +15,7 @@ import com.fastasyncworldedit.core.util.MemUtil;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
@ -51,6 +52,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
private boolean isInit = false; // Lighting handles queue differently. It relies on the chunk cache and not doing init.
private boolean createCopy = false;
private long initTime = -1L;
private SideEffectSet sideEffectSet;
private ChunkHolder() {
this.delegate = NULL;
@ -150,6 +152,16 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
return chunkSet != null && chunkSet.hasBiomes(layer);
}
@Override
public void setSideEffectSet(SideEffectSet sideEffectSet) {
this.sideEffectSet = sideEffectSet;
}
@Override
public SideEffectSet getSideEffectSet() {
return sideEffectSet;
}
public boolean isInit() {
return isInit;
}
@ -874,7 +886,6 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
public synchronized void filterBlocks(Filter filter, ChunkFilterBlock block, @Nullable Region region, boolean full) {
final IChunkGet get = getOrCreateGet();
final IChunkSet set = getOrCreateSet();
set.setFastMode(fastmode);
try {
block.filter(this, get, set, filter, region, full);
} finally {
@ -948,13 +959,21 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
return chunkSet;
}
public final IChunkSet getChunkSet() {
return chunkSet;
}
/**
* Create a wrapped set object
* - The purpose of wrapping is to allow different extents to intercept / alter behavior
* - e.g., caching, optimizations, filtering
*/
private IChunkSet newWrappedSet() {
return extent.getCachedSet(chunkX, chunkZ);
IChunkSet set = extent.getCachedSet(chunkX, chunkZ);
set.setFastMode(fastmode);
set.setSideEffectSet(sideEffectSet);
set.setBitMask(bitMask);
return set;
}
/**
@ -985,7 +1004,6 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
@Override
public synchronized T call() {
if (chunkSet != null && !chunkSet.isEmpty()) {
chunkSet.setBitMask(bitMask);
IChunkSet copy = chunkSet.createCopy();
return this.call(copy, () -> {
// Do nothing

Datei anzeigen

@ -8,6 +8,7 @@ import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
@ -86,6 +87,16 @@ public final class NullChunk implements IQueueChunk {
return false;
}
@Override
public void setSideEffectSet(SideEffectSet sideEffectSet) {
}
@Override
public SideEffectSet getSideEffectSet() {
return SideEffectSet.none();
}
@Nonnull
public int[] getHeightMap(@Nullable HeightMapType type) {
return new int[256];

Datei anzeigen

@ -1,7 +1,11 @@
package com.fastasyncworldedit.core.util;
import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent;
import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.World;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -21,6 +25,26 @@ public class ExtentTraverser<T extends Extent> {
this.parent = parent;
}
/**
* Get the world backing the given extent, if present, else null.
*
* @since TODO
*/
@Nullable
public static World getWorldFromExtent(Extent extent) {
if (extent.isWorld()) {
return (World) extent;
} else if (extent instanceof EditSession session) {
return session.getWorld();
} else if (extent instanceof SingleThreadQueueExtent stqe) {
return stqe.getWorld();
} else if (extent instanceof ParallelQueueExtent pqe) {
return ((SingleThreadQueueExtent) pqe.getExtent()).getWorld();
} else {
return new ExtentTraverser<>(extent).findAndGet(World.class);
}
}
public boolean exists() {
return root != null;
}

Datei anzeigen

@ -64,7 +64,10 @@ import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionIntersection;
import com.sk89q.worldedit.util.Identifiable;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.eventbus.EventBus;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
@ -104,6 +107,7 @@ public final class EditSessionBuilder {
private Extent extent;
private boolean compiled;
private boolean wrapped;
private SideEffectSet sideEffectSet = null;
private @Nullable
World world;
@ -415,6 +419,16 @@ public final class EditSessionBuilder {
return setDirty();
}
/**
* Set the side effects to be used with this edit
*
* @since TODO
*/
public EditSessionBuilder setSideEffectSet(@Nullable SideEffectSet sideEffectSet) {
this.sideEffectSet = sideEffectSet;
return setDirty();
}
/**
* Compile the builder to the settings given. Prepares history, limits, lighting, etc.
*/
@ -445,6 +459,10 @@ public final class EditSessionBuilder {
fastMode = actor.getSession().hasFastMode();
}
}
if (sideEffectSet == null) {
// Keep heightmaps to maintain behaviour
sideEffectSet = fastMode ? SideEffectSet.none().with(SideEffect.HEIGHTMAPS) : SideEffectSet.defaults();
}
if (checkMemory == null) {
checkMemory = actor != null && !this.fastMode;
}
@ -470,12 +488,18 @@ public final class EditSessionBuilder {
if (unwrapped instanceof IQueueExtent) {
extent = queue = (IQueueExtent) unwrapped;
} else if (Settings.settings().QUEUE.PARALLEL_THREADS > 1 && !Fawe.isMainThread()) {
ParallelQueueExtent parallel = new ParallelQueueExtent(Fawe.instance().getQueueHandler(), world, fastMode);
ParallelQueueExtent parallel = new ParallelQueueExtent(
Fawe.instance().getQueueHandler(),
world,
fastMode,
sideEffectSet
);
queue = parallel.getExtent();
extent = parallel;
} else {
extent = queue = Fawe.instance().getQueueHandler().getQueue(world);
}
queue.setSideEffectSet(sideEffectSet);
} else {
wnaMode = true;
extent = world;
@ -491,7 +515,7 @@ public final class EditSessionBuilder {
}
extent = this.bypassAll = wrapExtent(extent, eventBus, event, EditSession.Stage.BEFORE_CHANGE);
this.bypassHistory = this.extent = wrapExtent(bypassAll, eventBus, event, EditSession.Stage.BEFORE_REORDER);
if (!this.fastMode || changeSet != null) {
if (!this.fastMode || this.sideEffectSet.shouldApply(SideEffect.HISTORY) || changeSet != null) {
if (changeSet == null) {
if (Settings.settings().HISTORY.USE_DISK) {
UUID uuid = actor == null ? Identifiable.CONSOLE : actor.getUniqueId();
@ -546,13 +570,28 @@ public final class EditSessionBuilder {
}
// 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()
if (this.sideEffectSet.shouldApply(SideEffect.LIGHTING) || (relightMode != null && relightMode != RelightMode.NONE)) {
relighter = WorldEdit
.getInstance()
.getPlatformManager()
.queryCapability(Capability.WORLD_EDITING)
.getRelighterFactory().createRelighter(relightMode, world, queue);
.getRelighterFactory()
.createRelighter(relightMode, world, queue);
queue.addProcessor(new RelightProcessor(relighter));
}
queue.addProcessor(new HeightmapProcessor(world.getMinY(), world.getMaxY()));
if (this.sideEffectSet.shouldApply(SideEffect.HEIGHTMAPS)) {
queue.addProcessor(new HeightmapProcessor(world.getMinY(), world.getMaxY()));
}
if (this.sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) {
Region region = allowedRegions == null || allowedRegions.length == 0
? null
: allowedRegions.length == 1 ? allowedRegions[0] : new RegionIntersection(allowedRegions);
queue.addProcessor(WorldEdit
.getInstance()
.getPlatformManager()
.queryCapability(Capability.WORLD_EDITING)
.getPlatformPlacementProcessor(extent, null, region));
}
if (!Settings.settings().EXPERIMENTAL.KEEP_ENTITIES_IN_BLOCKS) {
queue.addProcessor(new EntityInBlockRemovingProcessor());
@ -714,6 +753,15 @@ public final class EditSessionBuilder {
return changeSet;
}
/**
* Get the SideEffectSet that will be used
*
* @since TODO
*/
public SideEffectSet getSideEffectSet() {
return sideEffectSet;
}
/**
* Get the ultimate resultant extent
*/

Datei anzeigen

@ -154,6 +154,7 @@ public class LocalSession implements TextureHolder {
private transient TextureUtil texture;
private transient ResettableExtent transform = null;
private transient World currentWorld;
private transient boolean fastMode = false;
//FAWE end
private transient ClipboardHolder clipboard;
private transient final Object clipboardLock = new Object();
@ -1725,11 +1726,12 @@ public class LocalSession implements TextureHolder {
* @return an edit session
*/
public EditSession createEditSession(Actor actor) {
//FAWE start
//FAWE start - save command used
return createEditSession(actor, null);
}
public EditSession createEditSession(Actor actor, String command) {
//FAWE end
checkNotNull(actor);
World world = null;
@ -1740,17 +1742,18 @@ public class LocalSession implements TextureHolder {
}
// Create an edit session
EditSession editSession;
EditSessionBuilder builder = WorldEdit.getInstance().newEditSessionBuilder().world(world);
if (actor.isPlayer() && actor instanceof Player) {
BlockBag blockBag = getBlockBag((Player) actor);
builder.actor(actor);
builder.blockBag(blockBag);
}
//FAWE start
builder.command(command);
builder.fastMode(!this.sideEffectSet.doesApplyAny());
builder.fastMode(this.fastMode);
builder.setSideEffectSet(this.sideEffectSet);
editSession = builder.build();
EditSession editSession = builder.build();
if (mask != null) {
editSession.setMask(mask);
@ -1762,7 +1765,7 @@ public class LocalSession implements TextureHolder {
editSession.addTransform(transform);
}
editSession.setTickingWatchdog(tickingWatchdog);
//FAWE end
return editSession;
}
//FAWE end
@ -1801,7 +1804,9 @@ public class LocalSession implements TextureHolder {
*/
@Deprecated
public boolean hasFastMode() {
return !this.sideEffectSet.doesApplyAny();
//FAWE start - use fastmode boolean not side effects
return this.fastMode;
//FAWE end
}
/**
@ -1811,7 +1816,9 @@ public class LocalSession implements TextureHolder {
*/
@Deprecated
public void setFastMode(boolean fastMode) {
this.sideEffectSet = fastMode ? SideEffectSet.none() : SideEffectSet.defaults();
//FAWE start - use fastmode boolean not side effects
this.fastMode = fastMode;
//FAWE end
}
/**

Datei anzeigen

@ -29,6 +29,7 @@ import com.fastasyncworldedit.core.util.MaskTraverser;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.command.util.CommandPermissions;
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
@ -38,6 +39,7 @@ import com.sk89q.worldedit.command.util.annotation.Preload;
import com.sk89q.worldedit.command.util.annotation.SynchronousSettingExpected;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.function.GroundFunction;
import com.sk89q.worldedit.function.generator.FloraGenerator;
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
@ -915,4 +917,33 @@ public class RegionCommands {
return affected;
}
//FAWE start - block "fixing"
@Command(
name = "/fixblocks",
aliases = {"/updateblocks", "/fixconnect"},
desc = "\"Fixes\" all blocks in the region to the correct shape/connections based on surrounding blocks"
)
@CommandPermissions("worldedit.region.fixblocks")
@Logging(REGION)
@Confirm(Confirm.Processor.REGION)
@Preload(Preload.PreloadCheck.PRELOAD)
public int fixblocks(
Actor actor, EditSession editSession, @Selection Region region
) {
int affected = editSession.setBlocks(
region,
WorldEdit
.getInstance()
.getPlatformManager()
.queryCapability(Capability.WORLD_EDITING)
.getPlatformPlacementProcessor(editSession, null, region)
);
if (affected != 0) {
actor.print(Caption.of("worldedit.set.done", TextComponent.of(affected)));
}
return affected;
}
//FAWE end
}

Datei anzeigen

@ -19,13 +19,16 @@
package com.sk89q.worldedit.extension.platform;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
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;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.internal.util.NonAbstractForCompatibility;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.registry.Keyed;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.io.ResourceLoader;
@ -276,5 +279,15 @@ public interface Platform extends Keyed {
default IBatchProcessor getPlatformPostProcessor(boolean fastMode) {
return null;
}
/**
* Returns an {@link PlacementStateProcessor} instance for processing placed blocks to "fix" them. Optional region to
* prevent any changes outside of, as sometimes block neighbours will also be updated otherwise.
*
* @since TODO
*/
default PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, @Nullable Region region) {
return null;
}
//FAWE end
}

Datei anzeigen

@ -22,6 +22,7 @@ package com.sk89q.worldedit.function.mask;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockCategory;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import javax.annotation.Nullable;
@ -54,6 +55,15 @@ public class BlockCategoryMask extends AbstractExtentMask {
public boolean test(Extent extent, BlockVector3 vector) {
return category.contains(extent.getBlock(vector));
}
/**
* Test a specific block against this category mask
*
* @since TODO
*/
public <B extends BlockStateHolder<B>> boolean test(B blockStateHolder) {
return category.contains(blockStateHolder);
}
//FAWE end
@Nullable

Datei anzeigen

@ -21,6 +21,7 @@ package com.sk89q.worldedit.function.mask;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
@ -140,6 +141,15 @@ public class BlockTypeMask extends AbstractExtentMask {
public boolean test(BlockType block) {
return types[block.getInternalId()];
}
/**
* Test a block state against this block type mask
*
* @since TODO
*/
public <B extends BlockStateHolder<B>> boolean test(B blockStateHolder) {
return types[blockStateHolder.getBlockType().getInternalId()];
}
//FAWE end
@Nullable
@ -149,7 +159,7 @@ public class BlockTypeMask extends AbstractExtentMask {
}
@Override
public Mask copy() {
public BlockTypeMask copy() {
return new BlockTypeMask(getExtent(), types.clone(), hasAir);
}

Datei anzeigen

@ -127,6 +127,10 @@ public abstract class BlockVector3 {
}
//FAWE start
public boolean isAt(int x, int y, int z) {
return x() == x && y() == y && z() == z;
}
public MutableBlockVector3 setComponents(double x, double y, double z) {
return new MutableBlockVector3((int) x, (int) y, (int) z);
}

Datei anzeigen

@ -21,9 +21,13 @@ package com.sk89q.worldedit.util;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.registry.state.DirectionalProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.block.BlockState;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@ -36,32 +40,32 @@ import java.util.OptionalInt;
public enum Direction {
//FAWE start - left, right
NORTH(Vector3.at(0, 0, -1), Flag.CARDINAL, 3, 1),
EAST(Vector3.at(1, 0, 0), Flag.CARDINAL, 0, 2),
SOUTH(Vector3.at(0, 0, 1), Flag.CARDINAL, 1, 3),
WEST(Vector3.at(-1, 0, 0), Flag.CARDINAL, 2, 0),
NORTH(Vector3.at(0, 0, -1), Flag.CARDINAL, 3, 1), // 0
EAST(Vector3.at(1, 0, 0), Flag.CARDINAL, 0, 2), // 1
SOUTH(Vector3.at(0, 0, 1), Flag.CARDINAL, 1, 3), // 2
WEST(Vector3.at(-1, 0, 0), Flag.CARDINAL, 2, 0), // 3
UP(Vector3.at(0, 1, 0), Flag.UPRIGHT, -1, -1),
DOWN(Vector3.at(0, -1, 0), Flag.UPRIGHT, -1, -1),
UP(Vector3.at(0, 1, 0), Flag.UPRIGHT, -1, -1), // 4
DOWN(Vector3.at(0, -1, 0), Flag.UPRIGHT, -1, -1), // 5
NORTHEAST(Vector3.at(1, 0, -1), Flag.ORDINAL, 7, 8),
NORTHWEST(Vector3.at(-1, 0, -1), Flag.ORDINAL, 9, 6),
SOUTHEAST(Vector3.at(1, 0, 1), Flag.ORDINAL, 6, 9),
SOUTHWEST(Vector3.at(-1, 0, 1), Flag.ORDINAL, 8, 7),
NORTHEAST(Vector3.at(1, 0, -1), Flag.ORDINAL, 7, 8), // 6
NORTHWEST(Vector3.at(-1, 0, -1), Flag.ORDINAL, 9, 6), // 7
SOUTHEAST(Vector3.at(1, 0, 1), Flag.ORDINAL, 6, 9), // 8
SOUTHWEST(Vector3.at(-1, 0, 1), Flag.ORDINAL, 8, 7), // 9
WEST_NORTHWEST(Vector3.at(-Math.cos(Math.PI / 8), 0, -Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 9, 6),
WEST_SOUTHWEST(Vector3.at(-Math.cos(Math.PI / 8), 0, Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 8, 7),
NORTH_NORTHWEST(Vector3.at(-Math.sin(Math.PI / 8), 0, -Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 9, 6),
NORTH_NORTHEAST(Vector3.at(Math.sin(Math.PI / 8), 0, -Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 7, 8),
EAST_NORTHEAST(Vector3.at(Math.cos(Math.PI / 8), 0, -Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 7, 8),
EAST_SOUTHEAST(Vector3.at(Math.cos(Math.PI / 8), 0, Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 6, 9),
SOUTH_SOUTHEAST(Vector3.at(Math.sin(Math.PI / 8), 0, Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 6, 9),
SOUTH_SOUTHWEST(Vector3.at(-Math.sin(Math.PI / 8), 0, Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 8, 7),
WEST_NORTHWEST(Vector3.at(-Math.cos(Math.PI / 8), 0, -Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 18, 14), // 11
WEST_SOUTHWEST(Vector3.at(-Math.cos(Math.PI / 8), 0, Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 17, 13), // 12
NORTH_NORTHWEST(Vector3.at(-Math.sin(Math.PI / 8), 0, -Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 12, 15), // 13
NORTH_NORTHEAST(Vector3.at(Math.sin(Math.PI / 8), 0, -Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 11, 16), // 14
EAST_NORTHEAST(Vector3.at(Math.cos(Math.PI / 8), 0, -Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 13, 17), // 15
EAST_SOUTHEAST(Vector3.at(Math.cos(Math.PI / 8), 0, Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 14, 18), // 16
SOUTH_SOUTHEAST(Vector3.at(Math.sin(Math.PI / 8), 0, Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 15, 12), // 17
SOUTH_SOUTHWEST(Vector3.at(-Math.sin(Math.PI / 8), 0, Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL, 16, 11), // 18
ASCENDING_NORTH(Vector3.at(0, 1, -1), Flag.ASCENDING_CARDINAL, 3 + 18, 1 + 18),
ASCENDING_EAST(Vector3.at(1, 1, 0), Flag.ASCENDING_CARDINAL, 0 + 18, 2 + 18),
ASCENDING_SOUTH(Vector3.at(0, 1, 1), Flag.ASCENDING_CARDINAL, 1 + 18, 3 + 18),
ASCENDING_WEST(Vector3.at(-1, 1, 0), Flag.ASCENDING_CARDINAL, 2 + 18, 0 + 18),
ASCENDING_NORTH(Vector3.at(0, 1, -1), Flag.ASCENDING_CARDINAL, 3 + 18, 1 + 18), // 19
ASCENDING_EAST(Vector3.at(1, 1, 0), Flag.ASCENDING_CARDINAL, 0 + 18, 2 + 18), // 20
ASCENDING_SOUTH(Vector3.at(0, 1, 1), Flag.ASCENDING_CARDINAL, 1 + 18, 3 + 18), // 21
ASCENDING_WEST(Vector3.at(-1, 1, 0), Flag.ASCENDING_CARDINAL, 2 + 18, 0 + 18), // 22
;
//FAWE end
@ -95,12 +99,18 @@ public enum Direction {
return map.get(sequence);
}
/**
* Get the direction 90 degrees left (anti-clockwise) of this direction if possible, else return this direction
*/
public Direction getLeft() {
return left != -1 ? values()[left] : null;
return left != -1 ? values()[left] : this;
}
/**
* Get the direction 90 degrees right (clockwise) of this direction if possible, else return this direction
*/
public Direction getRight() {
return right != -1 ? values()[right] : null;
return right != -1 ? values()[right] : this;
}
public double getX() {
@ -346,5 +356,46 @@ public enum Direction {
}
}
//FAWE start - utility methods for block states
/**
* Get the directions associated with the given block state, e.g. the connections a fence makes or the direction stairs face
*
* @since TODO
*/
public static EnumSet<Direction> getDirections(BlockState state) {
EnumSet<Direction> directions = EnumSet.noneOf(Direction.class);
for (Property<?> property : state.getBlockType().getProperties()) {
if (property instanceof DirectionalProperty dirProp) {
directions.add(state.getState(dirProp));
continue;
}
Object value = state.getState(property);
if (!(value instanceof String str)) {
if (value instanceof Integer i) {
fromRotationIndex(i).ifPresent(directions::add);
} else if (value instanceof Boolean b && b) {
try {
directions.add(Direction.valueOf(property.getName().toUpperCase(Locale.ROOT)));
} catch (IllegalArgumentException ignored) {
}
}
continue;
}
switch (str.toLowerCase(Locale.ROOT)) {
case "upper", "ceiling", "up", "top" -> directions.add(Direction.UP);
case "lower", "floor", "down", "bottom", "y" -> directions.add(Direction.DOWN);
case "double", "wall" -> {} // Do nothing
case "south" -> directions.add(Direction.SOUTH);
case "x", "east" -> directions.add(Direction.EAST);
case "z", "north" -> directions.add(Direction.NORTH);
case "west" -> directions.add(Direction.WEST);
case "hinge" -> {} // Do nothing for now
case "shape" -> {} // Do nothing for now
}
}
return directions;
}
//FAWE end
}

Datei anzeigen

@ -19,23 +19,30 @@
package com.sk89q.worldedit.util;
import com.fastasyncworldedit.core.configuration.Settings;
import java.util.Locale;
public enum SideEffect {
LIGHTING(State.ON, true),
NEIGHBORS(State.ON, true),
UPDATE(State.ON, true),
//FAWE start - adjust defaults, add history and heightmaps
HISTORY(State.ON, true),
HEIGHTMAPS(State.ON, true),
LIGHTING(Settings.settings().LIGHTING.MODE == 0 ? State.OFF : State.ON, true),
NEIGHBORS(State.OFF, true),
UPDATE(State.OFF, true),
//FAWE end
VALIDATION(State.OFF, true),
ENTITY_AI(State.OFF, true),
EVENTS(State.OFF, true),
/**
* Internal use only.
*/
POI_UPDATE(State.ON, false),
POI_UPDATE(State.OFF, false),
/**
* Internal use only.
*/
NETWORK(State.ON, false);
NETWORK(State.OFF, false);
//FAWE end
private final String displayName;
private final String description;

Datei anzeigen

@ -19,7 +19,6 @@
package com.sk89q.worldedit.util;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.util.Arrays;
@ -31,7 +30,12 @@ import java.util.stream.Collectors;
public class SideEffectSet {
private static final SideEffectSet DEFAULT = new SideEffectSet();
//FAWE start - assign value map
private static final SideEffectSet DEFAULT = new SideEffectSet(
Arrays.stream(SideEffect.values())
.filter(SideEffect::isExposed)
.collect(Collectors.toMap(Function.identity(), SideEffect::getDefaultValue)));
//FAWE end
private static final SideEffectSet NONE = new SideEffectSet(
Arrays.stream(SideEffect.values())
.filter(SideEffect::isExposed)
@ -42,12 +46,6 @@ public class SideEffectSet {
private final Set<SideEffect> appliedSideEffects;
private final boolean appliesAny;
//FAWE start
private SideEffectSet() {
this(ImmutableMap.of());
}
//FAWE end
public SideEffectSet(Map<SideEffect, SideEffect.State> sideEffects) {
this.sideEffects = Maps.immutableEnumMap(sideEffects);
@ -61,6 +59,27 @@ public class SideEffectSet {
//FAWE end
}
//FAWE start - simple overload method for setting side effects
/**
* Create a new {@link SideEffectSet} with the given side effect set to "on"
*
* @since TODO
*/
public SideEffectSet with(SideEffect sideEffect) {
return with(sideEffect, SideEffect.State.ON);
}
/**
* Create a new {@link SideEffectSet} with the given side effect set to "off"
*
* @since TODO
*/
public SideEffectSet without(SideEffect sideEffect) {
return with(sideEffect, SideEffect.State.OFF);
}
//FAWE end
public SideEffectSet with(SideEffect sideEffect, SideEffect.State state) {
Map<SideEffect, SideEffect.State> entries = this.sideEffects.isEmpty()
? Maps.newEnumMap(SideEffect.class)
@ -103,4 +122,23 @@ public class SideEffectSet {
return NONE;
}
//FAWE start
/**
* API-friendly side effect set.
* Sets:
* - Heightmaps
* - Lighting (if set to mode 1 or 2 in config)
* Does not set:
* - History
* - Neighbours
* - Lighting (if set to mode 0 in config
*
* @since TODO
*/
public static SideEffectSet api() {
return defaults().without(SideEffect.HISTORY);
}
//FAWE end
}

Datei anzeigen

@ -627,10 +627,14 @@
"worldedit.selection.polygon2d.explain.secondary": "Added point #{0} at {1}.",
"worldedit.selection.sphere.explain.secondary": "Radius set to {0}.",
"worldedit.selection.sphere.explain.secondary-defined": "Radius set to {0} ({1}).",
"worldedit.sideeffect.history": "History",
"worldedit.sideeffect.history.description": "Writes history of the change",
"worldedit.sideeffect.heightmaps": "Heightmaps",
"worldedit.sideeffect.heightmaps.description": "Updates heightmaps",
"worldedit.sideeffect.lighting": "Lighting",
"worldedit.sideeffect.lighting.description": "Updates block lighting",
"worldedit.sideeffect.neighbors": "Neighbors",
"worldedit.sideeffect.neighbors.description": "Notifies nearby blocks of changes",
"worldedit.sideeffect.neighbors.description": "Updates shapes of blocks in the edit",
"worldedit.sideeffect.update": "Update",
"worldedit.sideeffect.update.description": "Notifies the changed block",
"worldedit.sideeffect.validation": "Validation",