3
0
Mirror von https://github.com/IntellectualSites/FastAsyncWorldEdit.git synchronisiert 2024-11-19 17:30:08 +01:00

feat: implement command to fix block connections

- closes #313
Dieser Commit ist enthalten in:
dordsor21 2024-05-26 17:29:36 +01:00
Ursprung ba8b4e4ee8
Commit c51449dc49
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 1E53E88969FFCF0B
27 geänderte Dateien mit 2897 neuen und 29 gelöschten Zeilen

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

@ -0,0 +1,137 @@
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 FaweMutableBlockPlaceContext extends BlockPlaceContext {
private static final BlockHitResult DEFAULT_BLOCK_HIT = new BlockHitResult(Vec3.ZERO, Direction.NORTH, BlockPos.ZERO, false);
private final ServerLevel level;
private BlockHitResult hitResult = null;
private Direction direction = null;
private BlockPos relativePos;
@SuppressWarnings("DataFlowIssue")
public FaweMutableBlockPlaceContext(ServerLevel level) {
super(
level,
null,
null,
null,
DEFAULT_BLOCK_HIT
);
this.level = level;
this.replaceClicked = false;
}
public FaweMutableBlockPlaceContext 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

@ -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;
@ -21,6 +22,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;
@ -442,6 +444,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(ordinalToIbdID[blockState.getOrdinal()]);
}
@Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = getServerLevel(world);
@ -610,6 +616,11 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return new PaperweightPostProcessor();
}
@Override
public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, boolean includeUnedited) {
return new PaperweightPlacementStateProcessor(extent, mask, includeUnedited);
}
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
try {

Datei anzeigen

@ -0,0 +1,103 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extent.Extent;
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.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import sun.misc.Unsafe;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightLevelProxy extends ServerLevel {
private PaperweightFaweAdapter adapter;
private Extent extent;
private ServerLevel serverLevel;
private boolean enabled = false;
@SuppressWarnings("DataFlowIssue")
public 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 level, Extent extent) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PaperweightLevelProxy newLevel;
try {
newLevel = (PaperweightLevelProxy) unsafe.allocateInstance(PaperweightLevelProxy.class);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
newLevel.adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
newLevel.extent = extent;
newLevel.serverLevel = level;
return newLevel;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Nullable
@Override
public BlockEntity getBlockEntity(@Nonnull BlockPos blockPos) {
if (!enabled) {
return null;
}
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.extent.getFullBlock(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
).getNbtReference().getValue()));
return newEntity;
}
@Override
@Nonnull
public BlockState getBlockState(@Nonnull BlockPos blockPos) {
if (!enabled) {
return Blocks.AIR.defaultBlockState();
}
com.sk89q.worldedit.world.block.BlockState state = this.extent.getBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ());
return adapter.adapt(state);
}
@SuppressWarnings("unused")
@Override
@Nonnull
public FluidState getFluidState(@Nonnull BlockPos pos) {
if (!enabled) {
return Fluids.EMPTY.defaultFluidState();
}
return getBlockState(pos).getFluidState();
}
@SuppressWarnings("unused")
@Override
public boolean isWaterAt(@Nonnull BlockPos pos) {
if (!enabled) {
return false;
}
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
}

Datei anzeigen

