Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

accurate placement protocol V3 support (For Crafter Rotations) #323

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion gradle.properties
sakura-ryoko marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ org.gradle.jvmargs=-Xmx1G
fabric_api_version=0.100.3+1.21

# Mod Properties
mod_version = 1.4.147
mod_version = 1.4.147-accuratePlacement_v3
maven_group = carpet-extra
archives_base_name = carpet-extra

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ private BlockState getAlternatePlacement(Block block, ItemPlacementContext conte
{
if (CarpetExtraSettings.accurateBlockPlacement)
{
BlockState tryAlternative = BlockPlacer.alternativeBlockPlacement(block, context);
BlockState tryAlternative = BlockPlacer.alternativeBlockPlacementV3(block.getPlacementState(context), BlockPlacer.UseContext.from(context, context.getHand()));
if (tryAlternative != null)
return tryAlternative;
}
return block.getPlacementState(context);
}

}
221 changes: 215 additions & 6 deletions src/main/java/carpetextra/utils/BlockPlacer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package carpetextra.utils;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import com.google.common.collect.ImmutableSet;
import org.jetbrains.annotations.Nullable;
import net.minecraft.block.BedBlock;
import net.minecraft.block.Block;
Expand All @@ -9,22 +13,120 @@
import net.minecraft.block.enums.BlockHalf;
import net.minecraft.block.enums.ComparatorMode;
import net.minecraft.block.enums.SlabType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.property.DirectionProperty;
import net.minecraft.state.property.Properties;
import net.minecraft.state.property.Property;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;

