From 609dd009b48e1dab3b32a244762e03b8a96dad81 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sun, 30 Jun 2024 09:59:43 +0100 Subject: [PATCH] Add for 1.21 --- .../ext/fawe/v1_21_R1/PaperweightAdapter.java | 10 +- .../PaperweightServerLevelDelegateProxy.java | 240 ++++++++++++++++++ .../fawe/v1_21_R1/PaperweightFaweAdapter.java | 21 +- ...perweightFaweMutableBlockPlaceContext.java | 143 +++++++++++ .../fawe/v1_21_R1/PaperweightGetBlocks.java | 8 +- .../fawe/v1_21_R1/PaperweightLevelProxy.java | 99 ++++++++ .../PaperweightPlacementStateProcessor.java | 111 ++++++++ 7 files changed, 623 insertions(+), 9 deletions(-) create mode 100644 worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightServerLevelDelegateProxy.java create mode 100644 worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweMutableBlockPlaceContext.java create mode 100644 worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightLevelProxy.java create mode 100644 worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlacementStateProcessor.java diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightAdapter.java index f66bb1460..22df83c7b 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightAdapter.java @@ -886,12 +886,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( - SideEffect.NEIGHBORS, + //FAWE start - FAWE-supported side effects + SideEffect.HISTORY, + SideEffect.HEIGHTMAPS, SideEffect.LIGHTING, - SideEffect.VALIDATION, - SideEffect.ENTITY_AI, - SideEffect.EVENTS, - SideEffect.UPDATE + SideEffect.NEIGHBORS + //FAWE end ); @Override diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightServerLevelDelegateProxy.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightServerLevelDelegateProxy.java new file mode 100644 index 000000000..a992e936c --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/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_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); + } + +} diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java index e0dae1b2f..0839171d6 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java @@ -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.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; @@ -12,6 +13,7 @@ import com.fastasyncworldedit.core.util.NbtUtils; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; import com.mojang.serialization.Codec; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.blocks.BaseItemStack; @@ -21,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; @@ -293,9 +296,16 @@ public final class PaperweightFaweAdapter extends FaweAdapter 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 @@ -443,6 +453,10 @@ public final class PaperweightFaweAdapter extends FaweAdapter { // Set Modified nmsChunk.setLightCorrect(true); // Set Modified nmsChunk.mustNotSave = false; nmsChunk.setUnsaved(true); // send to player - if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) { + if (!set + .getSideEffectSet() + .shouldApply(SideEffect.LIGHTING) || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalBitMask == 0 && biomes != null) { this.send(); } if (finalizer != null) { diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightLevelProxy.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightLevelProxy.java new file mode 100644 index 000000000..cedf5ae20 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightLevelProxy.java @@ -0,0 +1,99 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1; + +import com.fastasyncworldedit.core.util.ReflectionUtils; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.FluidTags; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.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 { + + 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; + } + com.sk89q.jnbt.CompoundTag 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.fromNative(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); + } + +} diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlacementStateProcessor.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlacementStateProcessor.java new file mode 100644 index 000000000..19a7307a5 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlacementStateProcessor.java @@ -0,0 +1,111 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1; + +import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor; +import com.fastasyncworldedit.core.util.ExtentTraverser; +import com.fastasyncworldedit.core.wrappers.WorldWrapper; +import com.sk89q.worldedit.bukkit.BukkitWorld; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.BlockTypeMask; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypesCache; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import org.bukkit.craftbukkit.CraftWorld; + +import javax.annotation.Nullable; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +public class PaperweightPlacementStateProcessor extends PlacementStateProcessor { + + private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin + .getInstance() + .getBukkitImplAdapter()); + private final PaperweightLevelProxy proxyLevel; + private final PaperweightFaweMutableBlockPlaceContext mutableBlockPlaceContext; + + public PaperweightPlacementStateProcessor(Extent extent, BlockTypeMask mask, Region region) { + super(extent, mask, region); + World world = ExtentTraverser.getWorldFromExtent(extent); + if (world == null) { + throw new UnsupportedOperationException( + "World is required for PlacementStateProcessor but none found in given extent."); + } + BukkitWorld bukkitWorld; + if (world instanceof WorldWrapper wrapper) { + bukkitWorld = (BukkitWorld) wrapper.getParent(); + } else { + bukkitWorld = (BukkitWorld) world; + } + this.proxyLevel = PaperweightLevelProxy.getInstance(((CraftWorld) bukkitWorld.getWorld()).getHandle(), this); + this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel); + } + + private PaperweightPlacementStateProcessor( + Extent extent, + BlockTypeMask mask, + Map 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 + ); + } + +}