@ -0,0 +1,96 @@
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.EditSession;
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.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;
public class PaperweightPlacementStateProcessor extends PlacementStateProcessor {
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
private final FaweMutableBlockPlaceContext mutableBlockPlaceContext;
public PaperweightPlacementStateProcessor(
final Extent extent,
final BlockTypeMask mask,
final boolean includeUnedited
) {
super(extent, mask, includeUnedited);
World world;
if (extent.isWorld()) {
world = (World) extent;
} else if (extent instanceof EditSession session) {
world = session.getWorld();
} else if ((world = new ExtentTraverser<>(extent).findAndGet(BukkitWorld.class)) == null) {
throw new UnsupportedOperationException("Cannot find world of extent.");
}
BukkitWorld bukkitWorld;
if (world instanceof WorldWrapper wrapper) {
bukkitWorld = (BukkitWorld) wrapper.getParent();
} else {
bukkitWorld = (BukkitWorld) world;
}
PaperweightLevelProxy proxyLevel = PaperweightLevelProxy.getInstance(
((CraftWorld) bukkitWorld.getWorld()).getHandle(),
extent
);
mutableBlockPlaceContext = new FaweMutableBlockPlaceContext(proxyLevel);
proxyLevel.setEnabled(true);
}
@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, includeUnedited);
}
@Override
public PlacementStateProcessor fork() {
return new PaperweightPlacementStateProcessor(extent, mask, includeUnedited);
}
}

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

@ -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 FaweMutableBlockPlaceContext extends BlockPlaceContext {
private static final BlockHitResult DEFAULT_BLOCK_HIT = new BlockHitResult(Vec3.ZERO, Direction.NORTH, BlockPos.ZERO, false);
private final ServerLevel level;
private BlockHitResult hitResult = null;
private Direction direction = null;
private BlockPos relativePos;
@SuppressWarnings("DataFlowIssue")
public FaweMutableBlockPlaceContext(ServerLevel level) {
super(
level,
null,
null,
null,
DEFAULT_BLOCK_HIT
);
this.level = level;
this.replaceClicked = false;
}
public FaweMutableBlockPlaceContext 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

@ -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;
@ -21,7 +22,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;
@ -442,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(ordinalToIbdID[blockState.getOrdinal()]);
}
@Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = getServerLevel(world);
@ -479,8 +484,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(ordinalToIbdID[blockState.getOrdinal()]);
return blockState1.hasPostProcess(
getServerLevel(world),
new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z())
@ -610,6 +614,11 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return new PaperweightPostProcessor();
}
@Override
public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, boolean includeUnedited) {
return new PaperweightPlacementStateProcessor(extent, mask, includeUnedited);
}
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
try {

Datei anzeigen

@ -0,0 +1,103 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extent.Extent;
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.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import sun.misc.Unsafe;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightLevelProxy extends ServerLevel {
private PaperweightFaweAdapter adapter;
private Extent extent;
private ServerLevel serverLevel;
private boolean enabled = false;
@SuppressWarnings("DataFlowIssue")
public 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 level, Extent extent) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PaperweightLevelProxy newLevel;
try {
newLevel = (PaperweightLevelProxy) unsafe.allocateInstance(PaperweightLevelProxy.class);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
newLevel.adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
newLevel.extent = extent;
newLevel.serverLevel = level;
return newLevel;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Nullable
@Override
public BlockEntity getBlockEntity(@Nonnull BlockPos blockPos) {
if (!enabled) {
return null;
}
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.extent.getFullBlock(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
).getNbtReference().getValue()));
return newEntity;
}
@Override
@Nonnull
public BlockState getBlockState(@Nonnull BlockPos blockPos) {
if (!enabled) {
return Blocks.AIR.defaultBlockState();
}
com.sk89q.worldedit.world.block.BlockState state = this.extent.getBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ());
return adapter.adapt(state);
}
@SuppressWarnings("unused")
@Override
@Nonnull
public FluidState getFluidState(@Nonnull BlockPos pos) {
if (!enabled) {
return Fluids.EMPTY.defaultFluidState();
}
return getBlockState(pos).getFluidState();
}
@SuppressWarnings("unused")
@Override
public boolean isWaterAt(@Nonnull BlockPos pos) {
if (!enabled) {
return false;
}
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
}

Datei anzeigen

