From 2461b3c5e5cd66581350ee66d68c59403d07614d Mon Sep 17 00:00:00 2001 From: Pierre Maurice Schwang Date: Sun, 15 Dec 2024 21:15:04 +0100 Subject: [PATCH] chore: add block connection fixer to adapter --- .../ext/fawe/v1_21_4/PaperweightAdapter.java | 12 +- .../PaperweightServerLevelDelegateProxy.java | 240 ++++++++++++++++++ .../fawe/v1_21_4/PaperweightFaweAdapter.java | 21 +- ...perweightFaweMutableBlockPlaceContext.java | 143 +++++++++++ .../fawe/v1_21_4/PaperweightGetBlocks.java | 3 +- .../fawe/v1_21_4/PaperweightLevelProxy.java | 136 ++++++++++ .../PaperweightPlacementStateProcessor.java | 111 ++++++++ 7 files changed, 658 insertions(+), 8 deletions(-) create mode 100644 worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_4/PaperweightServerLevelDelegateProxy.java create mode 100644 worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweMutableBlockPlaceContext.java create mode 100644 worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightLevelProxy.java create mode 100644 worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightPlacementStateProcessor.java diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_4/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_4/PaperweightAdapter.java index 976a039cd7..dcee6e1462 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_4/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_4/PaperweightAdapter.java @@ -842,12 +842,12 @@ private ResourceKey getWorldDimKey(Environment env) { } private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( - SideEffect.NEIGHBORS, - SideEffect.LIGHTING, - SideEffect.VALIDATION, - SideEffect.ENTITY_AI, - SideEffect.EVENTS, - SideEffect.UPDATE + //FAWE start - FAWE-supported side effects + SideEffect.HISTORY, + SideEffect.HEIGHTMAPS, + SideEffect.LIGHTING, + SideEffect.NEIGHBORS + //FAWE end ); @Override diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_4/PaperweightServerLevelDelegateProxy.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_4/PaperweightServerLevelDelegateProxy.java new file mode 100644 index 0000000000..52dac65a09 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_4/PaperweightServerLevelDelegateProxy.java @@ -0,0 +1,240 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_4; + +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_4.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); + } + +} diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweAdapter.java index 92548a2477..ae3b591beb 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweAdapter.java @@ -4,6 +4,7 @@ import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.entity.LazyBaseEntity; +import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IBatchProcessor; @@ -13,6 +14,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; import com.mojang.serialization.Codec; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.blocks.BaseItemStack; @@ -22,6 +24,7 @@ import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4.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; @@ -279,9 +282,16 @@ public BaseBlock getFullBlock(final Location location) { return state.toBaseBlock(); } + private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( + SideEffect.HISTORY, + SideEffect.HEIGHTMAPS, + SideEffect.LIGHTING, + SideEffect.NEIGHBORS + ); + @Override public Set getSupportedSideEffects() { - return SideEffectSet.defaults().getSideEffectsToApply(); + return SUPPORTED_SIDE_EFFECTS; } @Override @@ -425,6 +435,10 @@ public > BlockData adapt(B state) { return material.getCraftBlockData(); } + public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockState) { + return Block.stateById(getOrdinalToIbdID()[blockState.getOrdinal()]); + } + @Override public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { ServerLevel nmsWorld = getServerLevel(world); @@ -605,6 +619,11 @@ public IBatchProcessor getTickingPostProcessor() { return new PaperweightPostProcessor(); } + @Override + public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, Region region) { + return new PaperweightPlacementStateProcessor(extent, mask, region); + } + private boolean wasAccessibleSinceLastSave(ChunkHolder holder) { if (PaperLib.isPaper()) { // Papers new chunk system has no related replacement - therefor we assume true. return true; diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweMutableBlockPlaceContext.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweMutableBlockPlaceContext.java new file mode 100644 index 0000000000..93b3550956 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweMutableBlockPlaceContext.java @@ -0,0 +1,143 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class PaperweightFaweMutableBlockPlaceContext extends BlockPlaceContext { + + private static final BlockHitResult DEFAULT_BLOCK_HIT = new BlockHitResult( + new Vec3(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE), + Direction.NORTH, + new BlockPos(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE), + false + ); + private final ServerLevel level; + private BlockHitResult hitResult = null; + private Direction direction = null; + private BlockPos relativePos; + + @SuppressWarnings("DataFlowIssue") + public PaperweightFaweMutableBlockPlaceContext(ServerLevel level) { + super( + level, + null, + null, + null, + DEFAULT_BLOCK_HIT + + ); + this.level = level; + this.replaceClicked = false; + } + + public PaperweightFaweMutableBlockPlaceContext withSetting(BlockHitResult hitResult, Direction direction) { + this.hitResult = hitResult; + this.direction = direction; + this.relativePos = hitResult.getBlockPos().relative(hitResult.getDirection()); + return this; + } + + @Override + @Nonnull + public BlockPos getClickedPos() { + return this.relativePos; + } + + @Override + @Nonnull + public Direction getClickedFace() { + return this.hitResult.getDirection(); + } + + @Override + @Nonnull + public Vec3 getClickLocation() { + return this.hitResult.getLocation(); + } + + @Override + public boolean isInside() { + return this.hitResult.isInside(); + } + + @Override + @SuppressWarnings("NullableProblems") + public ItemStack getItemInHand() { + return ItemStack.EMPTY; + } + + @Nullable + @Override + public Player getPlayer() { + return null; + } + + @Override + @SuppressWarnings("NullableProblems") + public InteractionHand getHand() { + return null; + } + + @Override + @Nonnull + public Level getLevel() { + return this.level; + } + + @Override + @Nonnull + public Direction getHorizontalDirection() { + return this.direction.getAxis() == Direction.Axis.Y ? Direction.NORTH : this.direction; + } + + @Override + public boolean isSecondaryUseActive() { + return false; + } + + @Override + public float getRotation() { + return (float) (this.direction.get2DDataValue() * 90); + } + + @Override + public boolean canPlace() { + return this.getLevel().getBlockState(this.getClickedPos()).canBeReplaced(this); + } + + @Override + public boolean replacingClickedOnBlock() { + return false; + } + + @Override + @Nonnull + public Direction getNearestLookingDirection() { + return direction; + } + + @Override + @Nonnull + public Direction getNearestLookingVerticalDirection() { + return direction.getAxis() == Direction.Axis.Y ? Direction.UP : Direction.DOWN; + } + + @Override + @Nonnull + public Direction[] getNearestLookingDirections() { + return new Direction[]{direction}; + } + +} + diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java index 8bc23289b5..581c8d0980 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java @@ -21,6 +21,7 @@ import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockTypesCache; @@ -837,7 +838,7 @@ public synchronized > T call(IChunkSet set, Runnable finaliz nmsChunk.mustNotSave = false; nmsChunk.markUnsaved(); // send to player - if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) { + if (!set.getSideEffectSet().shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) { this.send(); } if (finalizer != null) { diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightLevelProxy.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightLevelProxy.java new file mode 100644 index 0000000000..bb5f6f2b14 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightLevelProxy.java @@ -0,0 +1,136 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4; + +import com.fastasyncworldedit.core.util.ReflectionUtils; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.FluidTags; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.border.WorldBorder; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import org.enginehub.linbus.tree.LinCompoundTag; +import sun.misc.Unsafe; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class PaperweightLevelProxy extends ServerLevel { + + protected ServerLevel serverLevel; + private PaperweightPlacementStateProcessor processor; + private PaperweightFaweAdapter adapter; + + @SuppressWarnings("DataFlowIssue") + private PaperweightLevelProxy() { + super(null, null, null, null, null, null, null, true, 0L, null, true, null, null, null, null); + throw new IllegalStateException("Cannot be instantiated"); + } + + public static PaperweightLevelProxy getInstance(ServerLevel serverLevel, PaperweightPlacementStateProcessor processor) { + Unsafe unsafe = ReflectionUtils.getUnsafe(); + + PaperweightLevelProxy newLevel; + try { + newLevel = (PaperweightLevelProxy) unsafe.allocateInstance(PaperweightLevelProxy.class); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } + newLevel.processor = processor; + newLevel.adapter = ((PaperweightFaweAdapter) WorldEditPlugin.getInstance().getBukkitImplAdapter()); + newLevel.serverLevel = serverLevel; + return newLevel; + } + + @Nullable + @Override + public BlockEntity getBlockEntity(@Nonnull BlockPos blockPos) { + if (blockPos.getX() == Integer.MAX_VALUE) { + return null; + } + LinCompoundTag tag = processor.getTileAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()); + if (tag == null) { + return null; + } + BlockState state = adapter.adapt(processor.getBlockStateAt(blockPos.getX(), blockPos.getY(), blockPos.getZ())); + if (!(state.getBlock() instanceof EntityBlock entityBlock)) { + return null; + } + BlockEntity tileEntity = entityBlock.newBlockEntity(blockPos, state); + tileEntity.loadWithComponents((CompoundTag) adapter.fromNativeLin(tag), serverLevel.registryAccess()); + return tileEntity; + } + + @Override + @Nonnull + public BlockState getBlockState(@Nonnull BlockPos blockPos) { + if (blockPos.getX() == Integer.MAX_VALUE) { + return Blocks.AIR.defaultBlockState(); + } + com.sk89q.worldedit.world.block.BlockState state = processor.getBlockStateAt( + blockPos.getX(), + blockPos.getY(), + blockPos.getZ() + ); + return adapter.adapt(state); + } + + @SuppressWarnings("unused") + @Override + @Nonnull + public FluidState getFluidState(@Nonnull BlockPos pos) { + if (pos.getX() == Integer.MAX_VALUE) { + return Fluids.EMPTY.defaultFluidState(); + } + return getBlockState(pos).getFluidState(); + } + + @SuppressWarnings("unused") + @Override + public boolean isWaterAt(@Nonnull BlockPos pos) { + if (pos.getX() == Integer.MAX_VALUE) { + return false; + } + return getBlockState(pos).getFluidState().is(FluidTags.WATER); + } + + @Override + public int getHeight() { + return serverLevel.getHeight(); + } + + @Override + public int getMinY() { + return serverLevel.getMinY(); + } + + @Override + public int getMaxY() { + return serverLevel.getMaxY(); + } + + @Override + public boolean isInsideBuildHeight(int blockY) { + return serverLevel.isInsideBuildHeight(blockY); + } + + @Override + public boolean isOutsideBuildHeight(BlockPos pos) { + return serverLevel.isOutsideBuildHeight(pos); + } + + @Override + public boolean isOutsideBuildHeight(int blockY) { + return serverLevel.isOutsideBuildHeight(blockY); + } + + @Override + public WorldBorder getWorldBorder() { + return serverLevel.getWorldBorder(); + } + +} diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightPlacementStateProcessor.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightPlacementStateProcessor.java new file mode 100644 index 0000000000..1cee9692bf --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightPlacementStateProcessor.java @@ -0,0 +1,111 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4; + +import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor; +import com.fastasyncworldedit.core.util.ExtentTraverser; +import com.fastasyncworldedit.core.wrappers.WorldWrapper; +import com.sk89q.worldedit.bukkit.BukkitWorld; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.BlockTypeMask; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypesCache; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import org.bukkit.craftbukkit.CraftWorld; + +import javax.annotation.Nullable; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +public class PaperweightPlacementStateProcessor extends PlacementStateProcessor { + + private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin + .getInstance() + .getBukkitImplAdapter()); + private final PaperweightFaweMutableBlockPlaceContext mutableBlockPlaceContext; + private final PaperweightLevelProxy proxyLevel; + + public PaperweightPlacementStateProcessor(Extent extent, BlockTypeMask mask, Region region) { + super(extent, mask, region); + World world = ExtentTraverser.getWorldFromExtent(extent); + if (world == null) { + throw new UnsupportedOperationException( + "World is required for PlacementStateProcessor but none found in given extent."); + } + BukkitWorld bukkitWorld; + if (world instanceof WorldWrapper wrapper) { + bukkitWorld = (BukkitWorld) wrapper.getParent(); + } else { + bukkitWorld = (BukkitWorld) world; + } + this.proxyLevel = PaperweightLevelProxy.getInstance(((CraftWorld) bukkitWorld.getWorld()).getHandle(), this); + this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel); + } + + private PaperweightPlacementStateProcessor( + Extent extent, + BlockTypeMask mask, + Map crossChunkSecondPasses, + ServerLevel serverLevel, + ThreadLocal threadProcessors, + Region region, + AtomicBoolean finished + ) { + super(extent, mask, crossChunkSecondPasses, threadProcessors, region, finished); + this.proxyLevel = PaperweightLevelProxy.getInstance(serverLevel, this); + this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel); + } + + @Override + protected char getStateAtFor( + int x, + int y, + int z, + BlockState state, + Vector3 clickPos, + Direction clickedFaceDirection, + BlockVector3 clickedBlock + ) { + Block block = ((PaperweightBlockMaterial) state.getMaterial()).getBlock(); + Vec3 pos = new Vec3(clickPos.x(), clickPos.y(), clickPos.z()); + net.minecraft.core.Direction side = net.minecraft.core.Direction.valueOf(clickedFaceDirection.toString()); + BlockPos blockPos = new BlockPos(clickedBlock.x(), clickedBlock.y(), clickedBlock.z()); + net.minecraft.world.level.block.state.BlockState newState = block.getStateForPlacement(mutableBlockPlaceContext.withSetting( + new BlockHitResult(pos, side, blockPos, false), + side.getOpposite() + )); + return newState == null ? BlockTypesCache.ReservedIDs.AIR : adapter.ibdIDToOrdinal(Block.BLOCK_STATE_REGISTRY.getId( + newState)); + } + + @Override + @Nullable + public Extent construct(Extent child) { + if (child == getExtent()) { + return this; + } + return new PaperweightPlacementStateProcessor(child, mask, region); + } + + @Override + public PlacementStateProcessor fork() { + return new PaperweightPlacementStateProcessor( + extent, + mask, + postCompleteSecondPasses, + proxyLevel.serverLevel, + threadProcessors, + region, + finished + ); + } + +}