public class BlockPlacer
{
public static BlockState alternativeBlockPlacement(Block block, ItemPlacementContext context)
public static final ImmutableSet<Property<?>> WHITELISTED_PROPERTIES = ImmutableSet.of(
Properties.INVERTED,
Properties.OPEN,
Properties.PERSISTENT,
Properties.CAN_SUMMON,
Properties.ATTACHMENT,
Properties.AXIS,
Properties.BED_PART,
Properties.BLOCK_HALF,
Properties.BLOCK_FACE,
Properties.CHEST_TYPE,
Properties.COMPARATOR_MODE,
Properties.DOOR_HINGE,
Properties.DOUBLE_BLOCK_HALF,
Properties.ORIENTATION,
Properties.RAIL_SHAPE,
Properties.STRAIGHT_RAIL_SHAPE,
Properties.SLAB_TYPE,
Properties.STAIR_SHAPE,
Properties.BITES,
Properties.DELAY,
Properties.NOTE,
Properties.ROTATION
);

// Written by masa
public static <T extends Comparable<T>> BlockState alternativeBlockPlacementV3(BlockState state, UseContext context)
{
Vec3d hitPos = context.getHitPos();
BlockPos blockPos = context.getBlockPos();
int protocolValue = (int) (context.getHitVec().x - (double) context.getPos().getX()) - 2;

if (protocolValue < 0)
{
return state;
}

@Nullable DirectionProperty property = getFirstDirectionProperty(state);

if (property != null && property != Properties.VERTICAL_DIRECTION)
{
state = applyDirectionProperty(state, context, property, protocolValue);

if (state == null)
{
return null;
}

// Consume the bits used for the facing
protocolValue >>>= 3;
}
// Consume the lowest unused bit
protocolValue >>>= 1;

List<Property<?>> propList = new ArrayList<>(state.getBlock().getStateManager().getProperties());
propList.sort(Comparator.comparing(Property::getName));

try
{
for (Property<?> p : propList)
{
if ((p instanceof DirectionProperty) == false &&
WHITELISTED_PROPERTIES.contains(p))
{
@SuppressWarnings("unchecked")
Property<T> prop = (Property<T>) p;
List<T> list = new ArrayList<>(prop.getValues());
list.sort(Comparable::compareTo);

int requiredBits = MathHelper.floorLog2(MathHelper.smallestEncompassingPowerOfTwo(list.size()));
int bitMask = ~(0xFFFFFFFF << requiredBits);
int valueIndex = protocolValue & bitMask;

if (valueIndex >= 0 && valueIndex < list.size())
{
T value = list.get(valueIndex);

if (state.get(prop).equals(value) == false &&
value != SlabType.DOUBLE) // don't allow duping slabs by forcing a double slab via the protocol
{
state = state.with(prop, value);
}

protocolValue >>>= requiredBits;
}
}
}
}
catch (Exception e)
{
// Exception
}

return state;
}

public static BlockState alternativeBlockPlacementV2(Block block, UseContext context)
{
Vec3d hitPos = context.getHitVec();
BlockPos blockPos = context.getPos();
double relativeHitX = hitPos.x - blockPos.getX();
BlockState state = block.getPlacementState(context);
BlockState state = block.getPlacementState(context.getItemPlacementContext());

if (relativeHitX < 2 || state == null) // vanilla handling
return null;
Expand All @@ -51,7 +153,7 @@ else if (facingIndex >= 0 && facingIndex <= 5)

if (directionProp.getValues().contains(facing) == false)
{
facing = context.getPlayer().getHorizontalFacing().getOpposite();
facing = context.getEntity().getHorizontalFacing().getOpposite();
}

if (facing != origFacing && directionProp.getValues().contains(facing))
Expand All @@ -60,7 +162,7 @@ else if (facingIndex >= 0 && facingIndex <= 5)
{
BlockPos headPos = blockPos.offset(facing);

if (context.getWorld().getBlockState(headPos).canReplace(context) == false)
if (context.getWorld().getBlockState(headPos).canReplace(context.getItemPlacementContext()) == false)
{
return null;
}
Expand Down Expand Up @@ -110,6 +212,46 @@ else if (state.contains(Properties.SLAB_TYPE) &&
return state;
}

private static BlockState applyDirectionProperty(BlockState state, UseContext context,
DirectionProperty property, int protocolValue)
{
Direction facingOrig = state.get(property);
Direction facing = facingOrig;
int decodedFacingIndex = (protocolValue & 0xF) >> 1;

if (decodedFacingIndex == 6) // the opposite of the normal facing requested
{
facing = facing.getOpposite();
}
else if (decodedFacingIndex >= 0 && decodedFacingIndex <= 5)
{
facing = Direction.byId(decodedFacingIndex);

if (property.getValues().contains(facing) == false)
{
facing = context.getEntity().getHorizontalFacing().getOpposite();
}
}

if (facing != facingOrig && property.getValues().contains(facing))
{
if (state.getBlock() instanceof BedBlock)
{
BlockPos headPos = context.getPos().offset(facing);
ItemPlacementContext ctx = context.getItemPlacementContext();

if (context.getWorld().getBlockState(headPos).canReplace(ctx) == false)
{
return null;
}
}

state = state.with(property, facing);
}

return state;
}

@Nullable
public static DirectionProperty getFirstDirectionProperty(BlockState state)
{
Expand All @@ -123,4 +265,71 @@ public static DirectionProperty getFirstDirectionProperty(BlockState state)

return null;
}

public static class UseContext
{
private final World world;
private final BlockPos pos;
private final Direction side;
private final Vec3d hitVec;
private final LivingEntity entity;
private final Hand hand;
@Nullable
private final ItemPlacementContext itemPlacementContext;

private UseContext(World world, BlockPos pos, Direction side, Vec3d hitVec,
LivingEntity entity, Hand hand, @Nullable ItemPlacementContext itemPlacementContext)
{
this.world = world;
this.pos = pos;
this.side = side;
this.hitVec = hitVec;
this.entity = entity;
this.hand = hand;
this.itemPlacementContext = itemPlacementContext;
}

public static UseContext from(ItemPlacementContext ctx, Hand hand)
{
Vec3d pos = ctx.getHitPos();
return new UseContext(ctx.getWorld(), ctx.getBlockPos(), ctx.getSide(), new Vec3d(pos.x, pos.y, pos.z),
ctx.getPlayer(), hand, ctx);
}

public World getWorld()
{
return this.world;
}

public BlockPos getPos()
{
return this.pos;
}

public Direction getSide()
{
return this.side;
}

public Vec3d getHitVec()
{
return this.hitVec;
}

public LivingEntity getEntity()
{
return this.entity;
}

public Hand getHand()
{
return this.hand;
}

@Nullable
public ItemPlacementContext getItemPlacementContext()
{
return this.itemPlacementContext;
}
}
}