@ -0,0 +1,96 @@
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.EditSession;
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.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;
public class PaperweightPlacementStateProcessor extends PlacementStateProcessor {
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
private final FaweMutableBlockPlaceContext mutableBlockPlaceContext;
public PaperweightPlacementStateProcessor(
final Extent extent,
final BlockTypeMask mask,
final boolean includeUnedited
) {
super(extent, mask, includeUnedited);
World world;
if (extent.isWorld()) {
world = (World) extent;
} else if (extent instanceof EditSession session) {
world = session.getWorld();
} else if ((world = new ExtentTraverser<>(extent).findAndGet(BukkitWorld.class)) == null) {
throw new UnsupportedOperationException("Cannot find world of extent.");
}
BukkitWorld bukkitWorld;
if (world instanceof WorldWrapper wrapper) {
bukkitWorld = (BukkitWorld) wrapper.getParent();
} else {
bukkitWorld = (BukkitWorld) world;
}
PaperweightLevelProxy proxyLevel = PaperweightLevelProxy.getInstance(
((CraftWorld) bukkitWorld.getWorld()).getHandle(),
extent
);
mutableBlockPlaceContext = new FaweMutableBlockPlaceContext(proxyLevel);
proxyLevel.setEnabled(true);
}
@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, includeUnedited);
}
@Override
public PlacementStateProcessor fork() {
return new PaperweightPlacementStateProcessor(extent, mask, includeUnedited);
}
}

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

@ -0,0 +1,138 @@
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 FaweMutableBlockPlaceContext extends BlockPlaceContext {
private static final BlockHitResult DEFAULT_BLOCK_HIT = new BlockHitResult(Vec3.ZERO, Direction.NORTH, BlockPos.ZERO, false);
private final ServerLevel level;
private BlockHitResult hitResult = null;
private Direction direction = null;
private BlockPos relativePos;
@SuppressWarnings("DataFlowIssue")
public FaweMutableBlockPlaceContext(ServerLevel level) {
super(
level,
null,
null,
null,
DEFAULT_BLOCK_HIT
);
this.level = level;
this.replaceClicked = false;
}
public FaweMutableBlockPlaceContext 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

@ -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;
@ -22,6 +23,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;
@ -451,6 +453,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(ordinalToIbdID[blockState.getOrdinal()]);
}
@Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = getServerLevel(world);
@ -632,6 +638,11 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return new PaperweightPostProcessor();
}
@Override
public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, boolean includeUnedited) {
return new PaperweightPlacementStateProcessor(extent, mask, includeUnedited);
}
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
try {

Datei anzeigen

@ -0,0 +1,110 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extent.Extent;
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.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import sun.misc.Unsafe;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightLevelProxy extends ServerLevel {
private PaperweightFaweAdapter adapter;
private Extent extent;
private ServerLevel serverLevel;
private boolean enabled = false;
@SuppressWarnings("DataFlowIssue")
public 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 level, Extent extent) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PaperweightLevelProxy newLevel;
try {
newLevel = (PaperweightLevelProxy) unsafe.allocateInstance(PaperweightLevelProxy.class);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
newLevel.adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
newLevel.extent = extent;
newLevel.serverLevel = level;
return newLevel;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Nullable
@Override
public BlockEntity getBlockEntity(@Nonnull BlockPos blockPos) {
if (!enabled) {
return null;
}
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.extent.getFullBlock(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
).getNbtReference().getValue()),
this.serverLevel.registryAccess()
);
return newEntity;
}
@Override
@Nonnull
public BlockState getBlockState(@Nonnull BlockPos blockPos) {
if (!enabled) {
return Blocks.AIR.defaultBlockState();
}
com.sk89q.worldedit.world.block.BlockState state = this.extent.getBlock(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
);
return adapter.adapt(state);
}
@SuppressWarnings("unused")
@Override
@Nonnull
public FluidState getFluidState(@Nonnull BlockPos pos) {
if (!enabled) {
return Fluids.EMPTY.defaultFluidState();
}
return getBlockState(pos).getFluidState();
}
@SuppressWarnings("unused")
@Override
public boolean isWaterAt(@Nonnull BlockPos pos) {
if (!enabled) {
return false;
}
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
}

Datei anzeigen

