From 5d8a21d68855414e149fb0b707b372eaf52ec25d Mon Sep 17 00:00:00 2001 From: Weever1337 Date: Fri, 1 May 2026 17:48:43 +0300 Subject: [PATCH] feat: overlay visualizer --- .../api/common/network/VisorPayloadID.java | 6 +- .../toclient/VisorPayloadToClient.java | 1 + .../VROtherGuiStatePayloadToClient.java | 22 +++++ .../toserver/VisorPayloadToServer.java | 1 + .../vrstate/GuiStatePayloadToServer.java | 20 +++++ .../visor/api/common/player/VRPlayer.java | 5 ++ .../core/client/network/ClientNetworking.java | 8 +- .../client/network/ClientPacketHandler.java | 7 ++ .../core/client/player/VRLocalPlayerImpl.java | 7 ++ .../client/player/VRRemotePlayerImpl.java | 7 ++ .../effects/GameEffectRemoteGui.java | 83 +++++++++++++++++++ .../core/server/network/ServerNetworking.java | 10 +++ .../server/network/ServerPacketHandler.java | 4 + .../server/player/VRServerPlayerImpl.java | 5 ++ 14 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 visor-api/src/main/java/org/vmstudio/visor/api/common/network/toclient/vrstate/VROtherGuiStatePayloadToClient.java create mode 100644 visor-api/src/main/java/org/vmstudio/visor/api/common/network/toserver/vrstate/GuiStatePayloadToServer.java create mode 100644 visor-core/src/main/java/org/vmstudio/visor/core/client/render/decoration/effects/GameEffectRemoteGui.java diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/common/network/VisorPayloadID.java b/visor-api/src/main/java/org/vmstudio/visor/api/common/network/VisorPayloadID.java index 11c06a51..f9bbb99b 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/common/network/VisorPayloadID.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/common/network/VisorPayloadID.java @@ -17,11 +17,11 @@ public enum VisorPayloadID { SWING_ATTACK, SWING_BLOCK, BLOCK_DAMAGE, + GUI_STATE, + OTHER_VR_GUI_STATE, OTHER_VR_POSE_DATA, OTHER_VR_LEFT_HANDED, OTHER_VR_BODY_TYPE, OTHER_VR_WORLD_SCALE, - OTHER_VR_FULL_HEIGHT - - + OTHER_VR_FULL_HEIGHT, } diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/common/network/toclient/VisorPayloadToClient.java b/visor-api/src/main/java/org/vmstudio/visor/api/common/network/toclient/VisorPayloadToClient.java index fb8b93ac..03be95d4 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/common/network/toclient/VisorPayloadToClient.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/common/network/toclient/VisorPayloadToClient.java @@ -19,6 +19,7 @@ static VisorPayloadToClient readPacket(FriendlyByteBuf buffer) { case ROTATION_Y -> RotationYPayloadToClient.read(buffer); case OFFHAND_SLOT -> OffhandSlotPayloadToClient.read(buffer); case BLOCK_DAMAGE -> BlockDamagePayloadToClient.read(buffer); + case OTHER_VR_GUI_STATE -> VROtherGuiStatePayloadToClient.read(buffer); case OTHER_VR_LEFT_HANDED -> VROtherLeftHandedPayloadToClient.read(buffer); case OTHER_VR_BODY_TYPE -> VROtherBodyTypePayloadToClient.read(buffer); case OTHER_VR_POSE_DATA -> VROtherPoseDataPayloadToClient.read(buffer); diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/common/network/toclient/vrstate/VROtherGuiStatePayloadToClient.java b/visor-api/src/main/java/org/vmstudio/visor/api/common/network/toclient/vrstate/VROtherGuiStatePayloadToClient.java new file mode 100644 index 00000000..a11eb718 --- /dev/null +++ b/visor-api/src/main/java/org/vmstudio/visor/api/common/network/toclient/vrstate/VROtherGuiStatePayloadToClient.java @@ -0,0 +1,22 @@ +package org.vmstudio.visor.api.common.network.toclient.vrstate; + +import net.minecraft.network.FriendlyByteBuf; +import org.vmstudio.visor.api.common.network.VisorPayloadID; +import org.vmstudio.visor.api.common.network.toclient.VisorPayloadToClient; +import java.util.UUID; + +public record VROtherGuiStatePayloadToClient(UUID playerUUID, boolean guiOpened) implements VisorPayloadToClient { + @Override + public void onWrite(FriendlyByteBuf buffer) { + buffer.writeUUID(playerUUID); + buffer.writeBoolean(guiOpened); + } + @Override + public VisorPayloadID payloadId() { + return VisorPayloadID.OTHER_VR_GUI_STATE; + } + + public static VROtherGuiStatePayloadToClient read(FriendlyByteBuf buffer) { + return new VROtherGuiStatePayloadToClient(buffer.readUUID(), buffer.readBoolean()); + } +} \ No newline at end of file diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/common/network/toserver/VisorPayloadToServer.java b/visor-api/src/main/java/org/vmstudio/visor/api/common/network/toserver/VisorPayloadToServer.java index fd7613c6..b1f5dd64 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/common/network/toserver/VisorPayloadToServer.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/common/network/toserver/VisorPayloadToServer.java @@ -28,6 +28,7 @@ static VisorPayloadToServer readPacket(FriendlyByteBuf buffer) { case TELEPORT -> TeleportMovePayloadToServer.read(buffer); case SWING_ATTACK -> SwingAttackPayloadToServer.read(buffer); case SWING_BLOCK -> SwingBlockPayloadToServer.read(buffer); + case GUI_STATE -> GuiStatePayloadToServer.read(buffer); default -> { VisorAPI.server().getLogger().error( "Visor: Got unexpected payload identifier on server: {}", id diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/common/network/toserver/vrstate/GuiStatePayloadToServer.java b/visor-api/src/main/java/org/vmstudio/visor/api/common/network/toserver/vrstate/GuiStatePayloadToServer.java new file mode 100644 index 00000000..704eb4d5 --- /dev/null +++ b/visor-api/src/main/java/org/vmstudio/visor/api/common/network/toserver/vrstate/GuiStatePayloadToServer.java @@ -0,0 +1,20 @@ +package org.vmstudio.visor.api.common.network.toserver.vrstate; + +import net.minecraft.network.FriendlyByteBuf; +import org.vmstudio.visor.api.common.network.VisorPayloadID; +import org.vmstudio.visor.api.common.network.toserver.VisorPayloadToServer; + +public record GuiStatePayloadToServer(boolean guiOpened) implements VisorPayloadToServer { + @Override + public void onWrite(FriendlyByteBuf buffer) { + buffer.writeBoolean(guiOpened); + } + @Override + public VisorPayloadID payloadId() { + return VisorPayloadID.GUI_STATE; + } + + public static GuiStatePayloadToServer read(FriendlyByteBuf buffer) { + return new GuiStatePayloadToServer(buffer.readBoolean()); + } +} \ No newline at end of file diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/common/player/VRPlayer.java b/visor-api/src/main/java/org/vmstudio/visor/api/common/player/VRPlayer.java index d0c10d24..dcdb4ba7 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/common/player/VRPlayer.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/common/player/VRPlayer.java @@ -86,4 +86,9 @@ default float getFullHeightScale() { return getFullHeight() / 1.52f; } + /** + * todo: todo + * @return is gui opened + */ + boolean isGuiOpened(); } diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/network/ClientNetworking.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/network/ClientNetworking.java index b97116df..a3b21732 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/network/ClientNetworking.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/network/ClientNetworking.java @@ -38,7 +38,7 @@ public class ClientNetworking { private static HandType activeHandLastSent = HandType.MAIN; private static VRBodyType vrBodyLastSent = null; - + private static boolean guiOpenedLastSent = false; public static void sendVRPacket(VisorPayloadToServer payload) { if (MC.getConnection() == null) return; @@ -137,6 +137,12 @@ public static void sendVRPlayerState() { vrBodyLastSent = vrBody; } + boolean guiOpened = localPlayer.isGuiOpened(); + if (guiOpened != guiOpenedLastSent) { + sendVRPacket(new GuiStatePayloadToServer(guiOpened)); + guiOpenedLastSent = guiOpened; + } + PoseDataBuffer vrPlayerState = PoseDataBuffer.create( localPlayer diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/network/ClientPacketHandler.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/network/ClientPacketHandler.java index 5978ee7c..40b0f64e 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/network/ClientPacketHandler.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/network/ClientPacketHandler.java @@ -58,6 +58,13 @@ public static void handlePacket(VisorPayloadToClient payloadClient){ payload.destroyStage() ); } + case OTHER_VR_GUI_STATE -> { + var payload = (VROtherGuiStatePayloadToClient) payloadClient; + var vrPlayer = VRClientPlayers.getPacketReceiver(payload.playerUUID()); + if (vrPlayer != null) { + vrPlayer.setGuiOpened(payload.guiOpened()); + } + } case OTHER_VR_POSE_DATA -> { var payload = (VROtherPoseDataPayloadToClient) payloadClient; var remotePlayer = VRClientPlayers.getValidPacketReceiverMc(payload.playerUUID()); diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/player/VRLocalPlayerImpl.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/player/VRLocalPlayerImpl.java index 542c3c02..684a0084 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/player/VRLocalPlayerImpl.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/player/VRLocalPlayerImpl.java @@ -78,6 +78,9 @@ public class VRLocalPlayerImpl implements VRLocalPlayer { @Getter @Setter private boolean moving; + @Getter @Setter + private boolean guiOpened; + public VRLocalPlayerImpl() { this.roomRelativePose = new LocalPlayerPose(this, PlayerPoseType.RELATIVE); this.prevPose = new LocalPlayerPose(this, PlayerPoseType.PREV_TICK); @@ -184,6 +187,10 @@ public void postTick() { isTicking = false; rotationYRaw = pose.getRotationY(); + + //gui state + boolean currentGuiState = MC.screen != null || ClientContext.cursorHandler.isAnyHandFocused(false); + this.guiOpened = currentGuiState; } diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/player/VRRemotePlayerImpl.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/player/VRRemotePlayerImpl.java index a11aec9a..cf91de6b 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/player/VRRemotePlayerImpl.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/player/VRRemotePlayerImpl.java @@ -47,6 +47,9 @@ public class VRRemotePlayerImpl implements VRRemotePlayer { @Getter private boolean leftHanded; + @Getter @Setter + private boolean guiOpened; + public VRRemotePlayerImpl(RemotePlayer mcPlayer, PoseDataBuffer poseBuffer) { @@ -289,4 +292,8 @@ public int getOffhandSlot() { public @NotNull HandType getActiveHand() { return HandType.MAIN; } + + public void receivedGuiStatePacket(boolean opened) { + this.guiOpened = opened; + } } diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/render/decoration/effects/GameEffectRemoteGui.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/render/decoration/effects/GameEffectRemoteGui.java new file mode 100644 index 00000000..61526d1b --- /dev/null +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/render/decoration/effects/GameEffectRemoteGui.java @@ -0,0 +1,83 @@ +package org.vmstudio.visor.core.client.render.decoration.effects; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.renderer.GameRenderer; +import org.jetbrains.annotations.NotNull; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.vmstudio.visor.api.client.render.VRRenderPass; +import org.vmstudio.visor.api.client.render.decoration.VRDecorator; +import org.vmstudio.visor.api.client.render.decoration.annotations.RegisterVRGameEffect; +import org.vmstudio.visor.api.client.render.decoration.effects.VRGameEffect; +import org.vmstudio.visor.api.common.addon.VisorAddon; +import org.vmstudio.visor.api.common.player.VRPose; +import org.vmstudio.visor.core.client.ClientContext; +import org.vmstudio.visor.core.client.player.VRClientPlayers; +import org.vmstudio.visor.core.client.render.helpers.RenderHelper; +import org.vmstudio.visor.core.client.render.helpers.RenderPoseHelper; +import me.phoenixra.atumvr.api.misc.color.AtumColorImmutable; +import org.vmstudio.visor.api.client.player.pose.PlayerPoseType; + +@RegisterVRGameEffect +public class GameEffectRemoteGui extends VRGameEffect { + public static final String ID = "remote_gui"; + + public GameEffectRemoteGui(@NotNull VisorAddon owner) { + super(owner); + } + + @Override + public boolean isGlobal() { + return true; + } + + @Override + public boolean isVisible(@NotNull VRDecorator currentDecorator) { + return true; + } + + @Override + public void render(@NotNull VRRenderPass renderPass, @NotNull PoseStack poseStack, float partialTicks) { + if (VRClientPlayers.getRemotePlayers().isEmpty()) return; + + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.enableDepthTest(); + RenderSystem.setShader(GameRenderer::getPositionColorShader); + + var localRenderPose = ClientContext.localPlayer.getPoseData(PlayerPoseType.RENDER); + var eyePos = RenderPoseHelper.getCameraPosition(renderPass, localRenderPose); + + for (var remotePlayer : VRClientPlayers.getRemotePlayers()) { + if (!remotePlayer.isGuiOpened()) continue; + + VRPose hmdPose = remotePlayer.getPoseData(PlayerPoseType.RENDER).getHmd(); + + poseStack.pushPose(); + poseStack.setIdentity(); + RenderPoseHelper.applyCameraOrientation(renderPass, poseStack); + + Vector3f forward = hmdPose.getDirection().mul(0.45f, new Vector3f()); + Vector3f indicatorPos = new Vector3f(hmdPose.getPosition()).add(forward); + + poseStack.translate(indicatorPos.x - eyePos.x(), indicatorPos.y - eyePos.y(), indicatorPos.z - eyePos.z()); + poseStack.mulPoseMatrix((Matrix4f) hmdPose.getRotation()); + RenderHelper.renderDisplayQuad( + poseStack.last().pose(), + new AtumColorImmutable(40, 45, 60, 140), + 1.6f, 0.9f, + 0.4f + ); + // todo: add Visor's logo + poseStack.popPose(); + } + + RenderSystem.disableBlend(); + } + + @Override + public @NotNull String getId() { + return ID; + } +} \ No newline at end of file diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/server/network/ServerNetworking.java b/visor-core/src/main/java/org/vmstudio/visor/core/server/network/ServerNetworking.java index 45d44c04..481d4c38 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/server/network/ServerNetworking.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/server/network/ServerNetworking.java @@ -154,6 +154,16 @@ public static void sendVRStatePacketOf(ServerPlayer serverPlayer) { ); vrPlayer.setFullHeightLastSent(fullHeight); } + + boolean guiOpened = vrPlayer.isGuiOpened(); + if (guiOpened != vrPlayer.isGuiOpenedLastSent()) { + sendPacketToTrackedVRPlayers( + serverPlayer, + false, + new VROtherGuiStatePayloadToClient(serverPlayer.getUUID(), guiOpened) + ); + vrPlayer.setGuiOpenedLastSent(guiOpened); + } } diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/server/network/ServerPacketHandler.java b/visor-core/src/main/java/org/vmstudio/visor/core/server/network/ServerPacketHandler.java index d1b855ab..ba5b0ff0 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/server/network/ServerPacketHandler.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/server/network/ServerPacketHandler.java @@ -211,6 +211,10 @@ public static void handlePacket(VisorPayloadToServer payloadToServer, ); serverPlayer.connection.ackBlockChangesUpTo(payload.sequence()); } + case GUI_STATE -> { + var payload = (GuiStatePayloadToServer) payloadToServer; + vrPlayer.setGuiOpened(payload.guiOpened()); + } } } diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/server/player/VRServerPlayerImpl.java b/visor-core/src/main/java/org/vmstudio/visor/core/server/player/VRServerPlayerImpl.java index 04f8dc62..c8a8726b 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/server/player/VRServerPlayerImpl.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/server/player/VRServerPlayerImpl.java @@ -56,6 +56,11 @@ public class VRServerPlayerImpl extends VisorPacketReceiver implements VRServerP @Setter private float fullHeightLastSent = 1.0F; + @Setter + private boolean guiOpened; + @Setter + private boolean guiOpenedLastSent; + public VRServerPlayerImpl(ServerPlayer player) { super(player); poseHistoryRelative = new PoseHistoryImpl(poseDataRelative);