Skip to content

Commit

Permalink
Bug fixes and new feature
Browse files Browse the repository at this point in the history
- Fixed event handlers leak with the new Actions system, causing console spam
- Added spectator ghosts
  • Loading branch information
DaXcess committed Mar 4, 2024
1 parent 1bfa22f commit c2a7396
Show file tree
Hide file tree
Showing 13 changed files with 485 additions and 241 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ For more documentation on using the mod, check out the [LethalCompanyVR Thunders

# For developers

If you want to make your mod compatible with LCVR, make sure to check out the [API documentation](API.md). While at the time of writing it doesn't contain much, this might be expanded more in the future.
If you want to make your mod compatible with LCVR, make sure to check out the [API documentation](Docs/API). While at the time of writing it doesn't contain much, this might be expanded more in the future.

Also make sure you know how to use BepInEx Dependencies and assembly referencing properly to make sure that your mod keeps working even when LCVR is not installed _(unless your mod **requires** LCVR to work)_.

Expand Down
Binary file modified Resources/lethalcompanyvr
Binary file not shown.
4 changes: 3 additions & 1 deletion Source/Assets/AssetManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ internal static class AssetManager
public static GameObject animatedLogo;
public static GameObject volumeManager;
public static GameObject spectatorLight;
public static GameObject spectatorGhost;

public static GameObject enemyPrefab;

Expand Down Expand Up @@ -41,7 +42,7 @@ internal static class AssetManager
public static bool LoadAssets()
{
assetBundle = AssetBundle.LoadFromMemory(Properties.Resources.lethalcompanyvr);

if (assetBundle == null)
{
Logger.LogError("Failed to load asset bundle!");
Expand All @@ -56,6 +57,7 @@ public static bool LoadAssets()
volumeManager = assetBundle.LoadAsset<GameObject>("Volume Manager");
enemyPrefab = assetBundle.LoadAsset<GameObject>("DressGirl");
spectatorLight = assetBundle.LoadAsset<GameObject>("Spectator Light");
spectatorGhost = assetBundle.LoadAsset<GameObject>("SpectatorGhost");

defaultInputActions = assetBundle.LoadAsset<InputActionAsset>("XR Input Actions");
nullInputActions = assetBundle.LoadAsset<InputActionAsset>("NullPlayerActions");
Expand Down
83 changes: 83 additions & 0 deletions Source/Networking/DNet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public static class DNet
private static readonly Dictionary<string, ushort> clientByName = [];
private static readonly List<ushort> subscribers = [];

public static VRNetPlayer[] Players => players.Values.ToArray();

public static IEnumerator Initialize()
{
dissonance = GameObject.Find("DissonanceSetup").GetComponent<DissonanceComms>();
Expand Down Expand Up @@ -83,6 +85,11 @@ public static void BroadcastRig(Rig rig)
BroadcastPacket(MessageType.RigData, rig.Serialize());
}

public static void BroadcastSpectatorRig(SpectatorRig rig)
{
BroadcastPacket(MessageType.SpectatorRigData, rig.Serialize());
}

public static void InteractWithLever(bool started)
{
BroadcastPacket(MessageType.Lever, [started ? (byte)1 : (byte)0]);
Expand Down Expand Up @@ -220,6 +227,10 @@ public static void OnPacketReceived(MessageType messageType, ushort sender, byte
case MessageType.RigData:
HandleRigUpdate(sender, data);
break;

case MessageType.SpectatorRigData:
HandleSpectatorRigUpdate(sender, data);
break;

case MessageType.Lever:
HandleInteractWithLever(sender, BitConverter.ToBoolean(data));
Expand Down Expand Up @@ -310,6 +321,15 @@ private static void HandleRigUpdate(ushort sender, byte[] packet)
player.UpdateTargetTransforms(rig);
}

private static void HandleSpectatorRigUpdate(ushort sender, byte[] packet)
{
if (!players.TryGetValue(sender, out var player))
return;

var rig = SpectatorRig.Deserialize(packet);
player.UpdateSpectatorTransforms(rig);
}

private static void HandleInteractWithLever(ushort sender, bool started)
{
if (!players.TryGetValue(sender, out var player))
Expand Down Expand Up @@ -449,6 +469,68 @@ public enum CrouchState : byte
}
}

public struct SpectatorRig
{
public Vector3 headPosition;
public Vector3 headRotation;

public Vector3 leftHandPosition;
public Vector3 leftHandRotation;

public Vector3 rightHandPosition;
public Vector3 rightHandRotation;

public byte[] Serialize()
{
using var mem = new MemoryStream();
using var bw = new BinaryWriter(mem);

bw.Write(headPosition.x);
bw.Write(headPosition.y);
bw.Write(headPosition.z);

bw.Write(headRotation.x);
bw.Write(headRotation.y);
bw.Write(headRotation.z);

bw.Write(leftHandPosition.x);
bw.Write(leftHandPosition.y);
bw.Write(leftHandPosition.z);

bw.Write(leftHandRotation.x);
bw.Write(leftHandRotation.y);
bw.Write(leftHandRotation.z);

bw.Write(rightHandPosition.x);
bw.Write(rightHandPosition.y);
bw.Write(rightHandPosition.z);

bw.Write(rightHandRotation.x);
bw.Write(rightHandRotation.y);
bw.Write(rightHandRotation.z);

return mem.ToArray();
}

public static SpectatorRig Deserialize(byte[] raw)
{
using var mem = new MemoryStream(raw);
using var br = new BinaryReader(mem);

var rig = new SpectatorRig()
{
headPosition = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
headRotation = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
leftHandPosition = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
leftHandRotation = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
rightHandPosition = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
rightHandRotation = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
};

return rig;
}
}

public struct Fingers
{
public const int BYTE_COUNT = 5;
Expand Down Expand Up @@ -496,6 +578,7 @@ public enum MessageType : byte
HandshakeRequest = 16,
HandshakeResponse,
RigData,
SpectatorRigData,
Lever,
CancelChargerAnim,
Muffled
Expand Down
108 changes: 105 additions & 3 deletions Source/Networking/VRNetPlayer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using GameNetcodeStuff;
using LCVR.Assets;
using LCVR.Input;
using LCVR.Player;
using TMPro;
using UnityEngine;
using UnityEngine.Animations.Rigging;
using CrouchState = LCVR.Networking.DNet.Rig.CrouchState;
Expand All @@ -12,6 +14,11 @@ public class VRNetPlayer : MonoBehaviour
private ChainIKConstraintData originalLeftArmConstraintData;
private ChainIKConstraintData originalRightArmConstraintData;

private GameObject playerGhost;
private Transform usernameBillboard;
private CanvasGroup usernameAlpha;
private TextMeshProUGUI usernameText;

private Transform xrOrigin;
private Transform leftController;
private Transform rightController;
Expand All @@ -21,8 +28,8 @@ public class VRNetPlayer : MonoBehaviour
public Transform leftItemHolder;
public Transform rightItemHolder;

public FingerCurler leftFingerCurler;
public FingerCurler rightFingerCurler;
private FingerCurler leftFingerCurler;
private FingerCurler rightFingerCurler;

public Transform camera;

Expand Down Expand Up @@ -89,6 +96,31 @@ private void Awake()
rightFingerCurler = new FingerCurler(Bones.RightHand, false);

BuildVRRig();

// Create spectating player
playerGhost = Instantiate(AssetManager.spectatorGhost, VRSession.Instance.transform);
playerGhost.name = $"Spectating Player: {PlayerController.playerUsername}";

usernameBillboard = playerGhost.GetComponentInChildren<Canvas>().transform;
usernameText = playerGhost.GetComponentInChildren<TextMeshProUGUI>();
usernameAlpha = playerGhost.GetComponentInChildren<CanvasGroup>();

playerGhost.GetComponentInChildren<SpectatorGhost>().player = this;

// Disable rendering ghost until player dies
foreach (var renderer in playerGhost.GetComponentsInChildren<MeshRenderer>())
{
renderer.enabled = false;
}

// Set username text
if (PlayerController.playerSteamId is 76561198438308784 or 76561199575858981)
{
usernameText.color = new Color(0, 1, 1, 1);
usernameText.fontStyle = FontStyles.Bold;
}

usernameText.text = $"<noparse>{PlayerController.playerUsername}</noparse>";
}

private void BuildVRRig()
Expand Down Expand Up @@ -173,6 +205,8 @@ private void Update()
// Arms need to be moved forward when crouched
if (crouchState != CrouchState.None)
xrOrigin.position += transform.forward * 0.55f;

usernameAlpha.alpha -= Time.deltaTime;
}

private void LateUpdate()
Expand All @@ -197,8 +231,50 @@ private void LateUpdate()
{
rightFingerCurler?.Update();
}

// Rotate spectator username billboard
if (StartOfRound.Instance.localPlayerController.localVisorTargetPoint is not null)
{
usernameBillboard.LookAt(StartOfRound.Instance.localPlayerController.localVisorTargetPoint);
}
}

/// <summary>
/// Show the spectator ghost
/// </summary>
public void ShowSpectatorGhost()
{
// Show player ghost when player dies
foreach (var renderer in playerGhost.GetComponentsInChildren<MeshRenderer>())
{
renderer.enabled = true;
}
}

/// <summary>
/// Hide the spectator ghost and username billboard
/// </summary>
public void HideSpectatorGhost()
{
foreach (var renderer in playerGhost.GetComponentsInChildren<MeshRenderer>())
{
renderer.enabled = false;
}

usernameAlpha.alpha = 0f;
}

/// <summary>
/// Show the username of the player (if they are dead)
/// </summary>
public void ShowSpectatorNameBillboard()
{
if (!PlayerController.isPlayerDead)
return;

usernameAlpha.alpha = 1f;
}

public void UpdateTargetTransforms(DNet.Rig rig)
{
leftController.localPosition = rig.leftHandPosition;
Expand All @@ -219,10 +295,36 @@ public void UpdateTargetTransforms(DNet.Rig rig)
}

/// <summary>
/// Properly clean up the IK if a VR player leaves the game
/// Apply transforms for the spectator ghost
/// </summary>
public void UpdateSpectatorTransforms(DNet.SpectatorRig rig)
{
var head = playerGhost.transform.Find("Head");
var leftHand = playerGhost.transform.Find("Hand.L");
var rightHand = playerGhost.transform.Find("Hand.R");

head.position = rig.headPosition;
head.eulerAngles = rig.headRotation;

leftHand.position = rig.leftHandPosition;
leftHand.eulerAngles = rig.leftHandRotation;

rightHand.position = rig.rightHandPosition;
rightHand.eulerAngles = rig.rightHandRotation;

if (StartOfRound.Instance.localPlayerController.localVisorTargetPoint is not null)
{
usernameBillboard.LookAt(StartOfRound.Instance.localPlayerController.localVisorTargetPoint);
}
}

/// <summary>
/// Properly clean up the IK and spectator ghost if a VR player leaves the game
/// </summary>
void OnDestroy()
{
Destroy(playerGhost);

Bones.ResetToPrefabPositions();

Destroy(Bones.LeftArmRig.GetComponent<TwoBoneIKConstraint>());
Expand Down
Loading

0 comments on commit c2a7396

Please sign in to comment.