diff --git a/common/src/main/java/org/vivecraft/api/VRAPI.java b/common/src/main/java/org/vivecraft/api/VRAPI.java new file mode 100644 index 000000000..da608f17a --- /dev/null +++ b/common/src/main/java/org/vivecraft/api/VRAPI.java @@ -0,0 +1,38 @@ +package org.vivecraft.api; + +import net.minecraft.world.entity.player.Player; +import org.vivecraft.api.data.VRPose; +import org.vivecraft.common.api_impl.VRAPIImpl; + +import javax.annotation.Nullable; + +/** + * The main interface for interacting with Vivecraft from common code. + */ +public interface VRAPI { + + /** + * @return The Vivecraft API instance for interacting with Vivecraft's common API. + */ + static VRAPI instance() { + return VRAPIImpl.INSTANCE; + } + + /** + * Check whether a given player is currently in VR. + * + * @param player The player to check the VR status of. + * @return true if the player is in VR. + */ + boolean isVRPlayer(Player player); + + /** + * Returns the VR pose for the given player. Will return null if the player isn't in VR, + * or if being called from the client and the client has yet to receive any data for the player. + * + * @param player Player to get the VR pose of. + * @return The VR pose for a player, or null if the player isn't in VR or no data has been received for said player. + */ + @Nullable + VRPose getVRPose(Player player); +} diff --git a/common/src/main/java/org/vivecraft/api/client/ItemInUseTracker.java b/common/src/main/java/org/vivecraft/api/client/ItemInUseTracker.java new file mode 100644 index 000000000..82b6b5277 --- /dev/null +++ b/common/src/main/java/org/vivecraft/api/client/ItemInUseTracker.java @@ -0,0 +1,20 @@ +package org.vivecraft.api.client; + +import net.minecraft.client.player.LocalPlayer; + +/** + * An interface that should be implemented by {@link Tracker}s if they want to take advantage of + * {@link #itemInUse(LocalPlayer)}. + */ +public interface ItemInUseTracker { + + /** + * Called for the client player, to check if this tracker is currently causing the item to be used to not release + * the use key. In other words, if you want the item currently being held to act as the use key being held, one + * should call the use item function, then return true from this method while the item should still remain used. + * @param player The local player which is running this tracker. + * @return Whether the item should remain in use. + */ + boolean itemInUse(LocalPlayer player); + +} diff --git a/common/src/main/java/org/vivecraft/api/client/Tracker.java b/common/src/main/java/org/vivecraft/api/client/Tracker.java new file mode 100644 index 000000000..968e2a55a --- /dev/null +++ b/common/src/main/java/org/vivecraft/api/client/Tracker.java @@ -0,0 +1,65 @@ +package org.vivecraft.api.client; + +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.entity.player.Player; +import org.vivecraft.api.VRAPI; + +/** + * A tracker is an object that is run for the local player during the game tick or before rendering a frame only if + * they are in VR. Using trackers are one of the cleanest ways to interact with Vivecraft's data; it's how Vivecraft + * itself does. Trackers should generally use {@link VRClientAPI#getPreTickWorldPose()}, as this provides + * the most up-to-date data, and other methods such as {@link VRClientAPI#getPostTickWorldPose()} or + * {@link VRAPI#getVRPose(Player)} may not have data available when the tracker is run. + */ +public interface Tracker { + + /** + * Whether the tracker is active for the local player. + * + * @param player Player being checked if they are active for this tracker instances. + * @return true if the tracker is active for the specified player. + */ + boolean isActive(LocalPlayer player); + + /** + * Called for the client player if this tracker is active, which is when {@link #isActive(LocalPlayer)} returns true. + * + * @param player Player to run this tracker for, which is the local player. + */ + void doProcess(LocalPlayer player); + + /** + * The ticking type for this tracker. + * If this is PER_FRAME, the tracker is called once with the local player per frame before the frame is rendered. + * If this is PER_TICK, the tracker is called once with the local player per game tick during the tick. + * + * @return The ticking type this tracker should use. + */ + TrackerTickType tickType(); + + /** + * Called to reset this tracker's state. This is called whenever {@link #isActive(LocalPlayer)} returns false. + * + * @param player The local player. + */ + default void reset(LocalPlayer player) { + + } + + /** + * Called for the local player, whether the tracker is active or not for them. This runs before + * {@link #isActive(LocalPlayer)} or {@link #reset(LocalPlayer)}. + * + * @param player Player to do an idle tick for, which is the local player. + */ + default void idleTick(LocalPlayer player) { + + } + + /** + * The timing type used for ticking trackers. + */ + enum TrackerTickType { + PER_FRAME, PER_TICK + } +} diff --git a/common/src/main/java/org/vivecraft/api/client/VRClientAPI.java b/common/src/main/java/org/vivecraft/api/client/VRClientAPI.java new file mode 100644 index 000000000..c1742cd68 --- /dev/null +++ b/common/src/main/java/org/vivecraft/api/client/VRClientAPI.java @@ -0,0 +1,164 @@ +package org.vivecraft.api.client; + +import org.vivecraft.api.client.data.VRPoseHistory; +import org.vivecraft.api.data.FBTMode; +import org.vivecraft.api.data.VRPose; +import org.vivecraft.client.api_impl.VRClientAPIImpl; + +import javax.annotation.Nullable; + +/** + * The main interface for interacting with Vivecraft from client code. For rendering, one should use + * {@link VRRenderingAPI}. + */ +public interface VRClientAPI { + + /** + * @return The Vivecraft API instance for interacting with Vivecraft's client API. + */ + static VRClientAPI instance() { + return VRClientAPIImpl.INSTANCE; + } + + /** + * Registers the tracker to the list of all trackers to be run for the local player. See the documentation for + * {@link Tracker} for more information on what a tracker is. + * + * @param tracker Tracker to register. + */ + void registerTracker(Tracker tracker); + + /** + * Gets the VR pose representing the player in the room after the most recent poll of VR hardware. + * + * @return The most up-to-date VR pose representing the player in the room, or null if the local player isn't in VR. + */ + @Nullable + VRPose getLatestRoomPose(); + + /** + * Gets the VR pose representing the player in the room after the game tick. + * Note that this pose is gathered AFTER mod loaders' post-tick events. + * + * @return The VR pose representing the player in the room post-tick, or null if the local player isn't in VR. + */ + @Nullable + VRPose getPostTickRoomPose(); + + /** + * Gets the VR pose representing the player in Minecraft world coordinates before the game tick. If you're unsure + * which {@link VRPose} method to use, you very likely want to use this one. + * Note that this pose is gathered BEFORE mod loaders' pre-tick events. + * + * @return The VR pose representing the player in world space pre-tick, or null if the local player isn't in VR. + */ + @Nullable + VRPose getPreTickWorldPose(); + + /** + * Gets the VR pose representing the player in Minecraft world coordinates after the game tick. + * This is the pose sent to the server, and also used to calculate the pose in {@link #getWorldRenderPose()}. + * Note that this pose is gathered AFTER mod loaders' post-tick events. + * + * @return The VR pose representing the player in Minecraft space post-tick, or null if the local player isn't in VR. + */ + @Nullable + VRPose getPostTickWorldPose(); + + /** + * Gets the VR pose representing the player in Minecraft world coordinates interpolated for rendering. + * + * @return The VR pose representing the player in Minecraft space post-tick interpolated for rendering, or null if + * the local player isn't in VR. + */ + @Nullable + VRPose getWorldRenderPose(); + + /** + * Causes a haptic pulse (vibration/rumble) for the specified controller. + * This function silently fails if called for players not in VR or players who are in seated mode. + * + * @param controllerNum The controller number to trigger a haptic pulse. 0 is the main-hand's controller, while 1 is + * the off-hand's controller. + * @param duration The duration of the haptic pulse in seconds. Note that this number is passed to the + * underlying VR API used by Vivecraft, and may act with a shorter length than expected beyond + * very short pulses. + * @param frequency The frequency of the haptic pulse in Hz. 160 is a safe bet for this number, with Vivecraft's codebase + * using anywhere from 160F for actions such as a bite on a fishing line, to 1000F for things such + * as a chat notification. + * @param amplitude The amplitude of the haptic pulse. This should be kept between 0F and 1F. + * @param delay An amount of time to delay until creating the haptic pulse. The majority of the time, one should use 0F here. + */ + void triggerHapticPulse(int controllerNum, float duration, float frequency, float amplitude, float delay); + + /** + * Causes a haptic pulse (vibration/rumble) for the specified controller. + * This function silently fails if called for players not in VR or players who are in seated mode. + * + * @param controllerNum The controller number to trigger a haptic pulse. 0 is the main-hand's controller, while 1 is + * the off-hand's controller. + * @param duration The duration of the haptic pulse in seconds. Note that this number is passed to the + * underlying VR API used by Vivecraft, and may act with a shorter length than expected beyond + * very short pulses. + */ + default void triggerHapticPulse(int controllerNum, float duration) { + triggerHapticPulse(controllerNum, duration, 160F, 1F, 0F); + } + + /** + * @return Whether the local player is currently in seated mode. + */ + boolean isSeated(); + + /** + * @return Whether the local player is playing with left-handed controls. + */ + boolean isLeftHanded(); + + /** + * @return The full-body tracking mode currently in-use or some default value if the local player is not in VR. + */ + FBTMode getFBTMode(); + + /** + * @return Whether VR support is initialized. + */ + boolean isVRInitialized(); + + /** + * @return Whether the client is actively in VR. + */ + boolean isVRActive(); + + /** + * @return The currently active world scale. + */ + float getWorldScale(); + + /** + * Requests the amount of ticks of history wanted for {@link #getHistoricalVRPoses()}. Any value larger than 200 + * will be capped at 200. + * @param maxTicksBack The maximum number of ticks of history wanted. + * @throws IllegalArgumentException If a non-positive number is supplied. + */ + void requestTicksOfHistory(int maxTicksBack) throws IllegalArgumentException; + + /** + * Returns the history of VR poses for the player. One should make one call to {@link #requestTicksOfHistory(int)} + * before calling this method to inform Vivecraft of the amount of history to keep. + * + * @return The history of VR poses for the player. Will be null if the player isn't in VR or if + * {@link #requestTicksOfHistory(int)} has yet to be called. + */ + @Nullable + VRPoseHistory getHistoricalVRPoses(); + + /** + * Opens or closes Vivecraft's keyboard. Will fail silently if the user isn't in VR or if the keyboard's new state + * is the same as the old. + * + * @param isNowOpen Whether the keyboard should now be open. If false, the keyboard will attempt to close. + * @return Whether the keyboard is currently showing after attempting to open/close it. + */ + boolean setKeyboardState(boolean isNowOpen); +} diff --git a/common/src/main/java/org/vivecraft/api/client/VRRenderingAPI.java b/common/src/main/java/org/vivecraft/api/client/VRRenderingAPI.java new file mode 100644 index 000000000..514f4dca0 --- /dev/null +++ b/common/src/main/java/org/vivecraft/api/client/VRRenderingAPI.java @@ -0,0 +1,56 @@ +package org.vivecraft.api.client; + +import com.google.common.annotations.Beta; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.phys.Vec3; +import org.vivecraft.client.api_impl.VRRenderingAPIImpl; +import org.vivecraft.client_vr.render.RenderPass; + +/** + * The main interface for interacting with Vivecraft from rendering code. For other client-side code, one should use + * {@link VRClientAPI}. + */ +public interface VRRenderingAPI { + + /** + * @return The Vivecraft API instance for interacting with Vivecraft's rendering API. + */ + static VRRenderingAPI instance() { + return VRRenderingAPIImpl.INSTANCE; + } + + /** + * @return Whether the current render pass is a vanilla render pass. + */ + boolean isVanillaRenderPass(); + + /** + * @return The current render pass Vivecraft is performing. + */ + RenderPass getCurrentRenderPass(); + + /** + * @return Whether the current render pass is the first one performed for this render cycle. + */ + boolean isFirstRenderPass(); + + /** + * Gets the position that the provided {@link InteractionHand} renders at. Unlike + * {@link org.vivecraft.api.data.VRPose#getHand(InteractionHand)} from {@link VRClientAPI#getWorldRenderPose()}, + * this returns a reasonable, default value for seated mode. + * @param hand The hand to get the rendering position of. + * @return The rendering position for the provided hand. + */ + @Beta + Vec3 getHandRenderPos(InteractionHand hand); + + /** + * Sets the provided {@link PoseStack} to render at the position of and with the rotation of the provided + * {@link InteractionHand}. + * @param hand The hand to set the PoseStack to. + * @param stack The PoseStack to be set. + */ + @Beta + void setupRenderingAtHand(InteractionHand hand, PoseStack stack); +} diff --git a/common/src/main/java/org/vivecraft/api/client/data/VRPoseHistory.java b/common/src/main/java/org/vivecraft/api/client/data/VRPoseHistory.java new file mode 100644 index 000000000..f8d07b699 --- /dev/null +++ b/common/src/main/java/org/vivecraft/api/client/data/VRPoseHistory.java @@ -0,0 +1,93 @@ +package org.vivecraft.api.client.data; + +import net.minecraft.world.phys.Vec3; +import org.vivecraft.api.data.VRBodyPart; +import org.vivecraft.api.data.VRPose; + +import javax.annotation.Nullable; +import java.util.List; + +public interface VRPoseHistory { + + /** + * @return The amount of ticks worth of history being held. The number returned by this method will never be higher + * than the largest valid value set by {@link org.vivecraft.api.client.VRClientAPI#requestTicksOfHistory(int)}, however + * can be lower than it. + */ + int ticksOfHistory(); + + /** + * Gets a raw list of {@link VRPose} instances, with index 0 representing the least recent pose known. + * + * @return The aforementioned list of {@link VRPose} instances. + */ + List getAllHistoricalData() throws IllegalArgumentException; + + /** + * Gets the pose history {@code ticksBack} ticks back. This will throw an IllegalStateException if the data cannot + * be retrieved due to not having enough history. + * + * @param ticksBack Ticks back to retrieve data. + * @return A {@link VRPose} instance from index ticks ago. + * @throws IllegalStateException If ticksBack references a tick that there is not yet data for. + * @throws IllegalArgumentException Thrown when maxTicksBack is larger than the largest valid value set by + * {@link org.vivecraft.api.client.VRClientAPI#requestTicksOfHistory(int)} or less than 0. + */ + VRPose getHistoricalData(int ticksBack) throws IllegalArgumentException, IllegalStateException; + + /** + * Gets the net movement between the most recent pose in this instance and the oldest position that can be + * retrieved, going no farther back than {@code maxTicksBack}. + * + * @param bodyPart The body part to get the net movement for. + * @param maxTicksBack The maximum amount of ticks back to compare the most recent data with. + * @return The aforementioned net movement. Note that this will return zero change on all axes if only zero ticks + * can be looked back. Will be null if the body part requested isn't available. + * @throws IllegalArgumentException Thrown when maxTicksBack is larger than the largest valid value set by + * {@link org.vivecraft.api.client.VRClientAPI#requestTicksOfHistory(int)} or less than 0. + */ + @Nullable + Vec3 netMovement(VRBodyPart bodyPart, int maxTicksBack) throws IllegalArgumentException; + + /** + * Gets the average velocity in blocks/tick between the most recent pose in this instance and the oldest position + * that can be retrieved, going no farther back than {@code maxTicksBack}. + * + * @param bodyPart The body part to get the average velocity for. + * @param maxTicksBack The maximum amount of ticks back to calculate velocity with. + * @return The aforementioned average velocity on each axis. Note that this will return zero velocity on all axes + * if only zero ticks can be looked back. Will be null if the body part requested isn't available. + * @throws IllegalArgumentException Thrown when maxTicksBack is larger than the largest valid value set by + * {@link org.vivecraft.api.client.VRClientAPI#requestTicksOfHistory(int)} or less than 0. + */ + @Nullable + Vec3 averageVelocity(VRBodyPart bodyPart, int maxTicksBack) throws IllegalArgumentException; + + /** + * Gets the average speed in blocks/tick between the most recent pose in this instance and the oldest position + * that can be retrieved, going no farther back than {@code maxTicksBack}. + * + * @param bodyPart The body part to get the average speed for. + * @param maxTicksBack The maximum amount of ticks back to calculate speed with. + * @return The aforementioned average speed on each axis. Note that this will return zero speed if only zero ticks + * can be looked back. Will be 0 if the body part requested isn't available. + * @throws IllegalArgumentException Thrown when maxTicksBack is larger than the largest valid value set by + * {@link org.vivecraft.api.client.VRClientAPI#requestTicksOfHistory(int)} or less than 0. + */ + double averageSpeed(VRBodyPart bodyPart, int maxTicksBack) throws IllegalArgumentException; + + /** + * Gets the average position between the most recent pose in this instance and the oldest position that can be + * retrieved, going no farther back than maxTicksBack. + * + * @param bodyPart The body part to get the average position for. + * @param maxTicksBack The maximum amount of ticks back to calculate velocity with. + * @return The aforementioned average position. Note that this will return the current position if only zero ticks + * can be looked back. Will be null if the body part requested isn't available. + * @throws IllegalArgumentException Thrown when maxTicksBack is larger than the largest valid value set by + * {@link org.vivecraft.api.client.VRClientAPI#requestTicksOfHistory(int)} or less than 0. + */ + @Nullable + Vec3 averagePosition(VRBodyPart bodyPart, int maxTicksBack) throws IllegalArgumentException; + +} diff --git a/common/src/main/java/org/vivecraft/api/data/FBTMode.java b/common/src/main/java/org/vivecraft/api/data/FBTMode.java new file mode 100644 index 000000000..6c37a54e6 --- /dev/null +++ b/common/src/main/java/org/vivecraft/api/data/FBTMode.java @@ -0,0 +1,28 @@ +package org.vivecraft.api.data; + +/** + * The mode used for full-body tracking, denoting which body parts are being tracked. + */ +public enum FBTMode { + /** + * Only HMD and hands are available. + */ + ARMS_ONLY, + /** + * HMD, hands, waist, and feet trackers are available. + */ + ARMS_LEGS, + /** + * HMD, hands, waist, feet, elbow, and knee trackers are available. + */ + WITH_JOINTS; + + /** + * Whether the provided body part is available in this full-body tracking mode. + * @param bodyPart The body part to see if data is available for in this mode. + * @return Whether the provided body part is available in this mode. + */ + public boolean bodyPartAvailable(VRBodyPart bodyPart) { + return bodyPart.availableInMode(this); + } +} diff --git a/common/src/main/java/org/vivecraft/common/network/BodyPart.java b/common/src/main/java/org/vivecraft/api/data/VRBodyPart.java similarity index 63% rename from common/src/main/java/org/vivecraft/common/network/BodyPart.java rename to common/src/main/java/org/vivecraft/api/data/VRBodyPart.java index 87f23f695..4963f96e4 100644 --- a/common/src/main/java/org/vivecraft/common/network/BodyPart.java +++ b/common/src/main/java/org/vivecraft/api/data/VRBodyPart.java @@ -1,6 +1,6 @@ -package org.vivecraft.common.network; +package org.vivecraft.api.data; -public enum BodyPart { +public enum VRBodyPart { MAIN_HAND, OFF_HAND, RIGHT_FOOT, @@ -9,12 +9,13 @@ public enum BodyPart { RIGHT_KNEE, LEFT_KNEE, RIGHT_ELBOW, - LEFT_ELBOW; + LEFT_ELBOW, + HMD; /** * @return the opposite limb */ - public BodyPart opposite() { + public VRBodyPart opposite() { return switch (this) { case MAIN_HAND -> OFF_HAND; case OFF_HAND -> MAIN_HAND; @@ -29,21 +30,28 @@ public BodyPart opposite() { } /** - * @param fbtMode FBT mode to check for - * @return if {@code this} limb is valid for the given FBT mode + * Whether this body part type is available in the provided full-body tracking mode. + * @param fbtMode The full-body tracking mode to check. + * @return Whether this body part has available data in the provided mode. */ - public boolean isValid(FBTMode fbtMode) { + public boolean availableInMode(FBTMode fbtMode) { return switch (this) { - case MAIN_HAND, OFF_HAND -> true; + case HMD, MAIN_HAND, OFF_HAND -> true; case RIGHT_FOOT, LEFT_FOOT, WAIST -> fbtMode != FBTMode.ARMS_ONLY; case RIGHT_KNEE, LEFT_KNEE, RIGHT_ELBOW, LEFT_ELBOW -> fbtMode == FBTMode.WITH_JOINTS; }; } + /** + * @return Whether this body part is a foot. + */ public boolean isFoot() { return this == RIGHT_FOOT || this == LEFT_FOOT; } + /** + * @return Whether this body part is a hand. + */ public boolean isHand() { return this == MAIN_HAND || this == OFF_HAND; } diff --git a/common/src/main/java/org/vivecraft/api/data/VRBodyPartData.java b/common/src/main/java/org/vivecraft/api/data/VRBodyPartData.java new file mode 100644 index 000000000..db45bd815 --- /dev/null +++ b/common/src/main/java/org/vivecraft/api/data/VRBodyPartData.java @@ -0,0 +1,41 @@ +package org.vivecraft.api.data; + +import net.minecraft.world.phys.Vec3; +import org.joml.Quaternionfc; + +/** + * Represents the data for a body part, or a device usually tied to a body part in VR, such as the HMD or a + * controller. + */ +public interface VRBodyPartData { + + /** + * @return The position of the body part in Minecraft world coordinates. + */ + Vec3 getPos(); + + /** + * @return The rotation of the body part. + */ + Vec3 getRot(); + + /** + * @return The pitch of the body part in radians. + */ + double getPitch(); + + /** + * @return The yaw of the body part in radians. + */ + double getYaw(); + + /** + * @return The roll of the body part in radians. + */ + double getRoll(); + + /** + * @return The quaternion representing the rotation of the body part. + */ + Quaternionfc getQuaternion(); +} diff --git a/common/src/main/java/org/vivecraft/api/data/VRPose.java b/common/src/main/java/org/vivecraft/api/data/VRPose.java new file mode 100644 index 000000000..1fa9ad10c --- /dev/null +++ b/common/src/main/java/org/vivecraft/api/data/VRPose.java @@ -0,0 +1,84 @@ +package org.vivecraft.api.data; + +import net.minecraft.world.InteractionHand; + +import javax.annotation.Nullable; + +/** + * Represents the pose of the VR player. In other words, the position and rotation data of all tracked body parts of + * the VR player. + */ +public interface VRPose { + + /** + * Gets the pose data for a body part. + * + * @param vrBodyPart The body part to get the pose data for. + * @return The specified body part's pose data, or null if that body part is not being tracked. + */ + @Nullable + VRBodyPartData getBodyPartData(VRBodyPart vrBodyPart); + + /** + * @return Body part pose data for the HMD. + */ + default VRBodyPartData getHMD() { + return getBodyPartData(VRBodyPart.HMD); + } + + /** + * Gets the body part data for a given hand. + * + * @param hand The hand number to get, with 0 being the main-hand and 1 being the off-hand. + * @return The specified hand's pose data. + */ + default VRBodyPartData getHand(int hand) { + if (hand != 0 && hand != 1) { + throw new IllegalArgumentException("Hand number must be 0 or 1."); + } + return hand == 0 ? getBodyPartData(VRBodyPart.MAIN_HAND) : getBodyPartData(VRBodyPart.OFF_HAND); + } + + /** + * @return Whether the player is currently in seated mode. + */ + boolean isSeated(); + + /** + * @return Whether the player is playing with left-handed controls. + */ + boolean isLeftHanded(); + + /** + * @return The full-body tracking mode currently in-use. + */ + FBTMode getFBTMode(); + + /** + * Gets the pose for a given hand. + * + * @param hand The interaction hand to get hand data for. + * @return The specified hand's pose data. + */ + default VRBodyPartData getHand(InteractionHand hand) { + return getHand(hand.ordinal()); + } + + /** + * Gets the pose for the main-hand. + * + * @return The main-hand's pose data. + */ + default VRBodyPartData getMainHand() { + return getHand(0); + } + + /** + * Gets the pose for the off-hand. + * + * @return The off-hand's pose data. + */ + default VRBodyPartData getOffHand() { + return getHand(1); + } +} diff --git a/common/src/main/java/org/vivecraft/api_beta/VivecraftAPI.java b/common/src/main/java/org/vivecraft/api_beta/VivecraftAPI.java deleted file mode 100644 index a95a3b0b8..000000000 --- a/common/src/main/java/org/vivecraft/api_beta/VivecraftAPI.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.vivecraft.api_beta; - -import com.google.common.annotations.Beta; -import net.minecraft.world.entity.player.Player; -import org.vivecraft.common.APIImpl; - -@Beta -public interface VivecraftAPI { - - static VivecraftAPI getInstance() { - return APIImpl.INSTANCE; - } - - @Beta - boolean isVRPlayer(Player player); -} diff --git a/common/src/main/java/org/vivecraft/api_beta/client/VivecraftClientAPI.java b/common/src/main/java/org/vivecraft/api_beta/client/VivecraftClientAPI.java deleted file mode 100644 index ff629beb4..000000000 --- a/common/src/main/java/org/vivecraft/api_beta/client/VivecraftClientAPI.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.vivecraft.api_beta.client; - -import com.google.common.annotations.Beta; -import org.vivecraft.client.ClientAPIImpl; - -public interface VivecraftClientAPI { - - static VivecraftClientAPI getInstance() { - return ClientAPIImpl.INSTANCE; - } - - boolean isVrInitialized(); - - boolean isVrActive(); - - @Beta - boolean isVanillaRenderPass(); -} diff --git a/common/src/main/java/org/vivecraft/client/ClientAPIImpl.java b/common/src/main/java/org/vivecraft/client/ClientAPIImpl.java deleted file mode 100644 index 27fdbd721..000000000 --- a/common/src/main/java/org/vivecraft/client/ClientAPIImpl.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.vivecraft.client; - -import org.vivecraft.api_beta.client.VivecraftClientAPI; -import org.vivecraft.client_vr.VRState; -import org.vivecraft.client_xr.render_pass.RenderPassType; - -public final class ClientAPIImpl implements VivecraftClientAPI { - - public static final ClientAPIImpl INSTANCE = new ClientAPIImpl(); - - private ClientAPIImpl() {} - - @Override - public boolean isVrInitialized() { - return VRState.VR_INITIALIZED; - } - - @Override - public boolean isVrActive() { - return VRState.VR_RUNNING; - } - - @Override - public boolean isVanillaRenderPass() { - return RenderPassType.isVanilla(); - } -} diff --git a/common/src/main/java/org/vivecraft/client/ClientVRPlayers.java b/common/src/main/java/org/vivecraft/client/ClientVRPlayers.java index 7843215de..fc67d16e8 100644 --- a/common/src/main/java/org/vivecraft/client/ClientVRPlayers.java +++ b/common/src/main/java/org/vivecraft/client/ClientVRPlayers.java @@ -13,6 +13,8 @@ import org.joml.Quaternionfc; import org.joml.Vector3f; import org.joml.Vector3fc; +import org.vivecraft.api.data.VRBodyPart; +import org.vivecraft.api.data.VRPose; import org.vivecraft.client.extensions.SparkParticleExtension; import org.vivecraft.client.utils.ClientUtils; import org.vivecraft.client.utils.ModelUtils; @@ -25,12 +27,16 @@ import org.vivecraft.client_vr.settings.AutoCalibration; import org.vivecraft.client_vr.settings.VRSettings; import org.vivecraft.client_xr.render_pass.RenderPassType; -import org.vivecraft.common.network.FBTMode; +import org.vivecraft.common.api_impl.data.VRPoseImpl; +import org.vivecraft.common.api_impl.data.VRBodyPartDataImpl; +import org.vivecraft.api.data.FBTMode; import org.vivecraft.common.network.VrPlayerState; import org.vivecraft.common.utils.MathUtils; import java.util.*; +import static org.vivecraft.common.utils.MathUtils.fromVector3fc; + public class ClientVRPlayers { private static ClientVRPlayers INSTANCE; @@ -545,6 +551,8 @@ public static class RotInfo { public Vector3fc leftElbowPos; public Quaternionfc leftElbowQuat; + private VRPose vrPose; + /** * IMPORTANT!!! when changing this, also change {@link VRData#getBodyYawRad()} */ @@ -567,5 +575,36 @@ public float getBodyYawRad() { } return (float) Math.atan2(-dir.x, dir.z); } + + public VRPose asVRPose(Vec3 playerPos) { + if (vrPose == null) { + vrPose = new VRPoseImpl( + makeBodyPartData(this.headPos, this.headRot, this.headQuat, playerPos), + makeBodyPartData(this.mainHandPos, this.mainHandRot, this.mainHandQuat, playerPos), + makeBodyPartData(this.offHandPos, this.offHandRot, this.offHandQuat, playerPos), + makeBodyPartData(this.rightFootPos, this.rightFootQuat, playerPos, this.fbtMode.bodyPartAvailable(VRBodyPart.RIGHT_FOOT)), + makeBodyPartData(this.leftFootPos, this.leftFootQuat, playerPos, this.fbtMode.bodyPartAvailable(VRBodyPart.LEFT_FOOT)), + makeBodyPartData(this.waistPos, this.waistQuat, playerPos, this.fbtMode.bodyPartAvailable(VRBodyPart.WAIST)), + makeBodyPartData(this.rightKneePos, this.rightKneeQuat, playerPos, this.fbtMode.bodyPartAvailable(VRBodyPart.RIGHT_KNEE)), + makeBodyPartData(this.leftKneePos, this.leftKneeQuat, playerPos, this.fbtMode.bodyPartAvailable(VRBodyPart.LEFT_KNEE)), + makeBodyPartData(this.rightElbowPos, this.rightElbowQuat, playerPos, this.fbtMode.bodyPartAvailable(VRBodyPart.RIGHT_ELBOW)), + makeBodyPartData(this.leftElbowPos, this.leftElbowQuat, playerPos, this.fbtMode.bodyPartAvailable(VRBodyPart.LEFT_ELBOW)), + this.seated, + this.leftHanded, + this.fbtMode + ); + } + return vrPose; + } + } + + private static VRBodyPartDataImpl makeBodyPartData(Vector3fc pos, Vector3fc rot, Quaternionfc quat, Vec3 playerPos) { + return new VRBodyPartDataImpl(fromVector3fc(pos).add(playerPos), fromVector3fc(rot), quat); + } + + private static VRBodyPartDataImpl makeBodyPartData(Vector3fc pos, Quaternionfc quat, Vec3 playerPos, boolean partAvailable) { + return partAvailable + ? makeBodyPartData(pos, quat.transform(MathUtils.BACK, new Vector3f()), quat, playerPos) + : null; } } diff --git a/common/src/main/java/org/vivecraft/client/api_impl/VRClientAPIImpl.java b/common/src/main/java/org/vivecraft/client/api_impl/VRClientAPIImpl.java new file mode 100644 index 000000000..85d26bcda --- /dev/null +++ b/common/src/main/java/org/vivecraft/client/api_impl/VRClientAPIImpl.java @@ -0,0 +1,170 @@ +package org.vivecraft.client.api_impl; + +import org.jetbrains.annotations.Nullable; +import org.vivecraft.api.client.Tracker; +import org.vivecraft.api.client.VRClientAPI; +import org.vivecraft.api.client.data.VRPoseHistory; +import org.vivecraft.api.data.FBTMode; +import org.vivecraft.api.data.VRPose; +import org.vivecraft.client.api_impl.data.VRPoseHistoryImpl; +import org.vivecraft.client_vr.ClientDataHolderVR; +import org.vivecraft.client_vr.VRState; +import org.vivecraft.client_vr.gameplay.screenhandlers.KeyboardHandler; +import org.vivecraft.client_vr.provider.ControllerType; + +public final class VRClientAPIImpl implements VRClientAPI { + + public static final VRClientAPIImpl INSTANCE = new VRClientAPIImpl(); + // If updated, should also update Javadocs in VRClientAPI + private static final int MAX_CONFIGURABLE_HISTORY_TICKS = 200; + + private final VRPoseHistoryImpl poseHistory = new VRPoseHistoryImpl(); + private int maxPoseHistorySize = 0; + + private VRClientAPIImpl() { + } + + public void clearPoseHistory() { + this.poseHistory.clear(); + } + + public void addPoseToHistory(VRPose pose) { + this.poseHistory.addPose(pose); + } + + public int maxPoseHistorySize() { + return this.maxPoseHistorySize; + } + + @Nullable + @Override + public VRPose getLatestRoomPose() { + if (!isVRActive()) { + return null; + } + return ClientDataHolderVR.getInstance().vrPlayer.vrdata_room_pre.asVRPose(); + } + + @Nullable + @Override + public VRPose getPostTickRoomPose() { + if (!isVRActive()) { + return null; + } + return ClientDataHolderVR.getInstance().vrPlayer.vrdata_room_post.asVRPose(); + } + + @Nullable + @Override + public VRPose getPreTickWorldPose() { + if (!isVRActive()) { + return null; + } + return ClientDataHolderVR.getInstance().vrPlayer.vrdata_world_pre.asVRPose(); + } + + @Nullable + @Override + public VRPose getPostTickWorldPose() { + if (!isVRActive()) { + return null; + } + return ClientDataHolderVR.getInstance().vrPlayer.vrdata_world_post.asVRPose(); + } + + @Nullable + @Override + public VRPose getWorldRenderPose() { + if (!isVRActive()) { + return null; + } + return ClientDataHolderVR.getInstance().vrPlayer.vrdata_world_render.asVRPose(); + } + + @Override + public void triggerHapticPulse(int controllerNum, float duration, float frequency, float amplitude, float delay) { + if (controllerNum != 0 && controllerNum != 1) { + throw new IllegalArgumentException("Can only trigger a haptic pulse for controllers 0 and 1."); + } + if (amplitude < 0F || amplitude > 1F) { + throw new IllegalArgumentException("The amplitude of a haptic pulse must be between 0 and 1."); + } + if (isVRActive() && !isSeated()) { + ClientDataHolderVR.getInstance().vr.triggerHapticPulse( + ControllerType.values()[controllerNum], + duration, + frequency, + amplitude, + delay + ); + } + } + + @Override + public boolean isSeated() { + return ClientDataHolderVR.getInstance().vrSettings.seated; + } + + @Override + public boolean isLeftHanded() { + return ClientDataHolderVR.getInstance().vrSettings.reverseHands; + } + + @Override + public FBTMode getFBTMode() { + // Need to check if VR is running, not just initialized, since the VR player is set after initialization + if (!isVRActive()) { + return FBTMode.ARMS_ONLY; + } + return ClientDataHolderVR.getInstance().vrPlayer.vrdata_world_pre.fbtMode; + } + + @Override + public boolean isVRInitialized() { + return VRState.VR_INITIALIZED; + } + + @Override + public boolean isVRActive() { + return VRState.VR_RUNNING; + } + + @Override + public float getWorldScale() { + if (isVRActive()) { + return ClientDataHolderVR.getInstance().vrPlayer.worldScale; + } else { + return 1f; + } + } + + @Override + public void requestTicksOfHistory(int maxTicksBack) throws IllegalArgumentException { + if (maxTicksBack <= 0) { + throw new IllegalArgumentException("Must call requestTicksOfHistory() with a positive number."); + } + this.maxPoseHistorySize = Math.max(this.maxPoseHistorySize, Math.min(maxTicksBack, MAX_CONFIGURABLE_HISTORY_TICKS)); + } + + @Override + @Nullable + public VRPoseHistory getHistoricalVRPoses() { + if (!isVRActive() || this.maxPoseHistorySize <= 0) { + return null; + } + return this.poseHistory; + } + + @Override + public void registerTracker(Tracker tracker) { + ClientDataHolderVR.getInstance().registerTracker(tracker); + } + + @Override + public boolean setKeyboardState(boolean isNowOpen) { + if (isVRActive()) { + return KeyboardHandler.setOverlayShowing(isNowOpen); + } + return false; + } +} diff --git a/common/src/main/java/org/vivecraft/client/api_impl/VRRenderingAPIImpl.java b/common/src/main/java/org/vivecraft/client/api_impl/VRRenderingAPIImpl.java new file mode 100644 index 000000000..68b49aee4 --- /dev/null +++ b/common/src/main/java/org/vivecraft/client/api_impl/VRRenderingAPIImpl.java @@ -0,0 +1,43 @@ +package org.vivecraft.client.api_impl; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.phys.Vec3; +import org.vivecraft.api.client.VRRenderingAPI; +import org.vivecraft.client_vr.ClientDataHolderVR; +import org.vivecraft.client_vr.render.RenderPass; +import org.vivecraft.client_vr.render.helpers.RenderHelper; +import org.vivecraft.client_xr.render_pass.RenderPassType; + +public class VRRenderingAPIImpl implements VRRenderingAPI { + + public static final VRRenderingAPIImpl INSTANCE = new VRRenderingAPIImpl(); + + private VRRenderingAPIImpl() { + } + + @Override + public boolean isVanillaRenderPass() { + return RenderPassType.isVanilla(); + } + + @Override + public RenderPass getCurrentRenderPass() { + return ClientDataHolderVR.getInstance().currentPass; + } + + @Override + public boolean isFirstRenderPass() { + return ClientDataHolderVR.getInstance().isFirstPass; + } + + @Override + public Vec3 getHandRenderPos(InteractionHand hand) { + return RenderHelper.getControllerRenderPos(hand.ordinal()); + } + + @Override + public void setupRenderingAtHand(InteractionHand hand, PoseStack stack) { + RenderHelper.setupRenderingAtController(hand.ordinal(), stack); + } +} diff --git a/common/src/main/java/org/vivecraft/client/api_impl/data/VRPoseHistoryImpl.java b/common/src/main/java/org/vivecraft/client/api_impl/data/VRPoseHistoryImpl.java new file mode 100644 index 000000000..e2845dcd1 --- /dev/null +++ b/common/src/main/java/org/vivecraft/client/api_impl/data/VRPoseHistoryImpl.java @@ -0,0 +1,174 @@ +package org.vivecraft.client.api_impl.data; + +import net.minecraft.world.phys.Vec3; +import org.vivecraft.api.client.VRClientAPI; +import org.vivecraft.api.client.data.VRPoseHistory; +import org.vivecraft.api.data.VRBodyPart; +import org.vivecraft.api.data.VRBodyPartData; +import org.vivecraft.api.data.VRPose; +import org.vivecraft.client.api_impl.VRClientAPIImpl; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.stream.DoubleStream; + +public class VRPoseHistoryImpl implements VRPoseHistory { + + // Holds historical VRPose data. The index into here is simply the number of ticks back that data is. + private final LinkedList dataQueue = new LinkedList<>(); + + public VRPoseHistoryImpl() { + } + + public void addPose(VRPose pose) { + this.dataQueue.addFirst(pose); + if (this.dataQueue.size() > maxTicksOfHistory() + 1) { + this.dataQueue.removeLast(); + } + } + + public void clear() { + this.dataQueue.clear(); + } + + @Override + public int ticksOfHistory() { + return this.dataQueue.size(); + } + + @Override + public List getAllHistoricalData() throws IllegalArgumentException { + return List.copyOf(this.dataQueue); + } + + @Override + public VRPose getHistoricalData(int ticksBack) throws IllegalArgumentException, IllegalStateException { + checkTicksBack(ticksBack); + if (this.dataQueue.size() <= ticksBack) { + throw new IllegalStateException("Cannot retrieve data from " + ticksBack + " ticks ago, when there is " + + "only data for up to " + (this.dataQueue.size() - 1) + " ticks ago."); + } + return this.dataQueue.get(ticksBack); + } + + @Override + public Vec3 netMovement(VRBodyPart bodyPart, int maxTicksBack) throws IllegalArgumentException { + checkPartNonNull(bodyPart); + checkTicksBack(maxTicksBack); + if (this.dataQueue.size() <= 1) { + return Vec3.ZERO; + } + VRBodyPartData currentData = this.dataQueue.getFirst().getBodyPartData(bodyPart); + if (currentData == null) { + return null; + } + Vec3 current = currentData.getPos(); + VRBodyPartData oldData = this.dataQueue.get(maxTicksBack).getBodyPartData(bodyPart); + if (oldData == null) { + return null; + } + Vec3 old = oldData.getPos(); + return current.subtract(old); + } + + @Override + public Vec3 averageVelocity(VRBodyPart bodyPart, int maxTicksBack) throws IllegalArgumentException { + checkPartNonNull(bodyPart); + checkTicksBack(maxTicksBack); + if (this.dataQueue.size() <= 1) { + return Vec3.ZERO; + } + maxTicksBack = getNumTicksBack(maxTicksBack); + List diffs = new ArrayList<>(maxTicksBack); + for (int i = 0; i < maxTicksBack; i++) { + VRBodyPartData newer = dataQueue.get(i).getBodyPartData(bodyPart); + VRBodyPartData older = dataQueue.get(i + 1).getBodyPartData(bodyPart); + if (newer == null || older == null) { + break; + } + diffs.add(newer.getPos().subtract(older.getPos())); + } + if (diffs.isEmpty()) { + // Return no change if the body part is available but no historical data or null if body part isn't + // available. + return dataQueue.getFirst().getBodyPartData(bodyPart) != null ? Vec3.ZERO : null; + } + return new Vec3( + diffs.stream().mapToDouble(vec -> vec.x).average().orElse(0), + diffs.stream().mapToDouble(vec -> vec.y).average().orElse(0), + diffs.stream().mapToDouble(vec -> vec.z).average().orElse(0) + ); + } + + @Override + public double averageSpeed(VRBodyPart bodyPart, int maxTicksBack) throws IllegalArgumentException { + checkPartNonNull(bodyPart); + checkTicksBack(maxTicksBack); + if (this.dataQueue.size() <= 1) { + return 0; + } + maxTicksBack = getNumTicksBack(maxTicksBack); + List speeds = new ArrayList<>(maxTicksBack); + for (int i = 0; i < maxTicksBack; i++) { + VRBodyPartData newer = dataQueue.get(i).getBodyPartData(bodyPart); + VRBodyPartData older = dataQueue.get(i + 1).getBodyPartData(bodyPart); + if (newer == null || older == null) { + break; + } + speeds.add(newer.getPos().distanceTo(older.getPos())); + } + return speeds.stream().mapToDouble(Double::valueOf).average().orElse(0); + } + + @Override + public Vec3 averagePosition(VRBodyPart bodyPart, int maxTicksBack) throws IllegalArgumentException { + checkPartNonNull(bodyPart); + checkTicksBack(maxTicksBack); + if (this.dataQueue.size() <= 1) { + return VRClientAPI.instance().getPreTickWorldPose().getBodyPartData(bodyPart).getPos(); + } + maxTicksBack = getNumTicksBack(maxTicksBack); + List positions = new ArrayList<>(maxTicksBack); + for (VRPose pose : dataQueue) { + VRBodyPartData data = pose.getBodyPartData(bodyPart); + if (data == null) { + break; + } + positions.add(data.getPos()); + } + if (positions.isEmpty()) { + return null; + } + return new Vec3( + positions.stream().mapToDouble(vec -> vec.x).average().orElse(0), + positions.stream().mapToDouble(vec -> vec.y).average().orElse(0), + positions.stream().mapToDouble(vec -> vec.z).average().orElse(0) + ); + } + + private void checkTicksBack(int ticksBack) { + if (ticksBack < 0 || ticksBack > maxTicksOfHistory()) { + throw new IllegalArgumentException("Value must be between 0 and " + maxTicksOfHistory() + "."); + } + } + + private void checkPartNonNull(VRBodyPart bodyPart) { + if (bodyPart == null) { + throw new IllegalArgumentException("Cannot get data for a null body part!"); + } + } + + private int getNumTicksBack(int maxTicksBack) { + if (this.dataQueue.size() <= maxTicksBack) { + return this.dataQueue.size() - 1; + } else { + return maxTicksBack; + } + } + + private int maxTicksOfHistory() { + return VRClientAPIImpl.INSTANCE.maxPoseHistorySize(); + } +} diff --git a/common/src/main/java/org/vivecraft/client/network/ClientNetworking.java b/common/src/main/java/org/vivecraft/client/network/ClientNetworking.java index d4d5ff9e0..547700f80 100644 --- a/common/src/main/java/org/vivecraft/client/network/ClientNetworking.java +++ b/common/src/main/java/org/vivecraft/client/network/ClientNetworking.java @@ -23,7 +23,7 @@ import org.vivecraft.common.CommonDataHolder; import org.vivecraft.common.VRServerPerms; import org.vivecraft.common.network.CommonNetworkHelper; -import org.vivecraft.common.network.BodyPart; +import org.vivecraft.api.data.VRBodyPart; import org.vivecraft.common.network.VrPlayerState; import org.vivecraft.common.network.packet.c2s.*; import org.vivecraft.common.network.packet.s2c.*; @@ -51,7 +51,7 @@ public class ClientNetworking { private static float CAPTURED_YAW; private static float CAPTURED_PITCH; private static boolean OVERRIDE_ACTIVE; - public static BodyPart LAST_SENT_BODY_PART = BodyPart.MAIN_HAND; + public static VRBodyPart LAST_SENT_BODY_PART = VRBodyPart.MAIN_HAND; public static boolean NEEDS_RESET = true; @@ -193,11 +193,11 @@ public static int getTeleportHorizLimit() { public static void sendActiveHand(InteractionHand hand) { if (SERVER_WANTS_DATA) { - sendActiveBodyPart(hand == InteractionHand.MAIN_HAND ? BodyPart.MAIN_HAND : BodyPart.OFF_HAND); + sendActiveBodyPart(hand == InteractionHand.MAIN_HAND ? VRBodyPart.MAIN_HAND : VRBodyPart.OFF_HAND); } } - public static void sendActiveBodyPart(BodyPart bodyPart) { + public static void sendActiveBodyPart(VRBodyPart bodyPart) { if (SERVER_WANTS_DATA) { // only send if the hand is different from last time, don't need to spam packets if (bodyPart != LAST_SENT_BODY_PART) { diff --git a/common/src/main/java/org/vivecraft/client/render/VRPlayerModel.java b/common/src/main/java/org/vivecraft/client/render/VRPlayerModel.java index 5714657a6..16a62d4fe 100644 --- a/common/src/main/java/org/vivecraft/client/render/VRPlayerModel.java +++ b/common/src/main/java/org/vivecraft/client/render/VRPlayerModel.java @@ -27,7 +27,7 @@ import org.vivecraft.client_vr.gameplay.screenhandlers.GuiHandler; import org.vivecraft.client_vr.render.RenderPass; import org.vivecraft.client_vr.settings.VRSettings; -import org.vivecraft.common.network.FBTMode; +import org.vivecraft.api.data.FBTMode; import org.vivecraft.common.utils.MathUtils; import org.vivecraft.mod_compat_vr.immersiveportals.ImmersivePortalsHelper; import org.vivecraft.mod_compat_vr.mca.MCAHelper; diff --git a/common/src/main/java/org/vivecraft/client/render/VRPlayerModel_WithArmsLegs.java b/common/src/main/java/org/vivecraft/client/render/VRPlayerModel_WithArmsLegs.java index 078a2faeb..3d557e9ef 100644 --- a/common/src/main/java/org/vivecraft/client/render/VRPlayerModel_WithArmsLegs.java +++ b/common/src/main/java/org/vivecraft/client/render/VRPlayerModel_WithArmsLegs.java @@ -17,7 +17,7 @@ import org.vivecraft.client.render.models.FeetModel; import org.vivecraft.client.utils.ModelUtils; import org.vivecraft.client_vr.ClientDataHolderVR; -import org.vivecraft.common.network.FBTMode; +import org.vivecraft.api.data.FBTMode; import org.vivecraft.common.utils.MathUtils; public class VRPlayerModel_WithArmsLegs extends VRPlayerModel_WithArms implements FeetModel { diff --git a/common/src/main/java/org/vivecraft/client_vr/ClientDataHolderVR.java b/common/src/main/java/org/vivecraft/client_vr/ClientDataHolderVR.java index 6bf9e57c7..59cc1a9d1 100644 --- a/common/src/main/java/org/vivecraft/client_vr/ClientDataHolderVR.java +++ b/common/src/main/java/org/vivecraft/client_vr/ClientDataHolderVR.java @@ -1,8 +1,11 @@ package org.vivecraft.client_vr; import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.world.entity.HumanoidArm; +import org.vivecraft.api.client.ItemInUseTracker; +import org.vivecraft.api.client.Tracker; import org.vivecraft.client_vr.gameplay.VRPlayer; import org.vivecraft.client_vr.gameplay.trackers.*; import org.vivecraft.client_vr.menuworlds.MenuWorldRenderer; @@ -42,6 +45,8 @@ public class ClientDataHolderVR { // list of all registered trackers private final List trackers = new ArrayList<>(); + // list of all trackers that control holding item usage + private final List itemInUseTrackers = new ArrayList<>(); // our trackers public final BackpackTracker backpackTracker = createTracker(BackpackTracker::new); @@ -86,6 +91,9 @@ public class ClientDataHolderVR { public boolean showedStencilMessage; public boolean showedFbtCalibrationNotification; + private ClientDataHolderVR() { + } + public static ClientDataHolderVR getInstance() { if (INSTANCE == null) { INSTANCE = new ClientDataHolderVR(); @@ -131,6 +139,9 @@ public void registerTracker(Tracker tracker) throws IllegalArgumentException { throw new IllegalArgumentException("Tracker is already added and should not be added again!"); } this.trackers.add(tracker); + if (tracker instanceof ItemInUseTracker itemInUseTracker) { + this.itemInUseTrackers.add(itemInUseTracker); + } } /** @@ -139,4 +150,12 @@ public void registerTracker(Tracker tracker) throws IllegalArgumentException { public List getTrackers() { return Collections.unmodifiableList(this.trackers); } + + /** + * @param player Current local player. + * @return Whether some tracker is currently using an item. + */ + public boolean isTrackerUsingItem(LocalPlayer player) { + return this.itemInUseTrackers.stream().anyMatch(tracker -> tracker.itemInUse(player)); + } } diff --git a/common/src/main/java/org/vivecraft/client_vr/VRData.java b/common/src/main/java/org/vivecraft/client_vr/VRData.java index 12135538d..94157e8df 100644 --- a/common/src/main/java/org/vivecraft/client_vr/VRData.java +++ b/common/src/main/java/org/vivecraft/client_vr/VRData.java @@ -4,16 +4,22 @@ import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; import org.joml.*; +import org.vivecraft.api.data.VRBodyPart; +import org.vivecraft.api.data.VRPose; import org.vivecraft.client.ClientVRPlayers; import org.vivecraft.client.gui.screens.FBTCalibrationScreen; import org.vivecraft.client_vr.provider.MCVR; import org.vivecraft.client_vr.render.RenderPass; import org.vivecraft.client_vr.settings.VRSettings; -import org.vivecraft.common.network.FBTMode; +import org.vivecraft.api.data.FBTMode; import org.vivecraft.common.utils.MathUtils; import javax.annotation.Nullable; import java.lang.Math; +import org.joml.Quaternionf; +import org.vivecraft.api.data.VRBodyPartData; +import org.vivecraft.common.api_impl.data.VRPoseImpl; +import org.vivecraft.common.api_impl.data.VRBodyPartDataImpl; public class VRData { // headset center @@ -60,6 +66,8 @@ public class VRData { // pose positions get scaled by that public float worldScale; + private VRPose vrPose; + public VRData(Vec3 origin, float walkMul, float worldScale, float rotation) { ClientDataHolderVR dataHolder = ClientDataHolderVR.getInstance(); MCVR mcVR = dataHolder.vr; @@ -358,6 +366,35 @@ public VRDevicePose getEye(RenderPass pass) { }; } + /** + * @return this data in a manner better-suited for the API + */ + public VRPose asVRPose() { + if (vrPose == null) { + vrPose = new VRPoseImpl( + this.hmd.asVRBodyPart(), + this.c0.asVRBodyPart(), + this.c1.asVRBodyPart(), + getDataIfAvailable(this.foot_right, this.fbtMode.bodyPartAvailable(VRBodyPart.RIGHT_FOOT)), + getDataIfAvailable(this.foot_left, this.fbtMode.bodyPartAvailable(VRBodyPart.LEFT_FOOT)), + getDataIfAvailable(this.waist, this.fbtMode.bodyPartAvailable(VRBodyPart.WAIST)), + getDataIfAvailable(this.knee_right, this.fbtMode.bodyPartAvailable(VRBodyPart.RIGHT_KNEE)), + getDataIfAvailable(this.knee_left, this.fbtMode.bodyPartAvailable(VRBodyPart.LEFT_KNEE)), + getDataIfAvailable(this.elbow_right, this.fbtMode.bodyPartAvailable(VRBodyPart.RIGHT_ELBOW)), + getDataIfAvailable(this.elbow_left, this.fbtMode.bodyPartAvailable(VRBodyPart.LEFT_ELBOW)), + ClientDataHolderVR.getInstance().vrSettings.seated, + ClientDataHolderVR.getInstance().vrSettings.reverseHands, + this.fbtMode + ); + } + return vrPose; + } + + @Nullable + private static VRBodyPartData getDataIfAvailable(VRDevicePose pose, boolean partAvailable) { + return partAvailable ? pose.asVRBodyPart() : null; + } + @Override public String toString() { return """ @@ -502,6 +539,14 @@ public Matrix4f getMatrix() { return new Matrix4f().rotationY(VRData.this.rotation_radians).mul(this.matrix); } + public VRBodyPartData asVRBodyPart() { + return new VRBodyPartDataImpl( + getPosition(), + new Vec3(getDirection()), + new Quaternionf().setFromUnnormalized(getMatrix()) + ); + } + @Override public String toString() { return "Device: pos:" + this.getPosition() + ", dir: " + this.getDirection(); diff --git a/common/src/main/java/org/vivecraft/client_vr/VRState.java b/common/src/main/java/org/vivecraft/client_vr/VRState.java index c5fa9212a..ef104e825 100644 --- a/common/src/main/java/org/vivecraft/client_vr/VRState.java +++ b/common/src/main/java/org/vivecraft/client_vr/VRState.java @@ -8,7 +8,7 @@ import org.vivecraft.client.gui.screens.GarbageCollectorScreen; import org.vivecraft.client.utils.TextUtils; import org.vivecraft.client_vr.gameplay.VRPlayer; -import org.vivecraft.client_vr.gameplay.trackers.Tracker; +import org.vivecraft.client.api_impl.VRClientAPIImpl; import org.vivecraft.client_vr.menuworlds.MenuWorldRenderer; import org.vivecraft.client_vr.provider.nullvr.NullVR; import org.vivecraft.client_vr.provider.openvr_lwjgl.MCOpenVR; @@ -70,9 +70,6 @@ public static void initializeVR() { RenderPassManager.setVanillaRenderPass(); dh.vrPlayer = new VRPlayer(); - for (Tracker t : dh.getTrackers()) { - dh.vrPlayer.registerTracker(t); - } dh.menuWorldRenderer = new MenuWorldRenderer(); @@ -156,5 +153,6 @@ public static void destroyVR(boolean disableVRSetting) { // this reloads any PostChain, at least in vanilla Minecraft.getInstance().levelRenderer.onResourceManagerReload(Minecraft.getInstance().getResourceManager()); } + VRClientAPIImpl.INSTANCE.clearPoseHistory(); } } diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/VRPlayer.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/VRPlayer.java index 0df4478c2..9bf0363bf 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/VRPlayer.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/VRPlayer.java @@ -23,11 +23,14 @@ import net.minecraft.world.phys.Vec3; import org.joml.Vector3f; import org.joml.Vector3fc; +import org.vivecraft.api.client.Tracker; import org.vivecraft.client.VivecraftVRMod; +import org.vivecraft.client.api_impl.VRClientAPIImpl; +import org.vivecraft.client_vr.ClientDataHolderVR; +import org.vivecraft.common.VRServerPerms; import org.vivecraft.client.network.ClientNetworking; import org.vivecraft.client.utils.ClientUtils; import org.vivecraft.client.utils.ScaleHelper; -import org.vivecraft.client_vr.ClientDataHolderVR; import org.vivecraft.client_vr.MethodHolder; import org.vivecraft.client_vr.VRData; import org.vivecraft.client_vr.extensions.GameRendererExtension; @@ -35,16 +38,12 @@ import org.vivecraft.client_vr.gameplay.screenhandlers.GuiHandler; import org.vivecraft.client_vr.gameplay.screenhandlers.KeyboardHandler; import org.vivecraft.client_vr.gameplay.screenhandlers.RadialHandler; -import org.vivecraft.client_vr.gameplay.trackers.Tracker; import org.vivecraft.client_vr.gameplay.trackers.VehicleTracker; import org.vivecraft.client_vr.render.RenderPass; import org.vivecraft.client_vr.settings.VRSettings; -import org.vivecraft.common.VRServerPerms; import org.vivecraft.common.utils.MathUtils; import org.vivecraft.data.ItemTags; -import java.util.ArrayList; - public class VRPlayer { private final Minecraft mc = Minecraft.getInstance(); private final ClientDataHolderVR dh = ClientDataHolderVR.getInstance(); @@ -62,8 +61,6 @@ public class VRPlayer { public VRData vrdata_world_post; // interpolate here between post and pre public VRData vrdata_world_render; - - private final ArrayList trackers = new ArrayList<>(); public float worldScale = this.dh.vrSettings.overrides.getSetting(VRSettings.VrOptions.WORLD_SCALE).getFloat(); private float rawWorldScale = this.dh.vrSettings.overrides.getSetting(VRSettings.VrOptions.WORLD_SCALE).getFloat(); private boolean teleportOverride = false; @@ -83,10 +80,6 @@ public class VRPlayer { private boolean initDone = false; public boolean onTick; - public void registerTracker(Tracker tracker) { - this.trackers.add(tracker); - } - public VRPlayer() { this.vrdata_room_pre = new VRData( Vec3.ZERO, @@ -243,6 +236,12 @@ public void preTick() { if (this.dh.vrSettings.seated && !MethodHolder.isInMenuRoom()) { this.dh.vrSettings.worldRotation = this.dh.vr.seatedRot; } + + // Gather VRPose history if some API consumer wants it and we're in a non-paused world. + if (VRClientAPIImpl.INSTANCE.maxPoseHistorySize() > 0 && this.mc.level != null && + (this.mc.getSingleplayerServer() == null || !this.mc.getSingleplayerServer().isPaused())) { + VRClientAPIImpl.INSTANCE.addPoseToHistory(this.vrdata_world_pre.asVRPose()); + } } public void postTick() { @@ -306,11 +305,9 @@ public void preRender(float partialTick) { interpolatedWorldScale, interpolatedWorldRotation_Radians); - // handle special items - for (Tracker tracker : this.trackers) { - if (tracker.getEntryPoint() == Tracker.EntryPoint.SPECIAL_ITEMS) { + for (Tracker tracker : ClientDataHolderVR.getInstance().getTrackers()) { + if (tracker.tickType() == Tracker.TrackerTickType.PER_FRAME) { tracker.idleTick(this.mc.player); - if (tracker.isActive(this.mc.player)) { tracker.doProcess(this.mc.player); } else { @@ -405,11 +402,11 @@ public void tick(LocalPlayer player) { } this.doPlayerMoveInRoom(player); - - for (Tracker tracker : this.trackers) { - if (tracker.getEntryPoint() == Tracker.EntryPoint.LIVING_UPDATE) { + for (Tracker tracker : dh.getTrackers()) + { + if (tracker.tickType() == Tracker.TrackerTickType.PER_TICK) + { tracker.idleTick(player); - if (tracker.isActive(player)) { tracker.doProcess(player); } else { @@ -439,10 +436,6 @@ public void tick(LocalPlayer player) { } } - public boolean isTrackerUsingItem(LocalPlayer player) { - return this.trackers.stream().anyMatch(tracker -> tracker.itemInUse(player)); - } - public void doPlayerMoveInRoom(LocalPlayer player) { if (this.roomScaleMovementDelay > 0) { diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/BackpackTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/BackpackTracker.java index ef3abfdd7..f47f7fb42 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/BackpackTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/BackpackTracker.java @@ -1,5 +1,9 @@ package org.vivecraft.client_vr.gameplay.trackers; +import org.vivecraft.api.client.Tracker; +import org.vivecraft.client_vr.ClientDataHolderVR; +import org.vivecraft.client_vr.gameplay.VRPlayer; + import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; @@ -7,16 +11,17 @@ import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.world.phys.Vec3; import org.joml.Vector3f; -import org.vivecraft.client_vr.ClientDataHolderVR; -import org.vivecraft.client_vr.gameplay.VRPlayer; import org.vivecraft.common.utils.MathUtils; -public class BackpackTracker extends Tracker { +public class BackpackTracker implements Tracker { public boolean[] wasIn = new boolean[2]; public int previousSlot = 0; + protected Minecraft mc; + protected ClientDataHolderVR dh; public BackpackTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } @Override @@ -98,4 +103,9 @@ public void doProcess(LocalPlayer player) { } } } + + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } } diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/BowTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/BowTracker.java index 82479e71d..f761bdaa7 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/BowTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/BowTracker.java @@ -1,5 +1,13 @@ package org.vivecraft.client_vr.gameplay.trackers; +import org.vivecraft.api.client.ItemInUseTracker; +import org.vivecraft.api.client.Tracker; +import org.vivecraft.client_vr.ClientDataHolderVR; +import org.vivecraft.client_vr.extensions.PlayerExtension; +import org.vivecraft.client.network.ClientNetworking; +import org.vivecraft.client_vr.VRData; +import org.vivecraft.client_vr.settings.VRSettings; + import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; @@ -24,7 +32,7 @@ import org.vivecraft.common.network.packet.c2s.DrawPayloadC2S; import org.vivecraft.common.utils.MathUtils; -public class BowTracker extends Tracker { +public class BowTracker implements Tracker, ItemInUseTracker { private static final long MAX_DRAW_MILLIS = 1100L; private static final double NOTCH_DOT_THRESHOLD = 20F; @@ -43,8 +51,12 @@ public class BowTracker extends Tracker { private int hapCounter = 0; private int lastHapStep = 0; + protected Minecraft mc; + protected ClientDataHolderVR dh; + public BowTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } public Vector3fc getAimVector() { @@ -110,11 +122,6 @@ public void reset(LocalPlayer player) { this.canDraw = false; } - @Override - public EntryPoint getEntryPoint() { - return EntryPoint.SPECIAL_ITEMS; - } - @Override public void doProcess(LocalPlayer player) { VRData vrData = this.dh.vrPlayer.getVRDataWorld(); @@ -286,4 +293,9 @@ public void doProcess(LocalPlayer player) { } } } + + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_FRAME; + } } diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/CameraTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/CameraTracker.java index 5ea91f5d5..58782d845 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/CameraTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/CameraTracker.java @@ -1,19 +1,21 @@ package org.vivecraft.client_vr.gameplay.trackers; +import org.joml.Matrix4f; +import org.vivecraft.api.client.Tracker; +import org.vivecraft.client_vr.ClientDataHolderVR; +import org.vivecraft.client_vr.VRData; +import org.vivecraft.client_vr.render.RenderPass; + import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.world.phys.Vec3; -import org.joml.Matrix4f; import org.joml.Matrix4fc; import org.joml.Quaternionf; import org.joml.Vector3f; -import org.vivecraft.client_vr.ClientDataHolderVR; -import org.vivecraft.client_vr.VRData; -import org.vivecraft.client_vr.render.RenderPass; import org.vivecraft.common.utils.MathUtils; -public class CameraTracker extends Tracker { +public class CameraTracker implements Tracker { public static final ModelResourceLocation CAMERA_MODEL = new ModelResourceLocation("vivecraft", "camera", ""); public static final ModelResourceLocation CAMERA_DISPLAY_MODEL = new ModelResourceLocation("vivecraft", "camera_display", ""); @@ -27,9 +29,12 @@ public class CameraTracker extends Tracker { private Vec3 startPosition; private Quaternionf startRotation; private boolean quickMode; + protected Minecraft mc; + protected ClientDataHolderVR dh; public CameraTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } @Override @@ -76,17 +81,16 @@ public void doProcess(LocalPlayer player) { } @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_FRAME; + } + public void reset(LocalPlayer player) { this.visible = false; this.quickMode = false; this.stopMoving(); } - @Override - public EntryPoint getEntryPoint() { - return EntryPoint.SPECIAL_ITEMS; // smoother camera movement - } - public boolean isVisible() { return this.visible; } diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/ClimbTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/ClimbTracker.java index 87c35d345..38ff5e1bd 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/ClimbTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/ClimbTracker.java @@ -1,11 +1,25 @@ package org.vivecraft.client_vr.gameplay.trackers; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; + +import net.minecraft.network.chat.contents.TranslatableContents; +import org.vivecraft.api.client.Tracker; +import org.vivecraft.client.VivecraftVRMod; +import org.vivecraft.client_vr.ClientDataHolderVR; +import org.vivecraft.client_vr.extensions.PlayerExtension; +import org.vivecraft.client.network.ClientNetworking; +import org.vivecraft.client_vr.gameplay.VRPlayer; +import org.vivecraft.client_vr.provider.ControllerType; + import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.network.chat.contents.TranslatableContents; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; @@ -17,23 +31,15 @@ import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; import org.joml.Vector3f; -import org.vivecraft.client.VivecraftVRMod; -import org.vivecraft.client.network.ClientNetworking; -import org.vivecraft.client_vr.ClientDataHolderVR; -import org.vivecraft.client_vr.extensions.PlayerExtension; -import org.vivecraft.client_vr.gameplay.VRPlayer; -import org.vivecraft.client_vr.provider.ControllerType; import org.vivecraft.common.network.packet.c2s.ClimbingPayloadC2S; import org.vivecraft.data.BlockTags; import org.vivecraft.server.config.ClimbeyBlockmode; import java.util.*; -public class ClimbTracker extends Tracker { +public class ClimbTracker implements Tracker { public static final ModelResourceLocation CLAWS_MODEL = new ModelResourceLocation("vivecraft", "climb_claws", - "inventory"); - - public Set blocklist = new HashSet<>(); + "inventory"); public Set blocklist = new HashSet<>(); public ClimbeyBlockmode serverBlockmode = ClimbeyBlockmode.DISABLED; public boolean forceActivate = false; public int latchStartController = -1; @@ -60,9 +66,12 @@ public class ClimbTracker extends Tracker { private final AABB fullBB = new AABB(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D); private final Random rand = new Random(); private boolean unsetFlag; + protected Minecraft mc; + protected ClientDataHolderVR dh; public ClimbTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } public boolean isGrabbingLadder() { @@ -662,6 +671,11 @@ private boolean isClimbableTrapdoor(Level level, BlockPos blockPos, BlockState b return false; } + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } + private boolean allowed(BlockState bs) { return switch (this.serverBlockmode) { case DISABLED -> true; diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/CrawlTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/CrawlTracker.java index ffca80d4b..199d708dd 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/CrawlTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/CrawlTracker.java @@ -1,21 +1,26 @@ package org.vivecraft.client_vr.gameplay.trackers; +import org.vivecraft.api.client.Tracker; +import org.vivecraft.client_vr.ClientDataHolderVR; +import org.vivecraft.client.network.ClientNetworking; + import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.entity.Pose; -import org.vivecraft.client.network.ClientNetworking; import org.vivecraft.client.utils.ClientUtils; import org.vivecraft.client.utils.ScaleHelper; -import org.vivecraft.client_vr.ClientDataHolderVR; import org.vivecraft.common.network.packet.c2s.CrawlPayloadC2S; -public class CrawlTracker extends Tracker { +public class CrawlTracker implements Tracker { private boolean wasCrawling; public boolean crawling; public boolean crawlsteresis; + protected Minecraft mc; + protected ClientDataHolderVR dh; public CrawlTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } @Override @@ -53,6 +58,11 @@ public void doProcess(LocalPlayer player) { this.updateState(player); } + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } + private void updateState(LocalPlayer player) { if (this.crawling != this.wasCrawling) { if (this.crawling) { diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/EatingTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/EatingTracker.java index fd2f284ab..dedd85bc9 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/EatingTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/EatingTracker.java @@ -1,5 +1,9 @@ package org.vivecraft.client_vr.gameplay.trackers; +import org.vivecraft.api.client.ItemInUseTracker; +import org.vivecraft.api.client.Tracker; +import org.vivecraft.client_vr.ClientDataHolderVR; + import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; @@ -7,11 +11,10 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.UseAnim; import org.joml.Vector3f; -import org.vivecraft.client_vr.ClientDataHolderVR; -import org.vivecraft.client_vr.VRData; import org.vivecraft.common.utils.MathUtils; +import org.vivecraft.client_vr.VRData; -public class EatingTracker extends Tracker { +public class EatingTracker implements Tracker, ItemInUseTracker { private static final float MOUTH_TO_EYE_DISTANCE = 0.0F; private static final float THRESHOLD = 0.25F; private static final long EAT_TIME = 2100L; @@ -19,8 +22,12 @@ public class EatingTracker extends Tracker { private final boolean[] eating = new boolean[2]; private long eatStart; + protected Minecraft mc; + protected ClientDataHolderVR dh; + public EatingTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } @Override @@ -121,4 +128,9 @@ public void doProcess(LocalPlayer player) { } } } + + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } } diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/HorseTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/HorseTracker.java index 7594a6cde..bea1b0416 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/HorseTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/HorseTracker.java @@ -1,17 +1,19 @@ package org.vivecraft.client_vr.gameplay.trackers; +import org.vivecraft.api.client.Tracker; +import org.vivecraft.client_vr.ClientDataHolderVR; +import org.vivecraft.client_vr.gameplay.VRPlayer; +import org.vivecraft.client_vr.settings.VRSettings; + import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.entity.animal.horse.AbstractHorse; import net.minecraft.world.entity.animal.horse.Horse; import net.minecraft.world.phys.Vec3; import org.joml.Vector3f; -import org.vivecraft.client_vr.ClientDataHolderVR; -import org.vivecraft.client_vr.gameplay.VRPlayer; -import org.vivecraft.client_vr.settings.VRSettings; import org.vivecraft.common.utils.MathUtils; -public class HorseTracker extends Tracker { +public class HorseTracker implements Tracker { private static final double BOOST_TRIGGER = 1.4D; private static final double PULL_TRIGGER = 0.8D; private static final int MAX_SPEED_LEVEL = 3; @@ -25,8 +27,12 @@ public class HorseTracker extends Tracker { private Horse horse = null; private final ModelInfo info = new ModelInfo(); + protected Minecraft mc; + protected ClientDataHolderVR dh; + public HorseTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } @Override @@ -123,6 +129,11 @@ public void doProcess(LocalPlayer player) { this.horse.setDeltaMovement(movement.x, this.horse.getDeltaMovement().y, movement.z); } + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } + private boolean doBoost() { if (this.speedLevel >= MAX_SPEED_LEVEL) { return false; diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/InteractTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/InteractTracker.java index 714de2b00..7bc691cc7 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/InteractTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/InteractTracker.java @@ -1,5 +1,19 @@ package org.vivecraft.client_vr.gameplay.trackers; +import java.util.HashSet; + +import org.vivecraft.api.client.Tracker; +import org.vivecraft.client.VivecraftVRMod; +import org.vivecraft.client_vr.ClientDataHolderVR; +import org.vivecraft.client.Xplat; +import org.vivecraft.client_vr.extensions.PlayerExtension; +import org.vivecraft.client_vr.VRData; +import org.vivecraft.client_vr.provider.ControllerType; +import org.vivecraft.client_vr.settings.VRHotkeys; +import org.vivecraft.client_vr.settings.VRSettings; +import org.vivecraft.client_vr.render.RenderPass; +import org.vivecraft.client_vr.render.VRFirstPersonArmSwing; + import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; @@ -35,7 +49,7 @@ import java.util.HashSet; -public class InteractTracker extends Tracker { +public class InteractTracker implements Tracker { // indicates when a hand has a bucket and is in a liquid public boolean[] bukkit = new boolean[2]; @@ -62,8 +76,12 @@ public class InteractTracker extends Tracker { // a set of blocks that can be interacted with private HashSet> rightClickable = null; + protected Minecraft mc; + protected ClientDataHolderVR dh; + public InteractTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } @Override @@ -274,6 +292,11 @@ private void addIfClassHasMethod(String name, Class oclass) { } } + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } + public boolean isInteractActive(int controller) { return this.active[controller]; } diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/JumpTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/JumpTracker.java index c7de2baeb..b266aa296 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/JumpTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/JumpTracker.java @@ -1,8 +1,14 @@ package org.vivecraft.client_vr.gameplay.trackers; +import net.minecraft.network.chat.contents.TranslatableContents; +import org.vivecraft.api.client.Tracker; +import org.vivecraft.client.VivecraftVRMod; +import org.vivecraft.client_vr.ClientDataHolderVR; +import org.vivecraft.client.network.ClientNetworking; +import org.vivecraft.client_vr.settings.AutoCalibration; + import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; -import net.minecraft.network.chat.contents.TranslatableContents; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Player; @@ -10,13 +16,9 @@ import net.minecraft.world.item.Items; import net.minecraft.world.phys.Vec3; import org.joml.Vector3f; -import org.vivecraft.client.VivecraftVRMod; -import org.vivecraft.client.network.ClientNetworking; -import org.vivecraft.client_vr.ClientDataHolderVR; -import org.vivecraft.client_vr.settings.AutoCalibration; import org.vivecraft.client_vr.settings.VRSettings; -public class JumpTracker extends Tracker { +public class JumpTracker implements Tracker { // in room space public Vector3f[] latchStart = new Vector3f[]{new Vector3f(), new Vector3f()}; @@ -25,9 +27,12 @@ public class JumpTracker extends Tracker { public Vec3[] latchStartPlayer = new Vec3[]{Vec3.ZERO, Vec3.ZERO}; private boolean c0Latched = false; private boolean c1Latched = false; + protected Minecraft mc; + protected ClientDataHolderVR dh; public JumpTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } /** @@ -223,4 +228,9 @@ public void doProcess(LocalPlayer player) { player.jumpFromGround(); } } + + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } } diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/RowTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/RowTracker.java index bf42eafda..bbdfe79f2 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/RowTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/RowTracker.java @@ -1,5 +1,8 @@ package org.vivecraft.client_vr.gameplay.trackers; +import org.vivecraft.api.client.Tracker; +import org.vivecraft.client_vr.ClientDataHolderVR; + import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; @@ -8,10 +11,9 @@ import net.minecraft.world.phys.Vec3; import org.joml.Quaternionf; import org.joml.Vector3f; -import org.vivecraft.client_vr.ClientDataHolderVR; import org.vivecraft.common.utils.MathUtils; -public class RowTracker extends Tracker { +public class RowTracker implements Tracker { private static final double TRANSMISSION_EFFICIENCY = 0.9D; public double[] forces = new double[]{0.0D, 0.0D}; @@ -21,11 +23,14 @@ public class RowTracker extends Tracker { private final Vec3[] lastUWPs = new Vec3[2]; + protected Minecraft mc; + protected ClientDataHolderVR dh; + public RowTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } - @Override public boolean isActive(LocalPlayer player) { if (this.dh.vrSettings.seated) { return false; @@ -83,6 +88,11 @@ public void doProcess(LocalPlayer player) { // TODO: Backwards paddlin' } + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } + public void doProcessFinaltransmithastofixthis(LocalPlayer player) { Boat boat = (Boat) player.getVehicle(); Quaternionf boatRot = new Quaternionf().rotationYXZ( diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/RunTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/RunTracker.java index cae9ac70c..198bc5846 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/RunTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/RunTracker.java @@ -1,17 +1,22 @@ package org.vivecraft.client_vr.gameplay.trackers; +import org.vivecraft.api.client.Tracker; +import org.vivecraft.client_vr.ClientDataHolderVR; +import org.vivecraft.client_vr.settings.VRSettings; + import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import org.joml.Vector3f; -import org.vivecraft.client_vr.ClientDataHolderVR; -import org.vivecraft.client_vr.settings.VRSettings; -public class RunTracker extends Tracker { +public class RunTracker implements Tracker { private double direction = 0.0D; private float speed = 0.0F; + protected Minecraft mc; + protected ClientDataHolderVR dh; public RunTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } @Override @@ -86,4 +91,9 @@ public void doProcess(LocalPlayer player) { this.speed = 1.3F; } } + + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } } diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/SneakTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/SneakTracker.java index 0822299b7..b3372f943 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/SneakTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/SneakTracker.java @@ -1,16 +1,21 @@ package org.vivecraft.client_vr.gameplay.trackers; -import net.minecraft.client.Minecraft; -import net.minecraft.client.player.LocalPlayer; +import org.vivecraft.api.client.Tracker; import org.vivecraft.client_vr.ClientDataHolderVR; import org.vivecraft.client_vr.settings.AutoCalibration; -public class SneakTracker extends Tracker { +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; + +public class SneakTracker implements Tracker { public boolean sneakOverride = false; public int sneakCounter = 0; + protected Minecraft mc; + protected ClientDataHolderVR dh; public SneakTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } @Override @@ -44,4 +49,9 @@ public void doProcess(LocalPlayer player) { this.sneakOverride = AutoCalibration.getPlayerHeight() - this.dh.vr.hmdPivotHistory.latest().y() > this.dh.vrSettings.sneakThreshold; } + + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } } diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/SwimTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/SwimTracker.java index 3df513d1c..5f09e698f 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/SwimTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/SwimTracker.java @@ -1,22 +1,27 @@ package org.vivecraft.client_vr.gameplay.trackers; +import org.vivecraft.api.client.Tracker; +import org.vivecraft.client_vr.ClientDataHolderVR; + import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.phys.Vec3; import org.joml.Vector3f; -import org.vivecraft.client_vr.ClientDataHolderVR; import org.vivecraft.common.utils.MathUtils; -public class SwimTracker extends Tracker { +public class SwimTracker implements Tracker { private static final float FRICTION = 0.9F; private static final float RISE_SPEED = 0.005F; private static final float SWIM_SPEED = 1.3F; private Vector3f motion = new Vector3f(); private double lastDist; + protected Minecraft mc; + protected ClientDataHolderVR dh; public SwimTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } @Override @@ -73,4 +78,9 @@ public void doProcess(LocalPlayer player) { player.push(this.motion.x, this.motion.y, this.motion.z); this.motion = this.motion.mul(FRICTION); } + + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } } diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/SwingTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/SwingTracker.java index 6fdb213b2..595099696 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/SwingTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/SwingTracker.java @@ -1,5 +1,15 @@ package org.vivecraft.client_vr.gameplay.trackers; +import java.util.List; + +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.block.*; +import org.vivecraft.api.client.Tracker; +import org.vivecraft.client.VivecraftVRMod; +import org.vivecraft.client_vr.ClientDataHolderVR; +import org.vivecraft.client_vr.provider.ControllerType; +import org.vivecraft.client_vr.settings.VRSettings; + import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; @@ -12,9 +22,7 @@ import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.*; -import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.ClipContext; -import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.DoorHingeSide; import net.minecraft.world.phys.AABB; @@ -22,25 +30,19 @@ import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import org.joml.Vector3f; -import org.vivecraft.client.VivecraftVRMod; import org.vivecraft.client.Xplat; import org.vivecraft.client.network.ClientNetworking; -import org.vivecraft.client_vr.ClientDataHolderVR; import org.vivecraft.client_vr.Vector3fHistory; -import org.vivecraft.client_vr.provider.ControllerType; import org.vivecraft.client_vr.provider.MCVR; -import org.vivecraft.client_vr.settings.VRSettings; -import org.vivecraft.common.network.FBTMode; -import org.vivecraft.common.network.BodyPart; +import org.vivecraft.api.data.FBTMode; +import org.vivecraft.api.data.VRBodyPart; import org.vivecraft.common.utils.MathUtils; import org.vivecraft.data.BlockTags; import org.vivecraft.data.ItemTags; import org.vivecraft.mod_compat_vr.bettercombat.BetterCombatHelper; import org.vivecraft.mod_compat_vr.epicfight.EpicFightHelper; -import java.util.List; - -public class SwingTracker extends Tracker { +public class SwingTracker implements Tracker { private static final int[] CONTROLLER_AND_FEET = new int[]{MCVR.MAIN_CONTROLLER, MCVR.OFFHAND_CONTROLLER, MCVR.RIGHT_FOOT_TRACKER, MCVR.LEFT_FOOT_TRACKER}; private static final float SPEED_THRESH = 3.0F; @@ -52,8 +54,12 @@ public class SwingTracker extends Tracker { public boolean[] canAct = new boolean[4]; public int disableSwing = 3; + protected Minecraft mc; + protected ClientDataHolderVR dh; + public SwingTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } @Override @@ -235,10 +241,10 @@ public void doProcess(LocalPlayer player) { for (Entity entity : mobs) { if (entity.isPickable() && entity != this.mc.getCameraEntity().getVehicle()) { if (entityAct) { - // Minecraft.getInstance().physicalGuiManager.preClickAction(); + // this.mc.physicalGuiManager.preClickAction(); if (!EpicFightHelper.isLoaded() || !EpicFightHelper.attack()) { - ClientNetworking.sendActiveBodyPart(BodyPart.values()[i]); + ClientNetworking.sendActiveBodyPart(VRBodyPart.values()[i]); // only attack if epic fight didn't trigger this.mc.gameMode.attack(player, entity); } else { @@ -352,7 +358,7 @@ else if (blockstate.getBlock() instanceof NoteBlock || // this.mc.physicalGuiManager.preClickAction(); // send hitting hand - ClientNetworking.sendActiveBodyPart(BodyPart.values()[i]); + ClientNetworking.sendActiveBodyPart(VRBodyPart.values()[i]); // this will either destroy the block if in creative or set it as the current block. // does nothing in survival if you are already hitting this block. @@ -394,19 +400,24 @@ else if (blockstate.getBlock() instanceof NoteBlock || } // reset hitting hand - ClientNetworking.sendActiveBodyPart(BodyPart.MAIN_HAND); + ClientNetworking.sendActiveBodyPart(VRBodyPart.MAIN_HAND); this.mc.getProfiler().pop(); } + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } + private boolean getIsHittingBlock() { return this.mc.gameMode.isDestroying(); } private void clearBlockHitDelay() { // TODO set destroyTicks to 1 to cancel multiple sound events per hit - // MCReflection.PlayerController_blockHitDelay.set(Minecraft.getInstance().gameMode, 0); - // Minecraft.getInstance().gameMode.blockBreakingCooldown = 1; + // MCReflection.PlayerController_blockHitDelay.set(this.mc.gameMode, 0); + // this.mc.gameMode.blockBreakingCooldown = 1; } /** diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/TeleportTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/TeleportTracker.java index 6bd8930fe..c443420ad 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/TeleportTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/TeleportTracker.java @@ -15,6 +15,7 @@ import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import org.joml.Vector3f; +import org.vivecraft.api.client.Tracker; import org.vivecraft.client.VivecraftVRMod; import org.vivecraft.client.network.ClientNetworking; import org.vivecraft.client_vr.ClientDataHolderVR; @@ -27,7 +28,7 @@ import java.util.Random; -public class TeleportTracker extends Tracker { +public class TeleportTracker implements Tracker { private float teleportEnergy; private Vec3 movementTeleportDestination = Vec3.ZERO; private Direction movementTeleportDestinationSideHit; @@ -38,9 +39,12 @@ public class TeleportTracker extends Tracker { public int movementTeleportArcSteps = 0; public double lastTeleportArcDisplayOffset = 0.0D; public VRMovementStyle vrMovementStyle; + protected Minecraft mc; + protected ClientDataHolderVR dh; public TeleportTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; this.vrMovementStyle = new VRMovementStyle(); } @@ -499,4 +503,9 @@ public Vec3 getInterpolatedArcPosition(float progress) { this.movementTeleportArc[step].z + deltaZ * stepProgress); } } + + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } } diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/TelescopeTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/TelescopeTracker.java index 1d0c17080..f2b5d7fe6 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/TelescopeTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/TelescopeTracker.java @@ -8,13 +8,16 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import org.joml.Vector3f; +import org.vivecraft.api.client.ItemInUseTracker; +import org.vivecraft.common.utils.MathUtils; +import org.vivecraft.api.client.Tracker; import org.vivecraft.client_vr.ClientDataHolderVR; import org.vivecraft.client_vr.VRData; import org.vivecraft.client_vr.render.RenderPass; import org.vivecraft.common.utils.MathUtils; import org.vivecraft.data.ItemTags; -public class TelescopeTracker extends Tracker { +public class TelescopeTracker implements Tracker, ItemInUseTracker { public static final ModelResourceLocation SCOPE_MODEL = new ModelResourceLocation("vivecraft", "spyglass_in_hand", "inventory"); private static final float LENS_DIST_MAX = 0.05F; @@ -24,8 +27,12 @@ public class TelescopeTracker extends Tracker { private final boolean[] viewing = new boolean[2]; + protected Minecraft mc; + protected ClientDataHolderVR dh; + public TelescopeTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } @Override @@ -64,6 +71,11 @@ public void doProcess(LocalPlayer player) { } } + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } + /** * @param itemStack ItemStack to check * @return if the given {@code itemStack} is a telescope diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/Tracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/Tracker.java deleted file mode 100644 index d081c158d..000000000 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/Tracker.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.vivecraft.client_vr.gameplay.trackers; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.player.LocalPlayer; -import org.vivecraft.client_vr.ClientDataHolderVR; - -import javax.annotation.Nullable; - -public abstract class Tracker { - public Minecraft mc; - public ClientDataHolderVR dh; - - public Tracker(Minecraft mc, ClientDataHolderVR dh) { - this.mc = mc; - this.dh = dh; - } - - public abstract boolean isActive(@Nullable LocalPlayer player); - - public abstract void doProcess(@Nullable LocalPlayer player); - - public boolean itemInUse(@Nullable LocalPlayer player) { - return false; - } - - public void reset(@Nullable LocalPlayer player) {} - - public void idleTick(@Nullable LocalPlayer player) {} - - public EntryPoint getEntryPoint() { - return EntryPoint.LIVING_UPDATE; - } - - public enum EntryPoint { - LIVING_UPDATE, - SPECIAL_ITEMS - } -} diff --git a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/VehicleTracker.java b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/VehicleTracker.java index 3b8c43cfe..f9e7a6563 100644 --- a/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/VehicleTracker.java +++ b/common/src/main/java/org/vivecraft/client_vr/gameplay/trackers/VehicleTracker.java @@ -1,5 +1,10 @@ package org.vivecraft.client_vr.gameplay.trackers; +import org.vivecraft.api.client.Tracker; +import org.vivecraft.client_vr.ClientDataHolderVR; +import org.vivecraft.client_vr.VRData; +import org.vivecraft.client_vr.settings.VRSettings; + import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.entity.Entity; @@ -10,13 +15,10 @@ import net.minecraft.world.item.FoodOnAStickItem; import net.minecraft.world.phys.Vec3; import org.joml.Vector3f; -import org.vivecraft.client_vr.ClientDataHolderVR; -import org.vivecraft.client_vr.VRData; -import org.vivecraft.client_vr.settings.VRSettings; import org.vivecraft.common.utils.MathUtils; import org.vivecraft.data.ItemTags; -public class VehicleTracker extends Tracker { +public class VehicleTracker implements Tracker { private float PreMount_World_Rotation; public Vec3 Premount_Pos_Room = Vec3.ZERO; public float vehicleInitialRotation = 0.0F; @@ -27,8 +29,12 @@ public class VehicleTracker extends Tracker { private int minecartStupidityCounter; private boolean isRiding = false; + protected Minecraft mc; + protected ClientDataHolderVR dh; + public VehicleTracker(Minecraft mc, ClientDataHolderVR dh) { - super(mc, dh); + this.mc = mc; + this.dh = dh; } @Override @@ -271,4 +277,9 @@ public boolean canRoomscaleDismount(LocalPlayer player) { player.isPassenger() && player.getVehicle().onGround() && this.dismountCooldown == 0; } + + @Override + public TrackerTickType tickType() { + return TrackerTickType.PER_TICK; + } } diff --git a/common/src/main/java/org/vivecraft/client_vr/provider/nullvr/NullVR.java b/common/src/main/java/org/vivecraft/client_vr/provider/nullvr/NullVR.java index 50d92301e..e6c4aa3cf 100644 --- a/common/src/main/java/org/vivecraft/client_vr/provider/nullvr/NullVR.java +++ b/common/src/main/java/org/vivecraft/client_vr/provider/nullvr/NullVR.java @@ -18,7 +18,7 @@ import org.vivecraft.client_vr.provider.openvr_lwjgl.VRInputAction; import org.vivecraft.client_vr.render.MirrorNotification; import org.vivecraft.client_vr.settings.VRSettings; -import org.vivecraft.common.network.FBTMode; +import org.vivecraft.api.data.FBTMode; import org.vivecraft.common.utils.MathUtils; import java.lang.Math; diff --git a/common/src/main/java/org/vivecraft/client_vr/render/helpers/DebugRenderHelper.java b/common/src/main/java/org/vivecraft/client_vr/render/helpers/DebugRenderHelper.java index 41efe7173..e3163f969 100644 --- a/common/src/main/java/org/vivecraft/client_vr/render/helpers/DebugRenderHelper.java +++ b/common/src/main/java/org/vivecraft/client_vr/render/helpers/DebugRenderHelper.java @@ -23,7 +23,7 @@ import org.vivecraft.client_vr.provider.DeviceSource; import org.vivecraft.client_vr.provider.MCVR; import org.vivecraft.client_vr.render.RenderPass; -import org.vivecraft.common.network.FBTMode; +import org.vivecraft.api.data.FBTMode; import org.vivecraft.common.utils.MathUtils; import java.util.ArrayList; diff --git a/common/src/main/java/org/vivecraft/common/APIImpl.java b/common/src/main/java/org/vivecraft/common/APIImpl.java deleted file mode 100644 index 37a413512..000000000 --- a/common/src/main/java/org/vivecraft/common/APIImpl.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.vivecraft.common; - -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.player.Player; -import org.vivecraft.api_beta.VivecraftAPI; -import org.vivecraft.client.ClientVRPlayers; -import org.vivecraft.server.ServerVRPlayers; - -public final class APIImpl implements VivecraftAPI { - - public static final APIImpl INSTANCE = new APIImpl(); - - private APIImpl() {} - - @Override - public boolean isVRPlayer(Player player) { - if (player instanceof ServerPlayer serverPlayer) { - return ServerVRPlayers.isVRPlayer(serverPlayer); - } - - return ClientVRPlayers.getInstance().isVRPlayer(player); - } -} diff --git a/common/src/main/java/org/vivecraft/common/api_impl/VRAPIImpl.java b/common/src/main/java/org/vivecraft/common/api_impl/VRAPIImpl.java new file mode 100644 index 000000000..13fa86f3c --- /dev/null +++ b/common/src/main/java/org/vivecraft/common/api_impl/VRAPIImpl.java @@ -0,0 +1,39 @@ +package org.vivecraft.common.api_impl; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import org.vivecraft.api.VRAPI; +import org.vivecraft.client.ClientVRPlayers; +import org.jetbrains.annotations.Nullable; +import org.vivecraft.api.data.VRPose; +import org.vivecraft.server.ServerVRPlayers; + +public final class VRAPIImpl implements VRAPI { + + public static final VRAPIImpl INSTANCE = new VRAPIImpl(); + + private VRAPIImpl() { + } + + @Override + public boolean isVRPlayer(Player player) { + if (player instanceof ServerPlayer serverPlayer) { + return ServerVRPlayers.isVRPlayer(serverPlayer); + } + + return ClientVRPlayers.getInstance().isVRPlayer(player); + } + + @Nullable + @Override + public VRPose getVRPose(Player player) { + if (!isVRPlayer(player)) { + return null; + } + if (player instanceof ServerPlayer serverPlayer) { + return ServerVRPlayers.getVivePlayer(serverPlayer).asVRPose(); + } + + return ClientVRPlayers.getInstance().getRotationsForPlayer(player.getUUID()).asVRPose(player.position()); + } +} diff --git a/common/src/main/java/org/vivecraft/common/api_impl/data/VRBodyPartDataImpl.java b/common/src/main/java/org/vivecraft/common/api_impl/data/VRBodyPartDataImpl.java new file mode 100644 index 000000000..a08eb4534 --- /dev/null +++ b/common/src/main/java/org/vivecraft/common/api_impl/data/VRBodyPartDataImpl.java @@ -0,0 +1,44 @@ +package org.vivecraft.common.api_impl.data; + +import net.minecraft.world.phys.Vec3; +import org.joml.Quaternionfc; +import org.vivecraft.api.data.VRBodyPartData; + +public record VRBodyPartDataImpl(Vec3 pos, Vec3 rot, Quaternionfc quaternion) implements VRBodyPartData { + + @Override + public Vec3 getPos() { + return this.pos; + } + + @Override + public Vec3 getRot() { + return this.rot; + } + + @Override + public double getPitch() { + return Math.asin(this.rot.y / this.rot.length()); + } + + @Override + public double getYaw() { + return Math.atan2(-this.rot.x, this.rot.z); + } + + @Override + public double getRoll() { + return -Math.atan2(2.0F * (quaternion.x() * quaternion.y() + quaternion.w() * quaternion.z()), + quaternion.w() * quaternion.w() - quaternion.x() * quaternion.x() + quaternion.y() * quaternion.y() - quaternion.z() * quaternion.z()); + } + + @Override + public Quaternionfc getQuaternion() { + return this.quaternion; + } + + @Override + public String toString() { + return "Position: " + getPos() + ", Rotation: " + getRot(); + } +} diff --git a/common/src/main/java/org/vivecraft/common/api_impl/data/VRPoseImpl.java b/common/src/main/java/org/vivecraft/common/api_impl/data/VRPoseImpl.java new file mode 100644 index 000000000..88cc590ae --- /dev/null +++ b/common/src/main/java/org/vivecraft/common/api_impl/data/VRPoseImpl.java @@ -0,0 +1,46 @@ +package org.vivecraft.common.api_impl.data; + +import org.vivecraft.api.data.FBTMode; +import org.vivecraft.api.data.VRBodyPart; +import org.vivecraft.api.data.VRPose; +import org.vivecraft.api.data.VRBodyPartData; + +import javax.annotation.Nullable; + +public record VRPoseImpl(VRBodyPartData hmd, VRBodyPartData c0, VRBodyPartData c1, + VRBodyPartData rightFoot, VRBodyPartData leftFoot, + VRBodyPartData waist, + VRBodyPartData rightKnee, VRBodyPartData leftKnee, + VRBodyPartData rightElbow, VRBodyPartData leftElbow, + boolean isSeated, boolean isLeftHanded, FBTMode fbtMode) implements VRPose { + + @Override + @Nullable + public VRBodyPartData getBodyPartData(VRBodyPart vrBodyPart) { + if (vrBodyPart == null) { + throw new IllegalArgumentException("Cannot get a null body part's data!"); + } + return switch (vrBodyPart) { + case HMD -> this.hmd; + case MAIN_HAND -> this.c0; + case OFF_HAND -> this.c1; + case RIGHT_FOOT -> this.rightFoot; + case LEFT_FOOT -> this.leftFoot; + case WAIST -> this.waist; + case RIGHT_KNEE -> this.rightKnee; + case LEFT_KNEE -> this.leftKnee; + case RIGHT_ELBOW -> this.rightElbow; + case LEFT_ELBOW -> this.leftElbow; + }; + } + + @Override + public FBTMode getFBTMode() { + return this.fbtMode; + } + + @Override + public String toString() { + return "HMD: " + getHMD() + "\nController 0: " + getMainHand() + "\nController 1: " + getOffHand(); + } +} diff --git a/common/src/main/java/org/vivecraft/common/network/FBTMode.java b/common/src/main/java/org/vivecraft/common/network/FBTMode.java deleted file mode 100644 index 52eb4259a..000000000 --- a/common/src/main/java/org/vivecraft/common/network/FBTMode.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.vivecraft.common.network; - -public enum FBTMode { - /** - * only controllers are available - */ - ARMS_ONLY, - /** - * controller, waist and feet trackers are available - */ - ARMS_LEGS, - /** - * controller, waist, feet, elbow and knee trackers are available - */ - WITH_JOINTS -} diff --git a/common/src/main/java/org/vivecraft/common/network/Pose.java b/common/src/main/java/org/vivecraft/common/network/Pose.java index ca036d9fd..eaebe3fc6 100644 --- a/common/src/main/java/org/vivecraft/common/network/Pose.java +++ b/common/src/main/java/org/vivecraft/common/network/Pose.java @@ -1,8 +1,15 @@ package org.vivecraft.common.network; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.phys.Vec3; import org.joml.Quaternionfc; +import org.joml.Vector3f; import org.joml.Vector3fc; +import org.vivecraft.api.data.VRBodyPartData; +import org.vivecraft.common.api_impl.data.VRBodyPartDataImpl; +import org.vivecraft.common.utils.MathUtils; + +import static org.vivecraft.common.utils.MathUtils.fromVector3fc; /** * holds a device Pose @@ -32,4 +39,13 @@ public void serialize(FriendlyByteBuf buffer) { CommonNetworkHelper.serializeF(buffer, this.position); CommonNetworkHelper.serialize(buffer, this.orientation); } + + /** + * @param playerPos The current position of the player. + * + * @return This Pose as VRBodyPartData for use with the API. + */ + public VRBodyPartData asBodyPartData(Vec3 playerPos) { + return new VRBodyPartDataImpl(fromVector3fc(this.position).add(playerPos), fromVector3fc(this.orientation.transform(MathUtils.BACK, new Vector3f())), this.orientation); + } } diff --git a/common/src/main/java/org/vivecraft/common/network/VrPlayerState.java b/common/src/main/java/org/vivecraft/common/network/VrPlayerState.java index d81876ef9..7ff53c9bb 100644 --- a/common/src/main/java/org/vivecraft/common/network/VrPlayerState.java +++ b/common/src/main/java/org/vivecraft/common/network/VrPlayerState.java @@ -2,13 +2,18 @@ import net.minecraft.client.Minecraft; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.phys.Vec3; import org.joml.Quaternionf; import org.joml.Vector3f; +import org.vivecraft.api.data.VRBodyPart; +import org.vivecraft.api.data.FBTMode; +import org.vivecraft.api.data.VRBodyPartData; import org.vivecraft.client.network.ClientNetworking; import org.vivecraft.client_vr.ClientDataHolderVR; import org.vivecraft.client_vr.gameplay.VRPlayer; import org.vivecraft.client_vr.provider.MCVR; import org.vivecraft.client_vr.render.RenderPass; +import org.vivecraft.common.api_impl.data.VRPoseImpl; import org.vivecraft.common.utils.MathUtils; import javax.annotation.Nullable; @@ -192,7 +197,7 @@ public static VrPlayerState deserialize(FriendlyByteBuf buffer, int bytesAfter) * @return Pose of the {@code bodyPart}, or {@code null} if the body part is not valid for the current FBT mode */ @Nullable - public Pose getBodyPartPose(BodyPart bodyPart) { + public Pose getBodyPartPose(VRBodyPart bodyPart) { return switch(bodyPart) { case MAIN_HAND -> this.mainHand; case OFF_HAND -> this.offHand; @@ -203,6 +208,7 @@ public Pose getBodyPartPose(BodyPart bodyPart) { case LEFT_KNEE -> this.leftKnee; case RIGHT_KNEE -> this.rightKnee; case WAIST -> this.waist; + case HMD -> this.hmd; }; } @@ -232,4 +238,33 @@ public void serialize(FriendlyByteBuf buffer) { } } } + + /** + * @param playerPos The current position of the player. + * + * @return This object as a pose for use with the API. + */ + public VRPoseImpl asVRPose(Vec3 playerPos) { + return new VRPoseImpl( + this.hmd.asBodyPartData(playerPos), + this.mainHand.asBodyPartData(playerPos), + this.offHand.asBodyPartData(playerPos), + getDataOrNull(this.rightFoot, playerPos), + getDataOrNull(this.leftFoot, playerPos), + getDataOrNull(this.waist, playerPos), + getDataOrNull(this.rightKnee, playerPos), + getDataOrNull(this.leftKnee, playerPos), + getDataOrNull(this.rightElbow, playerPos), + getDataOrNull(this.leftElbow, playerPos), + this.seated, + this.leftHanded, + this.fbtMode + ); + } + + @Nullable + private static VRBodyPartData getDataOrNull(Pose pose, Vec3 playerPos) { + return pose == null ? null : pose.asBodyPartData(playerPos); + } + } diff --git a/common/src/main/java/org/vivecraft/common/network/packet/c2s/ActiveBodyPartPayloadC2S.java b/common/src/main/java/org/vivecraft/common/network/packet/c2s/ActiveBodyPartPayloadC2S.java index ba516ecf2..2d4f40a18 100644 --- a/common/src/main/java/org/vivecraft/common/network/packet/c2s/ActiveBodyPartPayloadC2S.java +++ b/common/src/main/java/org/vivecraft/common/network/packet/c2s/ActiveBodyPartPayloadC2S.java @@ -3,8 +3,8 @@ import net.minecraft.network.FriendlyByteBuf; import org.vivecraft.client.network.ClientNetworking; import org.vivecraft.common.network.CommonNetworkHelper; -import org.vivecraft.common.network.BodyPart; -import org.vivecraft.common.network.FBTMode; +import org.vivecraft.api.data.VRBodyPart; +import org.vivecraft.api.data.FBTMode; import org.vivecraft.common.network.packet.PayloadIdentifier; /** @@ -12,7 +12,7 @@ * * @param bodyPart the active BodyPart */ -public record ActiveBodyPartPayloadC2S(BodyPart bodyPart) implements VivecraftPayloadC2S { +public record ActiveBodyPartPayloadC2S(VRBodyPart bodyPart) implements VivecraftPayloadC2S { @Override public PayloadIdentifier payloadId() { @@ -23,16 +23,16 @@ public PayloadIdentifier payloadId() { public void write(FriendlyByteBuf buffer) { buffer.writeByte(payloadId().ordinal()); if (ClientNetworking.USED_NETWORK_VERSION < CommonNetworkHelper.NETWORK_VERSION_DUAL_WIELDING && - !this.bodyPart.isValid(FBTMode.ARMS_ONLY)) + !this.bodyPart.availableInMode(FBTMode.ARMS_ONLY)) { // old plugins only support main and offhand - buffer.writeByte(BodyPart.MAIN_HAND.ordinal()); + buffer.writeByte(VRBodyPart.MAIN_HAND.ordinal()); } else { buffer.writeByte(this.bodyPart.ordinal()); } } public static ActiveBodyPartPayloadC2S read(FriendlyByteBuf buffer) { - return new ActiveBodyPartPayloadC2S(BodyPart.values()[buffer.readByte()]); + return new ActiveBodyPartPayloadC2S(VRBodyPart.values()[buffer.readByte()]); } } diff --git a/common/src/main/java/org/vivecraft/common/utils/MathUtils.java b/common/src/main/java/org/vivecraft/common/utils/MathUtils.java index f89102e38..366d21fcd 100644 --- a/common/src/main/java/org/vivecraft/common/utils/MathUtils.java +++ b/common/src/main/java/org/vivecraft/common/utils/MathUtils.java @@ -233,4 +233,13 @@ public static float bodyYawRad(Vector3fc rightHand, Vector3fc leftHand, Vector3f dir.normalize().lerp(headDir, 0.5F, dir); return (float) Math.atan2(-dir.x, dir.z); } + + /** + * Converts a {@link Vector3fc} to a {@link Vec3}. + * @param vec The original Vector3fc. + * @return The Vec3. + */ + public static Vec3 fromVector3fc(Vector3fc vec) { + return new Vec3(vec.x(), vec.y(), vec.z()); + } } diff --git a/common/src/main/java/org/vivecraft/mixin/advancements/PlayerPredicateMixin.java b/common/src/main/java/org/vivecraft/mixin/advancements/PlayerPredicateMixin.java index d9dbbcedb..61f517c1f 100644 --- a/common/src/main/java/org/vivecraft/mixin/advancements/PlayerPredicateMixin.java +++ b/common/src/main/java/org/vivecraft/mixin/advancements/PlayerPredicateMixin.java @@ -10,7 +10,7 @@ import net.minecraft.world.phys.Vec3; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.vivecraft.common.network.BodyPart; +import org.vivecraft.api.data.VRBodyPart; import org.vivecraft.common.utils.MathUtils; import org.vivecraft.server.ServerVRPlayers; import org.vivecraft.server.ServerVivePlayer; @@ -24,7 +24,7 @@ public class PlayerPredicateMixin { if (player.getUseItem().is(Items.SPYGLASS) && ServerVRPlayers.isVRPlayer(player)) { vivePlayer.set(ServerVRPlayers.getVivePlayer(player)); if (!vivePlayer.get().isSeated()) { - return vivePlayer.get().getBodyPartPos(BodyPart.values()[player.getUsedItemHand().ordinal()]); + return vivePlayer.get().getBodyPartPos(VRBodyPart.values()[player.getUsedItemHand().ordinal()]); } } return original.call(player); @@ -37,7 +37,7 @@ public class PlayerPredicateMixin { { if (vivePlayer.get() != null && !vivePlayer.get().isSeated()) { return vivePlayer.get() - .getBodyPartVectorCustom(BodyPart.values()[player.getUsedItemHand().ordinal()], MathUtils.DOWN); + .getBodyPartVectorCustom(VRBodyPart.values()[player.getUsedItemHand().ordinal()], MathUtils.DOWN); } else { return original.call(player, partialTick); } diff --git a/common/src/main/java/org/vivecraft/mixin/client_vr/MinecraftVRMixin.java b/common/src/main/java/org/vivecraft/mixin/client_vr/MinecraftVRMixin.java index 03af1fd03..57909ea5d 100644 --- a/common/src/main/java/org/vivecraft/mixin/client_vr/MinecraftVRMixin.java +++ b/common/src/main/java/org/vivecraft/mixin/client_vr/MinecraftVRMixin.java @@ -714,7 +714,7 @@ public abstract class MinecraftVRMixin implements MinecraftExtension { if (!VRState.VR_RUNNING || ClientDataHolderVR.getInstance().vrSettings.seated) { return useKeyDown; } else { - return useKeyDown || ClientDataHolderVR.getInstance().vrPlayer.isTrackerUsingItem(this.player); + return useKeyDown || ClientDataHolderVR.getInstance().isTrackerUsingItem(this.player); } } diff --git a/common/src/main/java/org/vivecraft/mixin/client_vr/multiplayer/MultiPlayerGameModeVRMixin.java b/common/src/main/java/org/vivecraft/mixin/client_vr/multiplayer/MultiPlayerGameModeVRMixin.java index 5e48441ff..30815097c 100644 --- a/common/src/main/java/org/vivecraft/mixin/client_vr/multiplayer/MultiPlayerGameModeVRMixin.java +++ b/common/src/main/java/org/vivecraft/mixin/client_vr/multiplayer/MultiPlayerGameModeVRMixin.java @@ -20,7 +20,7 @@ import org.vivecraft.client.network.ClientNetworking; import org.vivecraft.client_vr.ClientDataHolderVR; import org.vivecraft.client_vr.VRState; -import org.vivecraft.common.network.BodyPart; +import org.vivecraft.api.data.VRBodyPart; /** * we override the players look direction so the server handles any interactions as if the player looked at the interacted block @@ -66,12 +66,12 @@ public class MultiPlayerGameModeVRMixin { if (VRState.VR_RUNNING && ClientNetworking.SERVER_ALLOWS_DUAL_WIELDING) { // check if main or offhand items match the started item, we want to limit abuse of this, // but still make both items work - BodyPart lastBodyPart = ClientNetworking.LAST_SENT_BODY_PART; + VRBodyPart lastBodyPart = ClientNetworking.LAST_SENT_BODY_PART; - ClientNetworking.LAST_SENT_BODY_PART = BodyPart.MAIN_HAND; + ClientNetworking.LAST_SENT_BODY_PART = VRBodyPart.MAIN_HAND; boolean sameItem = original.call(pos); - ClientNetworking.LAST_SENT_BODY_PART = BodyPart.OFF_HAND; + ClientNetworking.LAST_SENT_BODY_PART = VRBodyPart.OFF_HAND; sameItem |= original.call(pos); ClientNetworking.LAST_SENT_BODY_PART = lastBodyPart; diff --git a/common/src/main/java/org/vivecraft/mixin/server/ServerGamePacketListenerImplMixin.java b/common/src/main/java/org/vivecraft/mixin/server/ServerGamePacketListenerImplMixin.java index 7712eba6c..b9731d48e 100644 --- a/common/src/main/java/org/vivecraft/mixin/server/ServerGamePacketListenerImplMixin.java +++ b/common/src/main/java/org/vivecraft/mixin/server/ServerGamePacketListenerImplMixin.java @@ -45,7 +45,7 @@ public ServerGamePacketListenerImplMixin( if (this.player.hasDisconnected()) { // if they did disconnect remove them ServerVRPlayers.getPlayersWithVivecraft(this.player.server).remove(this.player.getUUID()); - } else if (vivePlayer.isVR() && vivePlayer.vrPlayerState != null) { + } else if (vivePlayer.isVR() && vivePlayer.vrPlayerState() != null) { ServerNetworking.sendVrPlayerStateToClients(vivePlayer); if (ServerConfig.DEBUG_PARTICLES.get()) { ServerUtil.debugParticleAxes(vivePlayer); diff --git a/common/src/main/java/org/vivecraft/mixin/world/entity/InventoryMixin.java b/common/src/main/java/org/vivecraft/mixin/world/entity/InventoryMixin.java index 3b124d8ec..5b5129c01 100644 --- a/common/src/main/java/org/vivecraft/mixin/world/entity/InventoryMixin.java +++ b/common/src/main/java/org/vivecraft/mixin/world/entity/InventoryMixin.java @@ -17,7 +17,7 @@ import org.vivecraft.client.network.ClientNetworking; import org.vivecraft.client_vr.VRState; import org.vivecraft.common.network.CommonNetworkHelper; -import org.vivecraft.common.network.BodyPart; +import org.vivecraft.api.data.VRBodyPart; import org.vivecraft.server.ServerVRPlayers; import org.vivecraft.server.ServerVivePlayer; import org.vivecraft.server.config.ServerConfig; @@ -44,7 +44,7 @@ public class InventoryMixin { @Unique private ItemStack vivecraft$activeItem(ItemStack original) { - BodyPart bodyPart = null; + VRBodyPart bodyPart = null; // server side if (this.player instanceof ServerPlayer serverPlayer && ServerConfig.DUAL_WIELDING.get()) { if (ServerVRPlayers.isVRPlayer(serverPlayer)) { @@ -61,9 +61,9 @@ else if (this.player.isLocalPlayer() && VRState.VR_RUNNING && ClientNetworking.S } if (bodyPart != null) { - if (bodyPart == BodyPart.OFF_HAND) { + if (bodyPart == VRBodyPart.OFF_HAND) { return this.offhand.get(0); - } else if (bodyPart != BodyPart.MAIN_HAND) { + } else if (bodyPart != VRBodyPart.MAIN_HAND) { // feet return ItemStack.EMPTY; } diff --git a/common/src/main/java/org/vivecraft/server/ServerNetworking.java b/common/src/main/java/org/vivecraft/server/ServerNetworking.java index c0caa209b..5713f54b3 100644 --- a/common/src/main/java/org/vivecraft/server/ServerNetworking.java +++ b/common/src/main/java/org/vivecraft/server/ServerNetworking.java @@ -18,8 +18,8 @@ import org.vivecraft.client.Xplat; import org.vivecraft.common.CommonDataHolder; import org.vivecraft.common.network.CommonNetworkHelper; -import org.vivecraft.common.network.FBTMode; -import org.vivecraft.common.network.BodyPart; +import org.vivecraft.api.data.FBTMode; +import org.vivecraft.api.data.VRBodyPart; import org.vivecraft.common.network.VrPlayerState; import org.vivecraft.common.network.packet.PayloadIdentifier; import org.vivecraft.common.network.packet.c2s.*; @@ -182,7 +182,7 @@ public static void handlePacket( } } case DRAW -> vivePlayer.draw = ((DrawPayloadC2S) c2sPayload).draw(); - case VR_PLAYER_STATE -> vivePlayer.vrPlayerState = ((VRPlayerStatePayloadC2S) c2sPayload).playerState(); + case VR_PLAYER_STATE -> vivePlayer.setVrPlayerState(((VRPlayerStatePayloadC2S) c2sPayload).playerState()); case WORLDSCALE -> vivePlayer.worldScale = ((WorldScalePayloadC2S) c2sPayload).worldScale(); case HEIGHT -> vivePlayer.heightScale = ((HeightPayloadC2S) c2sPayload).heightScale(); case TELEPORT -> { @@ -194,7 +194,7 @@ public static void handlePacket( player.connection.aboveGroundTickCount = 0; } case ACTIVEHAND -> { - BodyPart newBodyPart = vivePlayer.isSeated() ? BodyPart.MAIN_HAND : ((ActiveBodyPartPayloadC2S) c2sPayload).bodyPart(); + VRBodyPart newBodyPart = vivePlayer.isSeated() ? VRBodyPart.MAIN_HAND : ((ActiveBodyPartPayloadC2S) c2sPayload).bodyPart(); if (vivePlayer.activeBodyPart != newBodyPart) { // handle equipment changes ItemStack oldItem = player.getItemBySlot(EquipmentSlot.MAINHAND); @@ -239,7 +239,7 @@ public static void handlePacket( LegacyHeadDataPayloadC2S headData = (LegacyHeadDataPayloadC2S) playerData .get(PayloadIdentifier.HEADDATA); - vivePlayer.vrPlayerState = new VrPlayerState( + vivePlayer.setVrPlayerState(new VrPlayerState( headData.seated(), // isSeated headData.hmdPose(), // head pose controller0Data.leftHanded(), // leftHanded 0 @@ -249,7 +249,7 @@ public static void handlePacket( FBTMode.ARMS_ONLY, null, null, null, null, null, - null, null); + null, null)); LEGACY_DATA_MAP.remove(player.getUUID()); } @@ -287,10 +287,10 @@ public static VivecraftPayloadS2C getClimbeyServerPayload() { public static void sendVrPlayerStateToClients(ServerVivePlayer vivePlayer) { // create the packets here, to try to avoid unnecessary memory copies when creating multiple packets Packet legacyPacket = Xplat.getS2CPacket( - new UberPacketPayloadS2C(vivePlayer.player.getUUID(), new VrPlayerState(vivePlayer.vrPlayerState, 0), + new UberPacketPayloadS2C(vivePlayer.player.getUUID(), new VrPlayerState(vivePlayer.vrPlayerState(), 0), vivePlayer.worldScale, vivePlayer.heightScale)); Packet newPacket = Xplat.getS2CPacket( - new UberPacketPayloadS2C(vivePlayer.player.getUUID(), vivePlayer.vrPlayerState, vivePlayer.worldScale, + new UberPacketPayloadS2C(vivePlayer.player.getUUID(), vivePlayer.vrPlayerState(), vivePlayer.worldScale, vivePlayer.heightScale)); sendPacketToTrackingPlayers(vivePlayer, (version) -> version < 1 ? legacyPacket : newPacket); diff --git a/common/src/main/java/org/vivecraft/server/ServerUtil.java b/common/src/main/java/org/vivecraft/server/ServerUtil.java index fd9bad517..470d1f277 100644 --- a/common/src/main/java/org/vivecraft/server/ServerUtil.java +++ b/common/src/main/java/org/vivecraft/server/ServerUtil.java @@ -19,7 +19,7 @@ import org.joml.Quaternionfc; import org.joml.Vector3f; import org.vivecraft.client.utils.UpdateChecker; -import org.vivecraft.common.network.BodyPart; +import org.vivecraft.api.data.VRBodyPart; import org.vivecraft.common.utils.MathUtils; import org.vivecraft.server.config.ConfigBuilder; import org.vivecraft.server.config.ServerConfig; @@ -337,13 +337,13 @@ public static void registerCommands( * @param vivePlayer vive vivePlayer to spawn particles for */ public static void debugParticleAxes(ServerVivePlayer vivePlayer) { - if (vivePlayer.isVR() && vivePlayer.vrPlayerState != null) { - for(BodyPart bodyPart : BodyPart.values()) { - if (bodyPart.isValid(vivePlayer.vrPlayerState.fbtMode())) { + if (vivePlayer.isVR() && vivePlayer.vrPlayerState() != null) { + for(VRBodyPart bodyPart : VRBodyPart.values()) { + if (bodyPart.availableInMode(vivePlayer.vrPlayerState().fbtMode()) && bodyPart != VRBodyPart.HMD) { debugParticleAxes( vivePlayer.player.serverLevel(), vivePlayer.getBodyPartPos(bodyPart), - vivePlayer.vrPlayerState.getBodyPartPose(bodyPart).orientation()); + vivePlayer.vrPlayerState().getBodyPartPose(bodyPart).orientation()); } } @@ -351,7 +351,7 @@ public static void debugParticleAxes(ServerVivePlayer vivePlayer) { debugParticleAxes( vivePlayer.player.serverLevel(), vivePlayer.getHMDPos(), - vivePlayer.vrPlayerState.hmd().orientation()); + vivePlayer.vrPlayerState().hmd().orientation()); } } } diff --git a/common/src/main/java/org/vivecraft/server/ServerVivePlayer.java b/common/src/main/java/org/vivecraft/server/ServerVivePlayer.java index 262d207ef..9af64826f 100644 --- a/common/src/main/java/org/vivecraft/server/ServerVivePlayer.java +++ b/common/src/main/java/org/vivecraft/server/ServerVivePlayer.java @@ -5,20 +5,24 @@ import net.minecraft.world.phys.Vec3; import org.joml.Vector3f; import org.joml.Vector3fc; -import org.vivecraft.common.network.*; +import org.vivecraft.api.data.VRBodyPart; import org.vivecraft.common.utils.MathUtils; +import org.vivecraft.api.data.VRPose; +import org.vivecraft.common.network.CommonNetworkHelper; +import org.vivecraft.common.network.VrPlayerState; import javax.annotation.Nullable; public class ServerVivePlayer { // player movement state @Nullable - public VrPlayerState vrPlayerState; + private VrPlayerState vrPlayerState; + private VRPose vrPlayerStateAsPose; // how much the player is drawing the roomscale bow public float draw; public float worldScale = 1.0F; public float heightScale = 1.0F; - public BodyPart activeBodyPart = BodyPart.MAIN_HAND; + public VRBodyPart activeBodyPart = VRBodyPart.MAIN_HAND; public boolean crawling; // if the player has VR active private boolean isVR = false; @@ -40,10 +44,10 @@ public ServerVivePlayer(ServerPlayer player) { * @param direction local direction to transform * @return direction in world space */ - public Vec3 getBodyPartVectorCustom(BodyPart bodyPart, Vector3fc direction) { + public Vec3 getBodyPartVectorCustom(VRBodyPart bodyPart, Vector3fc direction) { if (this.vrPlayerState != null) { - if (this.isSeated() || !bodyPart.isValid(this.vrPlayerState.fbtMode())) { - bodyPart = BodyPart.MAIN_HAND; + if (this.isSeated() || !bodyPart.availableInMode(this.vrPlayerState.fbtMode())) { + bodyPart = VRBodyPart.MAIN_HAND; } return new Vec3( @@ -57,7 +61,7 @@ public Vec3 getBodyPartVectorCustom(BodyPart bodyPart, Vector3fc direction) { * @param bodyPart BodyPart to get the direction from, if not available, will use the MAIN_HAND * @return forward direction of the given BodyPart */ - public Vec3 getBodyPartDir(BodyPart bodyPart) { + public Vec3 getBodyPartDir(VRBodyPart bodyPart) { return this.getBodyPartVectorCustom(bodyPart, MathUtils.BACK); } @@ -104,17 +108,17 @@ public Vec3 getHMDPos() { * @param realPosition if true disables the seated override * @return BodyPart position in world space */ - public Vec3 getBodyPartPos(BodyPart bodyPart, boolean realPosition) { + public Vec3 getBodyPartPos(VRBodyPart bodyPart, boolean realPosition) { if (this.vrPlayerState != null) { - if (!bodyPart.isValid(this.vrPlayerState.fbtMode())) { - bodyPart = BodyPart.MAIN_HAND; + if (!bodyPart.availableInMode(this.vrPlayerState.fbtMode())) { + bodyPart = VRBodyPart.MAIN_HAND; } // in seated the realPosition is at the head, // so reconstruct the seated position when wanting the visual position if (this.isSeated() && !realPosition) { Vec3 dir = this.getHMDDir(); - dir = dir.yRot(Mth.DEG_TO_RAD * (bodyPart == BodyPart.MAIN_HAND ? -35.0F : 35.0F)); + dir = dir.yRot(Mth.DEG_TO_RAD * (bodyPart == VRBodyPart.MAIN_HAND ? -35.0F : 35.0F)); dir = new Vec3(dir.x, 0.0D, dir.z); dir = dir.normalize(); return this.getHMDPos().add( @@ -138,7 +142,7 @@ public Vec3 getBodyPartPos(BodyPart bodyPart, boolean realPosition) { * @param bodyPart BodyPart to get the position for, if not available, will use the MAIN_HAND * @return BodyPart position in world space */ - public Vec3 getBodyPartPos(BodyPart bodyPart) { + public Vec3 getBodyPartPos(VRBodyPart bodyPart) { return getBodyPartPos(bodyPart, false); } @@ -162,4 +166,34 @@ public void setVR(boolean vr) { public boolean isSeated() { return this.vrPlayerState != null && this.vrPlayerState.seated(); } + + /** + * @return if the player is using left-handed mode + */ + public boolean isLeftHanded() { + if (this.vrPlayerState == null) { + return false; + } + return this.vrPlayerState.leftHanded(); + } + + @Nullable + public VrPlayerState vrPlayerState() { + return this.vrPlayerState; + } + + public void setVrPlayerState(VrPlayerState vrPlayerState) { + this.vrPlayerState = vrPlayerState; + this.vrPlayerStateAsPose = null; + } + + public VRPose asVRPose() { + if (this.vrPlayerState == null) { + return null; + } + if (this.vrPlayerStateAsPose == null) { + this.vrPlayerStateAsPose = this.vrPlayerState.asVRPose(player.position()); + } + return this.vrPlayerStateAsPose; + } }