@ -0,0 +1,96 @@
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.EditSession;
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.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.CraftWorld;
import javax.annotation.Nullable;
public class PaperweightPlacementStateProcessor extends PlacementStateProcessor {
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
private final FaweMutableBlockPlaceContext mutableBlockPlaceContext;
public PaperweightPlacementStateProcessor(
final Extent extent,
final BlockTypeMask mask,
final boolean includeUnedited
) {
super(extent, mask, includeUnedited);
World world;
if (extent.isWorld()) {
world = (World) extent;
} else if (extent instanceof EditSession session) {
world = session.getWorld();
} else if ((world = new ExtentTraverser<>(extent).findAndGet(BukkitWorld.class)) == null) {
throw new UnsupportedOperationException("Cannot find world of extent.");
}
BukkitWorld bukkitWorld;
if (world instanceof WorldWrapper wrapper) {
bukkitWorld = (BukkitWorld) wrapper.getParent();
} else {
bukkitWorld = (BukkitWorld) world;
}
PaperweightLevelProxy proxyLevel = PaperweightLevelProxy.getInstance(
((CraftWorld) bukkitWorld.getWorld()).getHandle(),
extent
);
mutableBlockPlaceContext = new FaweMutableBlockPlaceContext(proxyLevel);
proxyLevel.setEnabled(true);
}
@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, includeUnedited);
}
@Override
public PlacementStateProcessor fork() {
return new PaperweightPlacementStateProcessor(extent, mask, includeUnedited);
}
}

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

@ -0,0 +1,138 @@
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 FaweMutableBlockPlaceContext extends BlockPlaceContext {
private static final BlockHitResult DEFAULT_BLOCK_HIT = new BlockHitResult(Vec3.ZERO, Direction.NORTH, BlockPos.ZERO, false);
private final ServerLevel level;
private BlockHitResult hitResult = null;
private Direction direction = null;
private BlockPos relativePos;
@SuppressWarnings("DataFlowIssue")
public FaweMutableBlockPlaceContext(ServerLevel level) {
super(
level,
null,
null,
null,
DEFAULT_BLOCK_HIT
);
this.level = level;
this.replaceClicked = false;
}
public FaweMutableBlockPlaceContext 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

@ -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;
@ -22,6 +23,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;
@ -451,6 +453,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(ordinalToIbdID[blockState.getOrdinal()]);
}
@Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = getServerLevel(world);
@ -632,6 +638,11 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
return new PaperweightPostProcessor();
}
@Override
public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, boolean includeUnedited) {
return new PaperweightPlacementStateProcessor(extent, mask, includeUnedited);
}
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,110 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extent.Extent;
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.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import sun.misc.Unsafe;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightLevelProxy extends ServerLevel {
private PaperweightFaweAdapter adapter;
private Extent extent;
private ServerLevel serverLevel;
private boolean enabled = false;
@SuppressWarnings("DataFlowIssue")
public 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 level, Extent extent) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PaperweightLevelProxy newLevel;
try {
newLevel = (PaperweightLevelProxy) unsafe.allocateInstance(PaperweightLevelProxy.class);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
newLevel.adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
newLevel.extent = extent;
newLevel.serverLevel = level;
return newLevel;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Nullable
@Override
public BlockEntity getBlockEntity(@Nonnull BlockPos blockPos) {
if (!enabled) {
return null;
}
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.extent.getFullBlock(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
).getNbtReference().getValue()),
this.serverLevel.registryAccess()
);
return newEntity;
}
@Override
@Nonnull
public BlockState getBlockState(@Nonnull BlockPos blockPos) {
if (!enabled) {
return Blocks.AIR.defaultBlockState();
}
com.sk89q.worldedit.world.block.BlockState state = this.extent.getBlock(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
);
return adapter.adapt(state);
}
@SuppressWarnings("unused")
@Override
@Nonnull
public FluidState getFluidState(@Nonnull BlockPos pos) {
if (!enabled) {
return Fluids.EMPTY.defaultFluidState();
}
return getBlockState(pos).getFluidState();
}
@SuppressWarnings("unused")
@Override
public boolean isWaterAt(@Nonnull BlockPos pos) {
if (!enabled) {
return false;
}
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
}

