diff --git a/common/src/main/java/com/hammy275/immersivemc/ImmersiveMC.java b/common/src/main/java/com/hammy275/immersivemc/ImmersiveMC.java index 83917ec7..ffb725b6 100644 --- a/common/src/main/java/com/hammy275/immersivemc/ImmersiveMC.java +++ b/common/src/main/java/com/hammy275/immersivemc/ImmersiveMC.java @@ -81,6 +81,8 @@ protected static void serverSetup() { BeaconDataPacket::decode, BeaconDataPacket::handle); Network.INSTANCE.register(DoubleControllerVibrate.class, DoubleControllerVibrate::encode, DoubleControllerVibrate::decode, DoubleControllerVibrate::handle); + Network.INSTANCE.register(UsePacket.class, UsePacket::encode, + UsePacket::decode, UsePacket::handle); } } diff --git a/common/src/main/java/com/hammy275/immersivemc/client/config/ClientConstants.java b/common/src/main/java/com/hammy275/immersivemc/client/config/ClientConstants.java index ad23cc98..75ec300d 100644 --- a/common/src/main/java/com/hammy275/immersivemc/client/config/ClientConstants.java +++ b/common/src/main/java/com/hammy275/immersivemc/client/config/ClientConstants.java @@ -31,6 +31,7 @@ public class ClientConstants { public static final int ticksToRenderHitboxesImmersive = Integer.MAX_VALUE; public static final int ticksToRenderSmithingTable = ticksToRenderAnvil; public static final int ticksToRenderChiseledBookshelf = Integer.MAX_VALUE; + public static final int ticksToRenderLever = Integer.MAX_VALUE; // Size of items when rendered in front of something immersive public static final float defaultItemScaleSize = 1f/3f; diff --git a/common/src/main/java/com/hammy275/immersivemc/client/immersive/ImmersiveLever.java b/common/src/main/java/com/hammy275/immersivemc/client/immersive/ImmersiveLever.java new file mode 100644 index 00000000..df32d4bd --- /dev/null +++ b/common/src/main/java/com/hammy275/immersivemc/client/immersive/ImmersiveLever.java @@ -0,0 +1,168 @@ +package com.hammy275.immersivemc.client.immersive; + +import com.hammy275.immersivemc.client.config.ClientConstants; +import com.hammy275.immersivemc.client.immersive.info.AbstractImmersiveInfo; +import com.hammy275.immersivemc.client.immersive.info.LeverInfo; +import com.hammy275.immersivemc.common.config.ActiveConfig; +import com.hammy275.immersivemc.common.immersive.ImmersiveCheckers; +import com.hammy275.immersivemc.common.immersive.handler.ImmersiveHandler; +import com.hammy275.immersivemc.common.immersive.storage.network.NetworkStorage; +import com.hammy275.immersivemc.common.network.Network; +import com.hammy275.immersivemc.common.network.packet.UsePacket; +import com.hammy275.immersivemc.common.util.Util; +import com.hammy275.immersivemc.common.vr.VRPlugin; +import com.mojang.blaze3d.vertex.PoseStack; +import net.blf02.vrapi.api.data.IVRData; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.AttachFace; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; + +public class ImmersiveLever extends AbstractImmersive { + public ImmersiveLever() { + super(-1); + } + + @Override + public boolean isVROnly() { + return true; + } + + @Override + public boolean clientAuthoritative() { + return true; + } + + @Override + public @Nullable ImmersiveHandler getHandler() { + return null; + } + + @Override + public boolean shouldRender(LeverInfo info, boolean isInVR) { + return info.readyToRender(); + } + + @Override + protected void render(LeverInfo info, PoseStack stack, boolean isInVR) { + for (int i = 0; i <= 1; i++) { + renderHitbox(stack, info.getHitbox(i)); + } + } + + @Override + protected void doTick(LeverInfo info, boolean isInVR) { + super.doTick(info, isInVR); + + BlockState lever = Minecraft.getInstance().level.getBlockState(info.getBlockPosition()); + boolean powered = lever.getValue(BlockStateProperties.POWERED); + int startHitbox = powered ? 1 : 0; + int endHitbox = powered ? 0 : 1; + + for (int c = 0; c <= 1; c++) { + IVRData hand = VRPlugin.API.getVRPlayer(Minecraft.getInstance().player).getController(c); + int lastGrabbed = info.grabbedBox[c]; + int grabbed = Util.getFirstIntersect(hand.position(), info.getAllHitboxes()).orElse(-1); + info.grabbedBox[c] = grabbed; + if (grabbed == endHitbox && lastGrabbed == startHitbox) { + Util.useLever(Minecraft.getInstance().player, info.getBlockPosition()); + Network.INSTANCE.sendToServer(new UsePacket(info.getBlockPosition())); + } + } + + } + + @Override + public boolean enabledInConfig() { + return ActiveConfig.active().useLever; + } + + @Override + protected boolean inputSlotShouldRenderHelpHitbox(LeverInfo info, int slotNum) { + return false; + } + + @Override + public boolean shouldTrack(BlockPos pos, Level level) { + return ImmersiveCheckers.isLever(pos, level); + } + + @Override + public @Nullable LeverInfo refreshOrTrackObject(BlockPos pos, Level level) { + for (LeverInfo info : getTrackedObjects()) { + if (info.getBlockPosition().equals(pos)) { + info.setTicksLeft(ClientConstants.ticksToRenderLever); + return info; + } + } + LeverInfo info = new LeverInfo(pos); + infos.add(info); + return info; + } + + @Override + public boolean shouldBlockClickIfEnabled(AbstractImmersiveInfo info) { + return true; + } + + @Override + protected void initInfo(LeverInfo info) { + Level level = Minecraft.getInstance().level; + BlockState state = level.getBlockState(info.getBlockPosition()); + Vec3 center = Vec3.atCenterOf(info.getBlockPosition()); + AttachFace attachFace = state.getValue(BlockStateProperties.ATTACH_FACE); + Direction facing = state.getValue(BlockStateProperties.HORIZONTAL_FACING); + Direction towardsBaseDir; + Direction towardsOnDir; + switch (attachFace) { + case WALL -> { + towardsBaseDir = facing.getOpposite(); + towardsOnDir = Direction.DOWN; + } + case CEILING -> { + towardsBaseDir = Direction.UP; + towardsOnDir = facing; + } + case FLOOR -> { + towardsBaseDir = Direction.DOWN; + towardsOnDir = facing; + } + default -> throw new IllegalStateException("Lever is attached to unknown face " + attachFace.getSerializedName()); + } + Vec3 towardsBase = Vec3.atLowerCornerOf(towardsBaseDir.getNormal()); + Vec3 towardsOn = Vec3.atLowerCornerOf(towardsOnDir.getNormal()); + center = center.add(towardsBase.scale(0.25)); + + Vec3 offPos = center.add(towardsOn.scale(-0.25)); + Vec3 onPos = center.add(towardsOn.scale(0.25)); + + info.setPosition(0, offPos); + info.setPosition(1, onPos); + + info.setHitbox(0, AABB.ofSize(offPos, 0.5, 0.5, 0.5)); + info.setHitbox(1, AABB.ofSize(onPos, 0.5, 0.5, 0.5)); + } + + @Override + public void handleRightClick(AbstractImmersiveInfo info, Player player, int closest, InteractionHand hand) { + // NO-OP. Handled in doTick() + } + + @Override + public void processStorageFromNetwork(AbstractImmersiveInfo info, NetworkStorage storage) { + // NO-OP. No storage. + } + + @Override + public BlockPos getLightPos(LeverInfo info) { + return info.getBlockPosition(); + } +} diff --git a/common/src/main/java/com/hammy275/immersivemc/client/immersive/Immersives.java b/common/src/main/java/com/hammy275/immersivemc/client/immersive/Immersives.java index 532e152c..a2a671f0 100644 --- a/common/src/main/java/com/hammy275/immersivemc/client/immersive/Immersives.java +++ b/common/src/main/java/com/hammy275/immersivemc/client/immersive/Immersives.java @@ -313,6 +313,8 @@ public class Immersives { .setVROnly(true) .build(); + public static final ImmersiveLever immersiveLever = new ImmersiveLever(); + public static final ImmersiveRepeater immersiveRepeater = new ImmersiveRepeater(); public static final BuiltImmersive immersiveShulker = ImmersiveBuilder.create(ImmersiveHandlers.shulkerBoxHandler) .setConfigChecker(() -> ActiveConfig.active().useShulkerImmersion) diff --git a/common/src/main/java/com/hammy275/immersivemc/client/immersive/info/LeverInfo.java b/common/src/main/java/com/hammy275/immersivemc/client/immersive/info/LeverInfo.java new file mode 100644 index 00000000..a2d29362 --- /dev/null +++ b/common/src/main/java/com/hammy275/immersivemc/client/immersive/info/LeverInfo.java @@ -0,0 +1,74 @@ +package com.hammy275.immersivemc.client.immersive.info; + +import com.hammy275.immersivemc.client.config.ClientConstants; +import com.hammy275.immersivemc.common.obb.BoundingBox; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; + +public class LeverInfo extends AbstractImmersiveInfo { + + protected BoundingBox[] hitboxes = new BoundingBox[2]; + protected Vec3[] positions = new Vec3[2]; + protected final BlockPos pos; + public int[] grabbedBox = new int[]{-1, -1}; // Index is for controller num + + public LeverInfo(BlockPos pos) { + super(ClientConstants.ticksToRenderLever); + this.pos = pos; + } + + @Override + public void setInputSlots() { + this.inputHitboxes = hitboxes; + } + + @Override + public BoundingBox getHitbox(int index) { + return hitboxes[index]; + } + + @Override + public BoundingBox[] getAllHitboxes() { + return hitboxes; + } + + @Override + public void setHitbox(int slot, BoundingBox hitbox) { + hitboxes[slot] = hitbox; + } + + @Override + public boolean hasHitboxes() { + return hitboxes[1] != null; + } + + @Override + public Vec3 getPosition(int index) { + return positions[index]; + } + + @Override + public Vec3[] getAllPositions() { + return positions; + } + + @Override + public void setPosition(int slot, Vec3 position) { + positions[slot] = position; + } + + @Override + public boolean hasPositions() { + return positions[1] != null; + } + + @Override + public boolean readyToRender() { + return hasHitboxes() && hasPositions(); + } + + @Override + public BlockPos getBlockPosition() { + return pos; + } +} diff --git a/common/src/main/java/com/hammy275/immersivemc/common/immersive/ImmersiveCheckers.java b/common/src/main/java/com/hammy275/immersivemc/common/immersive/ImmersiveCheckers.java index 96b8278f..7d805b88 100644 --- a/common/src/main/java/com/hammy275/immersivemc/common/immersive/ImmersiveCheckers.java +++ b/common/src/main/java/com/hammy275/immersivemc/common/immersive/ImmersiveCheckers.java @@ -4,6 +4,7 @@ import com.hammy275.immersivemc.common.immersive.handler.ImmersiveHandlers; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.LeverBlock; import net.minecraft.world.level.block.RepeaterBlock; import java.util.LinkedList; @@ -14,6 +15,7 @@ public class ImmersiveCheckers { public static final List CHECKERS = new LinkedList<>(); static { + CHECKERS.add(ImmersiveCheckers::isLever); CHECKERS.add(ImmersiveCheckers::isRepeater); for (ImmersiveHandler handler : ImmersiveHandlers.HANDLERS) { @@ -23,6 +25,10 @@ public class ImmersiveCheckers { // Vanilla + public static boolean isLever(BlockPos pos, Level level) { + return level.getBlockState(pos).getBlock() instanceof LeverBlock; + } + public static boolean isRepeater(BlockPos pos, Level level) { return level.getBlockState(pos).getBlock() instanceof RepeaterBlock; } diff --git a/common/src/main/java/com/hammy275/immersivemc/common/network/packet/UsePacket.java b/common/src/main/java/com/hammy275/immersivemc/common/network/packet/UsePacket.java new file mode 100644 index 00000000..ae2f1920 --- /dev/null +++ b/common/src/main/java/com/hammy275/immersivemc/common/network/packet/UsePacket.java @@ -0,0 +1,36 @@ +package com.hammy275.immersivemc.common.network.packet; + +import com.hammy275.immersivemc.common.immersive.ImmersiveCheckers; +import com.hammy275.immersivemc.common.util.Util; +import dev.architectury.networking.NetworkManager; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; + +import java.util.function.Supplier; + +public class UsePacket { + + public final BlockPos pos; + + public UsePacket(BlockPos pos) { + this.pos = pos; + } + + public static void encode(UsePacket message, FriendlyByteBuf buffer) { + buffer.writeBlockPos(message.pos); + } + + public static UsePacket decode(FriendlyByteBuf buffer) { + return new UsePacket(buffer.readBlockPos()); + } + + public static void handle(final UsePacket packet, Supplier ctx) { + ctx.get().queue(() -> { + ServerPlayer player = ctx.get().getPlayer() instanceof ServerPlayer ? (ServerPlayer) ctx.get().getPlayer() : null; + if (player != null && ImmersiveCheckers.isLever(packet.pos, player.level())) { + Util.useLever(player, packet.pos); + } + }); + } +} diff --git a/common/src/main/java/com/hammy275/immersivemc/common/util/Util.java b/common/src/main/java/com/hammy275/immersivemc/common/util/Util.java index fb530430..e18c8bc2 100644 --- a/common/src/main/java/com/hammy275/immersivemc/common/util/Util.java +++ b/common/src/main/java/com/hammy275/immersivemc/common/util/Util.java @@ -8,6 +8,7 @@ import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.*; @@ -197,6 +198,14 @@ public static void setRepeater(Level level, BlockPos pos, int newDelay) { } } + public static void useLever(Player player, BlockPos pos) { + if (ImmersiveCheckers.isLever(pos, player.level())) { + BlockState lever = player.level().getBlockState(pos); + lever.use(player.level(), player, InteractionHand.MAIN_HAND, + new BlockHitResult(Vec3.atCenterOf(pos), Direction.NORTH, pos, true)); + } + } + public static Vec3 getPlayerVelocity(Vec3 lastTickPos, Vec3 currentTickPos) { return new Vec3(currentTickPos.x - lastTickPos.x, currentTickPos.y - lastTickPos.y, currentTickPos.z - lastTickPos.z); diff --git a/common/src/main/java/com/hammy275/immersivemc/server/tracker/LeverTracker.java b/common/src/main/java/com/hammy275/immersivemc/server/tracker/LeverTracker.java deleted file mode 100644 index c5e88ac9..00000000 --- a/common/src/main/java/com/hammy275/immersivemc/server/tracker/LeverTracker.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.hammy275.immersivemc.server.tracker; - -import com.hammy275.immersivemc.common.config.ActiveConfig; -import com.hammy275.immersivemc.common.config.CommonConstants; -import com.hammy275.immersivemc.common.tracker.AbstractTracker; -import com.hammy275.immersivemc.common.vr.VRPlugin; -import com.hammy275.immersivemc.common.vr.VRPluginVerify; -import com.hammy275.immersivemc.common.vr.VRRumble; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.FaceAttachedHorizontalDirectionalBlock; -import net.minecraft.world.level.block.HorizontalDirectionalBlock; -import net.minecraft.world.level.block.LeverBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.AttachFace; -import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.Vec3; - -import java.util.HashMap; -import java.util.Map; - -public class LeverTracker extends AbstractTracker { - - public static Map infos = new HashMap<>(); - - public LeverTracker() { - ServerTrackerInit.playerTrackers.add(this); - } - - @Override - protected void tick(Player player) { - LeverInfo info = infos.get(player.getGameProfile().getName()); - if (info == null) { - info = new LeverInfo(); - infos.put(player.getGameProfile().getName(), info); - } - info.tick(); - if (info.cooldown > 0) return; - for (int c = 0; c <= 1; c++) { - Vec3 handPos = VRPlugin.API.getVRPlayer(player).getController(c).position(); - BlockPos handBlockPos = BlockPos.containing(handPos); - BlockPos leverPos; - BlockState state; - if (player.level().getBlockState(handBlockPos).getBlock() == Blocks.LEVER) { - state = player.level().getBlockState(handBlockPos); - leverPos = handBlockPos; - } else if (player.level().getBlockState(handBlockPos.below()).getBlock() == Blocks.LEVER) { - state = player.level().getBlockState(handBlockPos.below()); - leverPos = handBlockPos.below(); - } else if (player.level().getBlockState(handBlockPos.above()).getBlock() == Blocks.LEVER) { - state = player.level().getBlockState(handBlockPos.above()); - leverPos = handBlockPos.above(); - } else { - continue; - } - Vec3 hitboxPos; - if (state.getValue(FaceAttachedHorizontalDirectionalBlock.FACE) == AttachFace.WALL) { - hitboxPos = Vec3.atCenterOf(leverPos); - Direction facing = state.getValue(HorizontalDirectionalBlock.FACING); - Direction towardsWall = facing.getOpposite(); - Vec3 unit = Vec3.atLowerCornerOf(towardsWall.getNormal()); // Converts Vec3i to Vec3 - hitboxPos = hitboxPos.add(unit.multiply(0.25, 0.25, 0.25)); - if (state.getValue(LeverBlock.POWERED)) { - hitboxPos = hitboxPos.add(0, -1d/3d, 0); - } else { - hitboxPos = hitboxPos.add(0, 1d/3d, 0); - } - } else { - hitboxPos = Vec3.atCenterOf(leverPos); - Direction facingOn = state.getValue(HorizontalDirectionalBlock.FACING); - Direction facingOff = facingOn.getOpposite(); - Vec3 unit; - if (state.getValue(LeverBlock.POWERED)) { - unit = Vec3.atLowerCornerOf(facingOn.getNormal()); - } else { - unit = Vec3.atLowerCornerOf(facingOff.getNormal()); - } - hitboxPos = hitboxPos.add(unit.multiply(0.25, 0.25, 0.25)); - } - double size = 0.25; - AABB hitbox = new AABB(hitboxPos.x - size, hitboxPos.y - size, hitboxPos.z - size, - hitboxPos.x + size, hitboxPos.y + size, hitboxPos.z + size); - if (hitbox.contains(handPos)) { - Vec3 last = info.getLastPos(c); - if (last.distanceToSqr(handPos) > 0.1*0.1) { // If our hand moved a significant amount - info.cooldown = 20; - LeverBlock leverBlock = (LeverBlock) Blocks.LEVER; - leverBlock.use(state, player.level(), leverPos, player, c == 0 ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND, - null); - VRRumble.rumbleIfVR(player, c, CommonConstants.vibrationTimeWorldInteraction); - } - } - info.setLastPos(c, handPos); - } - } - - @Override - protected boolean shouldTick(Player player) { - return ActiveConfig.FILE.useLever && - VRPluginVerify.hasAPI && VRPlugin.API.playerInVR(player) - && ActiveConfig.getConfigForPlayer(player).useLever; - } - - public static class LeverInfo { - public int cooldown = 0; - public Vec3 lastPosMain = Vec3.ZERO; - public Vec3 lastPosOff = Vec3.ZERO; - - public void tick() { - if (cooldown > 0) { - cooldown--; - } - } - - public Vec3 getLastPos(int c) { - return c == 0 ? lastPosMain : lastPosOff; - } - - public void setLastPos(int c, Vec3 pos) { - if (c == 0) { - lastPosMain = pos; - } else { - lastPosOff = pos; - } - } - } -} diff --git a/common/src/main/java/com/hammy275/immersivemc/server/tracker/ServerTrackerInit.java b/common/src/main/java/com/hammy275/immersivemc/server/tracker/ServerTrackerInit.java index bdfbf8b6..989aec53 100644 --- a/common/src/main/java/com/hammy275/immersivemc/server/tracker/ServerTrackerInit.java +++ b/common/src/main/java/com/hammy275/immersivemc/server/tracker/ServerTrackerInit.java @@ -15,7 +15,6 @@ public class ServerTrackerInit { public static final RangedGrabTrackerServer rangedGrabTracker = new RangedGrabTrackerServer(); public static final ButtonPushTracker buttonPushTracker = new ButtonPushTracker(); public static final CampfireTracker campfireTracker = new CampfireTracker(); - public static final LeverTracker leverTracker = new LeverTracker(); public static final DoorMoveTracker doorTracker = new DoorMoveTracker(); public static final PetTracker petTracker = new PetTracker();