Datei anzeigen

@ -0,0 +1,96 @@
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.EditSession;
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.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.CraftWorld;
import javax.annotation.Nullable;
public class PaperweightPlacementStateProcessor extends PlacementStateProcessor {
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
private final FaweMutableBlockPlaceContext mutableBlockPlaceContext;
public PaperweightPlacementStateProcessor(
final Extent extent,
final BlockTypeMask mask,
final boolean includeUnedited
) {
super(extent, mask, includeUnedited);
World world;
if (extent.isWorld()) {
world = (World) extent;
} else if (extent instanceof EditSession session) {
world = session.getWorld();
} else if ((world = new ExtentTraverser<>(extent).findAndGet(BukkitWorld.class)) == null) {
throw new UnsupportedOperationException("Cannot find world of extent.");
}
BukkitWorld bukkitWorld;
if (world instanceof WorldWrapper wrapper) {
bukkitWorld = (BukkitWorld) wrapper.getParent();
} else {
bukkitWorld = (BukkitWorld) world;
}
PaperweightLevelProxy proxyLevel = PaperweightLevelProxy.getInstance(
((CraftWorld) bukkitWorld.getWorld()).getHandle(),
extent
);
mutableBlockPlaceContext = new FaweMutableBlockPlaceContext(proxyLevel);
proxyLevel.setEnabled(true);
}
@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, includeUnedited);
}
@Override
public PlacementStateProcessor fork() {
return new PaperweightPlacementStateProcessor(extent, mask, includeUnedited);
}
}

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,6 +38,8 @@ 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.util.SideEffect;
import com.sk89q.worldedit.util.lifecycle.Lifecycled;
@ -309,5 +312,10 @@ public class BukkitServerInterface extends AbstractPlatform implements MultiUser
}
return this.plugin.getBukkitImplAdapter().getTickingPostProcessor();
}
@Override
public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, boolean includeUnedited) {
return this.plugin.getBukkitImplAdapter().getPlatformPlacementProcessor(extent, mask, includeUnedited);
}
//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, boolean includeUnedited) {
return null;
}
//FAWE end
}

Datei anzeigen

@ -0,0 +1,267 @@
package com.fastasyncworldedit.core.extent.processor;
import com.fastasyncworldedit.core.extent.filter.block.FilterBlock;
import com.fastasyncworldedit.core.math.MutableBlockVector3;
import com.fastasyncworldedit.core.math.MutableVector3;
import com.fastasyncworldedit.core.queue.Filter;
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.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.NBTUtils;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.NullExtent;
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.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.Set;
import java.util.stream.Collectors;
public abstract class PlacementStateProcessor extends AbstractDelegateExtent implements IBatchProcessor, Filter, Pattern {
private static final Direction[] NESW = new Direction[]{Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
private static final boolean SETUP = false;
private static BlockTypeMask DEFAULT_MASK = null;
protected final Extent extent;
protected final BlockTypeMask mask;
protected final boolean includeUnedited;
private final MutableVector3 clickPos = new MutableVector3();
private final MutableBlockVector3 clickedBlock = new MutableBlockVector3();
public PlacementStateProcessor(Extent extent, BlockTypeMask mask, boolean includeUnedited) {
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.includeUnedited = includeUnedited;
}
private static void setup() {
DEFAULT_MASK = new BlockTypeMask(new NullExtent());
DEFAULT_MASK.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.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,
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.STAIRS, BlockCategories.WALLS, BlockCategories.BAMBOO_BLOCKS, BlockCategories.CAVE_VINES, BlockCategories.TALL_FLOWERS};
for (BlockCategory category : categories) {
if (category != null) {
DEFAULT_MASK.add(category.getAll());
}
}
}
@Override
public IChunkSet processSet(IChunk iChunk, IChunkGet iChunkGet, IChunkSet iChunkSet) {
int chunkX = iChunk.getX() << 4;
int chunkZ = iChunk.getZ() << 4;
for (int layer = iChunkGet.getMinSectionPosition(); layer <= iChunkGet.getMaxSectionPosition(); layer++) {
int layerY = layer << 4;
char[] set = iChunkSet.loadIfPresent(layer);
char[] get = null;
if (set == null) {
if (!includeUnedited) {
continue;
}
}
for (int y = 0, i = 0; y < 16; y++) {
int blockY = layerY + y;
for (int z = 0; z < 16; z++) {
int blockZ = chunkZ + z;
for (int x = 0; x < 16; x++, i++) {
int blockX = chunkX + x;
char ordinal = set == null ? BlockTypesCache.ReservedIDs.__RESERVED__ : set[i];
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
if (!includeUnedited) {
continue;
}
if (get == null) {
get = iChunkGet.load(layer);
}
ordinal = get[i];
}
BlockState state = BlockTypesCache.states[ordinal];
if (!mask.test(state.getBlockType())) {
continue;
}
char newOrdinal = getBlockOrdinal(blockX, blockY, blockZ, state);
if (set == null) {
set = iChunkSet.load(layer);
}
set[i] = newOrdinal;
}
}
}
}
return iChunkSet;
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.CHANGING_BLOCKS;
}
@Override
public abstract PlacementStateProcessor fork();
// Require block type to avoid duplicate lookup
protected abstract char getStateAtFor(
int x, int y, int z, BlockState state, Vector3 clickPos, Direction clickedFaceDirection, BlockVector3 clickedBlock
);
private char getBlockOrdinal(
final int blockX,
final int blockY,
final int blockZ,
final BlockState state
) {
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(); // opposite
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.5);
}
} 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);
}
@Override
public void applyBlock(FilterBlock block) {
BlockState state = BlockTypesCache.states[block.getOrdinal()];
if (!mask.test(state.getBlockType())) {
return;
}
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.getBlockType())) {
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, newState);
LinCompoundTag nbt = block.getNbt();
if (nbt != null && newState.getBlockType() == block.getBlockType()) {
orDefault.setTile(set.x(), set.y(), set.z(), new CompoundTag(nbt));
}
return true;
}
return false;
}
@Override
public BaseBlock applyBlock(final BlockVector3 position) {
BaseBlock block = extent.getFullBlock(position);
if (!mask.test(block.getBlockType())) {
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()) {
return state.toBaseBlock(nbt);
}
return state.toBaseBlock();
}
return null;
}
}

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

@ -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;
@ -908,4 +910,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, true)
);
if (affected != 0) {
actor.print(Caption.of("worldedit.set.done", TextComponent.of(affected)));
}
return affected;
}
//FAWE end
}

Datei anzeigen

@ -19,12 +19,15 @@
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.registry.Keyed;
import com.sk89q.worldedit.util.SideEffect;
@ -276,5 +279,13 @@ public interface Platform extends Keyed {
default IBatchProcessor getPlatformPostProcessor(boolean fastMode) {
return null;
}
/**
* Returns an {@link PlacementStateProcessor} instance for processing placed blocks to "fix" them.
* @since TODO
*/
default PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, boolean includeUnedited) {
return null;
}
//FAWE end
}

Datei anzeigen

@ -21,14 +21,20 @@ 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.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.Set;
/**
* A collection of cardinal, ordinal, and secondary-ordinal directions.
@ -36,32 +42,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 +101,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 +358,42 @@ public enum Direction {
}
//FAWE start - utility methods for block states
public static EnumSet<Direction> getDirections(BlockState state) {
Set<Direction> directions = new HashSet<>();
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);
}
if (value instanceof Boolean) {
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 EnumSet.copyOf(directions);
}
//FAWE end
}