mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 14:29:25 +00:00
[AvatarScaleMod] Test 2: Height Request
Now requests initial height on instance join instead of syncing every 10s. Reworked inbould & outbound message queue. Fixed potential race condition where scalefactor could become 0. A lot of this is still incredibly jank. Just trying to get things working before cleanup.
This commit is contained in:
parent
d599f57973
commit
c7eb5715ba
7 changed files with 440 additions and 146 deletions
|
@ -1,4 +1,7 @@
|
||||||
using ABI_RC.Core.Player;
|
using ABI_RC.Core.IO;
|
||||||
|
using ABI_RC.Core.Player;
|
||||||
|
using ABI_RC.Core.Player.AvatarTracking;
|
||||||
|
using ABI_RC.Systems.GameEventSystem;
|
||||||
using NAK.AvatarScaleMod.Networking;
|
using NAK.AvatarScaleMod.Networking;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
@ -8,9 +11,11 @@ public class AvatarScaleManager : MonoBehaviour
|
||||||
{
|
{
|
||||||
public static AvatarScaleManager Instance;
|
public static AvatarScaleManager Instance;
|
||||||
|
|
||||||
|
public bool Setting_PersistantHeight = false;
|
||||||
|
|
||||||
private Dictionary<string, UniversalAvatarScaler> _networkedScalers;
|
private Dictionary<string, UniversalAvatarScaler> _networkedScalers;
|
||||||
private UniversalAvatarScaler _localAvatarScaler;
|
private UniversalAvatarScaler _localAvatarScaler;
|
||||||
|
|
||||||
#region Unity Methods
|
#region Unity Methods
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
|
@ -25,6 +30,27 @@ public class AvatarScaleManager : MonoBehaviour
|
||||||
_networkedScalers = new Dictionary<string, UniversalAvatarScaler>();
|
_networkedScalers = new Dictionary<string, UniversalAvatarScaler>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
CVRGameEventSystem.Instance.OnConnected.AddListener(OnInstanceConnected);
|
||||||
|
//SchedulerSystem.AddJob(new SchedulerSystem.Job(ForceHeightUpdate), 0f, 10f, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
CVRGameEventSystem.Instance.OnConnected.RemoveListener(OnInstanceConnected);
|
||||||
|
//SchedulerSystem.RemoveJob(new SchedulerSystem.Job(ForceHeightUpdate));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Game Events
|
||||||
|
|
||||||
|
public void OnInstanceConnected(string instanceId)
|
||||||
|
{
|
||||||
|
SchedulerSystem.AddJob(ModNetwork.RequestHeightSync, 2f, 1f, 1);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Local Methods
|
#region Local Methods
|
||||||
|
@ -33,28 +59,37 @@ public class AvatarScaleManager : MonoBehaviour
|
||||||
{
|
{
|
||||||
if (playerSetup._avatar == null)
|
if (playerSetup._avatar == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_localAvatarScaler != null)
|
|
||||||
Destroy(_localAvatarScaler);
|
|
||||||
|
|
||||||
_localAvatarScaler = playerSetup._avatar.AddComponent<UniversalAvatarScaler>();
|
if (_localAvatarScaler == null)
|
||||||
_localAvatarScaler.Initialize(playerSetup._initialAvatarHeight, playerSetup.initialScale);
|
{
|
||||||
|
_localAvatarScaler = playerSetup.gameObject.AddComponent<UniversalAvatarScaler>();
|
||||||
|
_localAvatarScaler.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
_localAvatarScaler.OnAvatarInstantiated(playerSetup._avatar, playerSetup._initialAvatarHeight,
|
||||||
|
playerSetup.initialScale);
|
||||||
|
|
||||||
|
if (Setting_PersistantHeight && _localAvatarScaler.IsValid())
|
||||||
|
SchedulerSystem.AddJob(() => { ModNetwork.SendNetworkHeight(_localAvatarScaler.GetHeight()); }, 0.5f, 0f, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnAvatarDestroyed()
|
public void OnAvatarDestroyed(PlayerSetup playerSetup)
|
||||||
{
|
{
|
||||||
if (_localAvatarScaler != null)
|
if (_localAvatarScaler != null)
|
||||||
Destroy(_localAvatarScaler);
|
_localAvatarScaler.OnAvatarDestroyed(Setting_PersistantHeight);
|
||||||
|
|
||||||
|
if (Setting_PersistantHeight && _localAvatarScaler.IsValid())
|
||||||
|
SchedulerSystem.AddJob(() => { ModNetwork.SendNetworkHeight(_localAvatarScaler.GetHeight()); }, 0.5f, 0f, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetHeight(float targetHeight)
|
public void SetHeight(float targetHeight)
|
||||||
{
|
{
|
||||||
if (_localAvatarScaler == null)
|
if (_localAvatarScaler == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_localAvatarScaler.SetHeight(targetHeight);
|
_localAvatarScaler.SetTargetHeight(targetHeight);
|
||||||
ModNetwork.SendNetworkHeight(targetHeight);
|
ModNetwork.SendNetworkHeight(targetHeight);
|
||||||
|
|
||||||
// immediately update play space scale
|
// immediately update play space scale
|
||||||
PlayerSetup.Instance.CheckUpdateAvatarScaleToPlaySpaceRelation();
|
PlayerSetup.Instance.CheckUpdateAvatarScaleToPlaySpaceRelation();
|
||||||
}
|
}
|
||||||
|
@ -64,49 +99,98 @@ public class AvatarScaleManager : MonoBehaviour
|
||||||
if (_localAvatarScaler != null)
|
if (_localAvatarScaler != null)
|
||||||
_localAvatarScaler.ResetHeight();
|
_localAvatarScaler.ResetHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
public float GetHeight()
|
public float GetHeight()
|
||||||
{
|
{
|
||||||
return (_localAvatarScaler != null) ? _localAvatarScaler.GetHeight() : -1f;
|
return _localAvatarScaler != null ? _localAvatarScaler.GetHeight() : -1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Network Methods
|
#region Network Methods
|
||||||
|
|
||||||
public void OnNetworkAvatarInstantiated(PuppetMaster puppetMaster)
|
|
||||||
{
|
|
||||||
if (puppetMaster.avatarObject == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string playerId = puppetMaster._playerDescriptor.ownerId;
|
|
||||||
|
|
||||||
if (_networkedScalers.ContainsKey(playerId))
|
|
||||||
_networkedScalers.Remove(playerId);
|
|
||||||
|
|
||||||
UniversalAvatarScaler scaler = puppetMaster.avatarObject.AddComponent<UniversalAvatarScaler>();
|
|
||||||
scaler.Initialize(puppetMaster._initialAvatarHeight, puppetMaster.initialAvatarScale);
|
|
||||||
_networkedScalers[playerId] = scaler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnNetworkAvatarDestroyed(string playerId)
|
|
||||||
{
|
|
||||||
if (_networkedScalers.ContainsKey(playerId))
|
|
||||||
_networkedScalers.Remove(playerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnNetworkHeightUpdateReceived(string playerId, float targetHeight)
|
|
||||||
{
|
|
||||||
if (_networkedScalers.TryGetValue(playerId, out UniversalAvatarScaler scaler))
|
|
||||||
scaler.SetHeight(targetHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float GetNetworkHeight(string playerId)
|
public float GetNetworkHeight(string playerId)
|
||||||
{
|
{
|
||||||
if (_networkedScalers.TryGetValue(playerId, out UniversalAvatarScaler scaler))
|
if (_networkedScalers.TryGetValue(playerId, out UniversalAvatarScaler scaler))
|
||||||
return scaler.GetHeight();
|
return scaler.GetHeight();
|
||||||
return -1f;
|
return -1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we will create a Universal Scaler for only users that send a height update
|
||||||
|
// this is sent at a rate of 10s locally!
|
||||||
|
|
||||||
|
internal void OnNetworkHeightUpdateReceived(string playerId, float targetHeight)
|
||||||
|
{
|
||||||
|
if (_networkedScalers.TryGetValue(playerId, out UniversalAvatarScaler scaler))
|
||||||
|
scaler.SetTargetHeight(targetHeight);
|
||||||
|
else
|
||||||
|
SetupHeightScalerForNetwork(playerId, targetHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void OnNetworkAvatarInstantiated(PuppetMaster puppetMaster)
|
||||||
|
{
|
||||||
|
var playerId = puppetMaster._playerDescriptor.ownerId;
|
||||||
|
if (_networkedScalers.TryGetValue(playerId, out UniversalAvatarScaler scaler))
|
||||||
|
scaler.OnAvatarInstantiated(puppetMaster.avatarObject, puppetMaster._initialAvatarHeight,
|
||||||
|
puppetMaster.initialAvatarScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void OnNetworkAvatarDestroyed(PuppetMaster puppetMaster)
|
||||||
|
{
|
||||||
|
// on disconnect
|
||||||
|
if (puppetMaster == null || puppetMaster._playerDescriptor == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var playerId = puppetMaster._playerDescriptor.ownerId;
|
||||||
|
if (_networkedScalers.TryGetValue(playerId, out UniversalAvatarScaler scaler))
|
||||||
|
scaler.OnAvatarDestroyed();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void RemoveNetworkHeightScaler(string playerId)
|
||||||
|
{
|
||||||
|
if (_networkedScalers.ContainsKey(playerId))
|
||||||
|
{
|
||||||
|
AvatarScaleMod.Logger.Msg(
|
||||||
|
$"Removed user height scaler! This is hopefully due to a disconnect or block. : {playerId}");
|
||||||
|
|
||||||
|
_networkedScalers.Remove(playerId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvatarScaleMod.Logger.Msg(
|
||||||
|
$"Failed to remove a user height scaler! This shouldn't happen. : {playerId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupHeightScalerForNetwork(string playerId, float targetHeight)
|
||||||
|
{
|
||||||
|
CVRPlayerEntity playerEntity =
|
||||||
|
CVRPlayerManager.Instance.NetworkPlayers.Find(players => players.Uuid == playerId);
|
||||||
|
|
||||||
|
PuppetMaster puppetMaster = playerEntity?.PuppetMaster;
|
||||||
|
|
||||||
|
if (playerEntity == null || puppetMaster == null)
|
||||||
|
{
|
||||||
|
AvatarScaleMod.Logger.Error(
|
||||||
|
$"Attempted to set up height scaler for user which does not exist! : {playerId}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvatarScaleMod.Logger.Msg(
|
||||||
|
$"Setting up new height scaler for user which has sent a height update! : {playerId}");
|
||||||
|
|
||||||
|
if (_networkedScalers.ContainsKey(playerId))
|
||||||
|
_networkedScalers.Remove(playerId); // ??
|
||||||
|
|
||||||
|
UniversalAvatarScaler scaler = puppetMaster.gameObject.AddComponent<UniversalAvatarScaler>();
|
||||||
|
scaler.Initialize(playerId);
|
||||||
|
|
||||||
|
scaler.OnAvatarInstantiated(puppetMaster.avatarObject, puppetMaster._initialAvatarHeight,
|
||||||
|
puppetMaster.initialAvatarScale);
|
||||||
|
|
||||||
|
_networkedScalers[playerId] = scaler;
|
||||||
|
|
||||||
|
scaler.SetTargetHeight(targetHeight); // set initial height
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
|
@ -122,7 +122,7 @@ public class ScaledScaleConstraint
|
||||||
public void Scale(float scaleFactor)
|
public void Scale(float scaleFactor)
|
||||||
{
|
{
|
||||||
Component.scaleAtRest = InitialScaleAtRest * scaleFactor;
|
Component.scaleAtRest = InitialScaleAtRest * scaleFactor;
|
||||||
Component.scaleOffset = InitialScaleOffset * scaleFactor;
|
// Component.scaleOffset = InitialScaleOffset * scaleFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using ABI_RC.Core;
|
using ABI_RC.Core;
|
||||||
using ABI_RC.Core.Player;
|
using ABI_RC.Core.Player;
|
||||||
|
using ABI.CCK.Components;
|
||||||
using NAK.AvatarScaleMod.ScaledComponents;
|
using NAK.AvatarScaleMod.ScaledComponents;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Animations;
|
using UnityEngine.Animations;
|
||||||
|
@ -22,6 +23,12 @@ public class UniversalAvatarScaler : MonoBehaviour
|
||||||
|
|
||||||
#region Variables
|
#region Variables
|
||||||
|
|
||||||
|
internal bool requestedInitial;
|
||||||
|
|
||||||
|
[NonSerialized]
|
||||||
|
internal string ownerId;
|
||||||
|
|
||||||
|
private Transform _avatarTransform;
|
||||||
private CVRAnimatorManager _animatorManager;
|
private CVRAnimatorManager _animatorManager;
|
||||||
|
|
||||||
private float _initialHeight;
|
private float _initialHeight;
|
||||||
|
@ -33,52 +40,106 @@ public class UniversalAvatarScaler : MonoBehaviour
|
||||||
private bool _isLocalAvatar;
|
private bool _isLocalAvatar;
|
||||||
private bool _heightWasUpdated;
|
private bool _heightWasUpdated;
|
||||||
|
|
||||||
|
private bool _isAvatarInstantiated;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Unity Methods
|
#region Unity Methods
|
||||||
|
|
||||||
private async void Start()
|
|
||||||
{
|
|
||||||
await FindComponentsOfTypeAsync(scalableComponentTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LateUpdate()
|
private void LateUpdate()
|
||||||
{
|
{
|
||||||
ScaleAvatarRoot(); // override animation-based scaling
|
ScaleAvatarRoot(); // override animation-based scaling
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
ClearComponentLists();
|
||||||
|
if (!_isLocalAvatar) AvatarScaleManager.Instance.RemoveNetworkHeightScaler(ownerId);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Public Methods
|
#region Public Methods
|
||||||
|
|
||||||
public void Initialize(float initialHeight, Vector3 initialScale)
|
public void Initialize(string playerId = null)
|
||||||
{
|
{
|
||||||
_initialHeight = _targetHeight = initialHeight;
|
ownerId = playerId;
|
||||||
_initialScale = initialScale;
|
|
||||||
_scaleFactor = 1f;
|
|
||||||
|
|
||||||
_isLocalAvatar = gameObject.layer == 8;
|
_isLocalAvatar = gameObject.layer == 8;
|
||||||
|
|
||||||
_animatorManager = _isLocalAvatar
|
_animatorManager = _isLocalAvatar
|
||||||
? GetComponentInParent<PlayerSetup>().animatorManager
|
? GetComponentInParent<PlayerSetup>().animatorManager
|
||||||
: GetComponentInParent<PuppetMaster>()._animatorManager;
|
: GetComponentInParent<PuppetMaster>()._animatorManager;
|
||||||
|
|
||||||
_heightWasUpdated = false;
|
_heightWasUpdated = false;
|
||||||
|
_isAvatarInstantiated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetHeight(float height)
|
public async void OnAvatarInstantiated(GameObject avatarObject, float initialHeight, Vector3 initialScale)
|
||||||
{
|
{
|
||||||
_targetHeight = Mathf.Clamp(height, MinHeight, MaxHeight);
|
if (avatarObject == null)
|
||||||
|
{
|
||||||
|
AvatarScaleMod.Logger.Error("Avatar was somehow null?????");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AvatarScaleMod.Logger.Msg($"Avatar Object : {_avatarTransform} : {_avatarTransform == null}");
|
||||||
|
|
||||||
|
if (_isAvatarInstantiated) return;
|
||||||
|
_isAvatarInstantiated = true;
|
||||||
|
|
||||||
|
// if we don't have a queued height update, apply initial scaling
|
||||||
|
if (!_heightWasUpdated)
|
||||||
|
_targetHeight = initialHeight;
|
||||||
|
|
||||||
|
_initialHeight = initialHeight;
|
||||||
|
_initialScale = initialScale;
|
||||||
_scaleFactor = _targetHeight / _initialHeight;
|
_scaleFactor = _targetHeight / _initialHeight;
|
||||||
|
|
||||||
|
_avatarTransform = avatarObject.transform;
|
||||||
|
await FindComponentsOfTypeAsync(scalableComponentTypes);
|
||||||
|
|
||||||
|
ApplyScaling(); // apply queued scaling if avatar was loading
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAvatarDestroyed(bool shouldPersist = false)
|
||||||
|
{
|
||||||
|
if (!_isAvatarInstantiated) return;
|
||||||
|
_isAvatarInstantiated = false;
|
||||||
|
|
||||||
|
AvatarScaleMod.Logger.Msg($"Destroying Avatar Object : {_avatarTransform} : {_avatarTransform == null}");
|
||||||
|
|
||||||
|
_avatarTransform = null;
|
||||||
|
_heightWasUpdated = shouldPersist;
|
||||||
|
ClearComponentLists();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTargetHeight(float height)
|
||||||
|
{
|
||||||
|
if (Math.Abs(height - _targetHeight) < float.Epsilon)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_targetHeight = Mathf.Clamp(height, MinHeight, MaxHeight);
|
||||||
|
|
||||||
_heightWasUpdated = true;
|
_heightWasUpdated = true;
|
||||||
|
if (!_isAvatarInstantiated)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_scaleFactor = _targetHeight / _initialHeight;
|
||||||
ApplyScaling();
|
ApplyScaling();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetHeight()
|
public void ResetHeight()
|
||||||
{
|
{
|
||||||
|
if (Math.Abs(_initialHeight - _targetHeight) < float.Epsilon)
|
||||||
|
return;
|
||||||
|
|
||||||
_targetHeight = _initialHeight;
|
_targetHeight = _initialHeight;
|
||||||
_scaleFactor = 1f;
|
|
||||||
_heightWasUpdated = true;
|
_heightWasUpdated = true;
|
||||||
|
if (!_isAvatarInstantiated)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_scaleFactor = 1f;
|
||||||
ApplyScaling();
|
ApplyScaling();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,13 +148,21 @@ public class UniversalAvatarScaler : MonoBehaviour
|
||||||
return _targetHeight;
|
return _targetHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsValid()
|
||||||
|
{
|
||||||
|
return _isAvatarInstantiated;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Private Methods
|
#region Private Methods
|
||||||
|
|
||||||
private void ScaleAvatarRoot()
|
private void ScaleAvatarRoot()
|
||||||
{
|
{
|
||||||
transform.localScale = _initialScale * _scaleFactor;
|
if (_avatarTransform == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_avatarTransform.localScale = _initialScale * _scaleFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateAnimatorParameter()
|
private void UpdateAnimatorParameter()
|
||||||
|
@ -109,8 +178,9 @@ public class UniversalAvatarScaler : MonoBehaviour
|
||||||
|
|
||||||
private void ApplyScaling()
|
private void ApplyScaling()
|
||||||
{
|
{
|
||||||
if (!_heightWasUpdated)
|
if (_avatarTransform == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_heightWasUpdated = false;
|
_heightWasUpdated = false;
|
||||||
|
|
||||||
ScaleAvatarRoot();
|
ScaleAvatarRoot();
|
||||||
|
@ -138,10 +208,19 @@ public class UniversalAvatarScaler : MonoBehaviour
|
||||||
private readonly List<ScaledPositionConstraint> _scaledPositionConstraints = new List<ScaledPositionConstraint>();
|
private readonly List<ScaledPositionConstraint> _scaledPositionConstraints = new List<ScaledPositionConstraint>();
|
||||||
private readonly List<ScaledScaleConstraint> _scaledScaleConstraints = new List<ScaledScaleConstraint>();
|
private readonly List<ScaledScaleConstraint> _scaledScaleConstraints = new List<ScaledScaleConstraint>();
|
||||||
|
|
||||||
|
private void ClearComponentLists()
|
||||||
|
{
|
||||||
|
_scaledLights.Clear();
|
||||||
|
_scaledAudioSources.Clear();
|
||||||
|
_scaledParentConstraints.Clear();
|
||||||
|
_scaledPositionConstraints.Clear();
|
||||||
|
_scaledScaleConstraints.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task FindComponentsOfTypeAsync(Type[] types)
|
private async Task FindComponentsOfTypeAsync(Type[] types)
|
||||||
{
|
{
|
||||||
var tasks = new List<Task>();
|
var tasks = new List<Task>();
|
||||||
var components = GetComponentsInChildren<Component>(true);
|
var components = _avatarTransform.gameObject.GetComponentsInChildren<Component>(true);
|
||||||
|
|
||||||
foreach (Component component in components)
|
foreach (Component component in components)
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,38 +8,53 @@ using Object = UnityEngine.Object;
|
||||||
namespace NAK.AvatarScaleMod.HarmonyPatches;
|
namespace NAK.AvatarScaleMod.HarmonyPatches;
|
||||||
|
|
||||||
internal class PlayerSetupPatches
|
internal class PlayerSetupPatches
|
||||||
{
|
{
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.Start))]
|
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.Start))]
|
||||||
private static void Postfix_PlayerSetup_Start()
|
private static void Postfix_PlayerSetup_Start()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
GameObject scaleManager = new(nameof(AvatarScaleManager), typeof(AvatarScaleManager));
|
GameObject scaleManager = new (nameof(AvatarScaleManager), typeof(AvatarScaleManager));
|
||||||
Object.DontDestroyOnLoad(scaleManager);
|
Object.DontDestroyOnLoad(scaleManager);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
AvatarScaleMod.Logger.Error($"Error during the patched method {nameof(Postfix_PlayerSetup_Start)}");
|
AvatarScaleMod.Logger.Error($"Error during the patched method {nameof(Postfix_PlayerSetup_Start)}");
|
||||||
AvatarScaleMod.Logger.Error(e);
|
AvatarScaleMod.Logger.Error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.SetupAvatar))]
|
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.SetupAvatar))]
|
||||||
private static void Postfix_PlayerSetup_SetupAvatar(ref PlayerSetup __instance)
|
private static void Postfix_PlayerSetup_SetupAvatar(ref PlayerSetup __instance)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
AvatarScaleManager.Instance.OnAvatarInstantiated(__instance);
|
AvatarScaleManager.Instance.OnAvatarInstantiated(__instance);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
AvatarScaleMod.Logger.Error($"Error during the patched method {nameof(Postfix_PlayerSetup_SetupAvatar)}");
|
AvatarScaleMod.Logger.Error($"Error during the patched method {nameof(Postfix_PlayerSetup_SetupAvatar)}");
|
||||||
AvatarScaleMod.Logger.Error(e);
|
AvatarScaleMod.Logger.Error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.ClearAvatar))]
|
||||||
|
private static void Postfix_PlayerSetup_ClearAvatar(ref PlayerSetup __instance)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AvatarScaleManager.Instance.OnAvatarDestroyed(__instance);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
AvatarScaleMod.Logger.Error($"Error during the patched method {nameof(Postfix_PlayerSetup_ClearAvatar)}");
|
||||||
|
AvatarScaleMod.Logger.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class PuppetMasterPatches
|
internal class PuppetMasterPatches
|
||||||
{
|
{
|
||||||
|
@ -58,6 +73,22 @@ internal class PuppetMasterPatches
|
||||||
AvatarScaleMod.Logger.Error(e);
|
AvatarScaleMod.Logger.Error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(PuppetMaster), nameof(PuppetMaster.AvatarDestroyed))]
|
||||||
|
private static void Postfix_PuppetMaster_AvatarDestroyed(ref PuppetMaster __instance)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AvatarScaleManager.Instance.OnNetworkAvatarDestroyed(__instance);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
AvatarScaleMod.Logger.Error(
|
||||||
|
$"Error during the patched method {nameof(Postfix_PuppetMaster_AvatarDestroyed)}");
|
||||||
|
AvatarScaleMod.Logger.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class GesturePlaneTestPatches
|
internal class GesturePlaneTestPatches
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using MelonLoader;
|
using MelonLoader;
|
||||||
|
using NAK.AvatarScaleMod.AvatarScaling;
|
||||||
|
|
||||||
namespace NAK.AvatarScaleMod;
|
namespace NAK.AvatarScaleMod;
|
||||||
|
|
||||||
|
@ -8,12 +9,12 @@ internal static class ModSettings
|
||||||
{
|
{
|
||||||
public static readonly MelonPreferences_Category Category =
|
public static readonly MelonPreferences_Category Category =
|
||||||
MelonPreferences.CreateCategory(nameof(AvatarScaleMod));
|
MelonPreferences.CreateCategory(nameof(AvatarScaleMod));
|
||||||
|
|
||||||
|
public static MelonPreferences_Entry<bool> PersistantHeight;
|
||||||
|
|
||||||
// AvatarScaleTool supported scaling settings
|
// AvatarScaleTool supported scaling settings
|
||||||
public static readonly MelonPreferences_Entry<bool> EntryEnabled =
|
public static readonly MelonPreferences_Entry<bool> EntryEnabled =
|
||||||
Category.CreateEntry("AvatarScaleTool Scaling", true, description: "Should there be persistant avatar scaling? This only works properly across supported avatars.");
|
Category.CreateEntry("AvatarScaleTool Scaling", true, description: "Should there be persistant avatar scaling? This only works properly across supported avatars.");
|
||||||
public static readonly MelonPreferences_Entry<bool> EntryPersistAnyways =
|
|
||||||
Category.CreateEntry("Persist From Unsupported", true, description: "Should avatar scale persist even from unsupported avatars?");
|
|
||||||
|
|
||||||
// Universal scaling settings (Mod Network, requires others to have mod)
|
// Universal scaling settings (Mod Network, requires others to have mod)
|
||||||
public static readonly MelonPreferences_Entry<bool> EntryUniversalScaling =
|
public static readonly MelonPreferences_Entry<bool> EntryUniversalScaling =
|
||||||
|
@ -37,7 +38,6 @@ internal static class ModSettings
|
||||||
{
|
{
|
||||||
EntryEnabled.OnEntryValueChanged.Subscribe(OnEntryEnabledChanged);
|
EntryEnabled.OnEntryValueChanged.Subscribe(OnEntryEnabledChanged);
|
||||||
EntryUseScaleGesture.OnEntryValueChanged.Subscribe(OnEntryUseScaleGestureChanged);
|
EntryUseScaleGesture.OnEntryValueChanged.Subscribe(OnEntryUseScaleGestureChanged);
|
||||||
EntryPersistAnyways.OnEntryValueChanged.Subscribe(OnEntryPersistAnywaysChanged);
|
|
||||||
EntryUniversalScaling.OnEntryValueChanged.Subscribe(OnEntryUniversalScalingChanged);
|
EntryUniversalScaling.OnEntryValueChanged.Subscribe(OnEntryUniversalScalingChanged);
|
||||||
EntryScaleConstraints.OnEntryValueChanged.Subscribe(OnEntryScaleConstraintsChanged);
|
EntryScaleConstraints.OnEntryValueChanged.Subscribe(OnEntryScaleConstraintsChanged);
|
||||||
}
|
}
|
||||||
|
@ -52,10 +52,7 @@ internal static class ModSettings
|
||||||
//AvatarScaleGesture.GestureEnabled = newValue;
|
//AvatarScaleGesture.GestureEnabled = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnEntryPersistAnywaysChanged(bool oldValue, bool newValue)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnEntryUniversalScalingChanged(bool oldValue, bool newValue)
|
private static void OnEntryUniversalScalingChanged(bool oldValue, bool newValue)
|
||||||
{
|
{
|
||||||
|
@ -69,7 +66,15 @@ internal static class ModSettings
|
||||||
|
|
||||||
public static void InitializeModSettings()
|
public static void InitializeModSettings()
|
||||||
{
|
{
|
||||||
|
PersistantHeight = Category.CreateEntry("Persistant Height", false, description: "Should the avatar height persist between avatar switches?");
|
||||||
|
PersistantHeight.OnEntryValueChanged.Subscribe(OnPersistantHeightChanged);
|
||||||
|
|
||||||
//AvatarScaleManager.UseUniversalScaling = EntryEnabled.Value;
|
//AvatarScaleManager.UseUniversalScaling = EntryEnabled.Value;
|
||||||
//AvatarScaleGesture.GestureEnabled = EntryUseScaleGesture.Value;
|
//AvatarScaleGesture.GestureEnabled = EntryUseScaleGesture.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void OnPersistantHeightChanged(bool oldValue, bool newValue)
|
||||||
|
{
|
||||||
|
AvatarScaleManager.Instance.Setting_PersistantHeight = newValue;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,10 +1,15 @@
|
||||||
using ABI_RC.Systems.ModNetwork;
|
using ABI_RC.Core.Networking;
|
||||||
|
using ABI_RC.Systems.ModNetwork;
|
||||||
|
using DarkRift;
|
||||||
using MelonLoader;
|
using MelonLoader;
|
||||||
using NAK.AvatarScaleMod.AvatarScaling;
|
using NAK.AvatarScaleMod.AvatarScaling;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace NAK.AvatarScaleMod.Networking;
|
namespace NAK.AvatarScaleMod.Networking;
|
||||||
|
|
||||||
|
// overcomplicated, but functional
|
||||||
|
// a
|
||||||
|
|
||||||
public static class ModNetwork
|
public static class ModNetwork
|
||||||
{
|
{
|
||||||
#region Constants
|
#region Constants
|
||||||
|
@ -14,19 +19,37 @@ public static class ModNetwork
|
||||||
private const float ReceiveRateLimit = 0.2f;
|
private const float ReceiveRateLimit = 0.2f;
|
||||||
private const int MaxWarnings = 2;
|
private const int MaxWarnings = 2;
|
||||||
private const float TimeoutDuration = 10f;
|
private const float TimeoutDuration = 10f;
|
||||||
|
|
||||||
|
private class QueuedMessage
|
||||||
|
{
|
||||||
|
public MessageType Type { get; set; }
|
||||||
|
public float Height { get; set; }
|
||||||
|
public string TargetPlayer { get; set; }
|
||||||
|
public string Sender { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Private State
|
#region Private State
|
||||||
|
|
||||||
private static float? OutboundQueue;
|
private static readonly Dictionary<string, QueuedMessage> OutboundQueue = new();
|
||||||
private static float LastSentTime;
|
private static float LastSentTime;
|
||||||
|
|
||||||
private static readonly Dictionary<string, float> InboundQueue = new Dictionary<string, float>();
|
private static readonly Dictionary<string, QueuedMessage> InboundQueue = new();
|
||||||
private static readonly Dictionary<string, float> LastReceivedTimes = new Dictionary<string, float>();
|
private static readonly Dictionary<string, float> LastReceivedTimes = new();
|
||||||
|
|
||||||
private static readonly Dictionary<string, int> UserWarnings = new Dictionary<string, int>();
|
private static readonly Dictionary<string, int> UserWarnings = new();
|
||||||
private static readonly Dictionary<string, float> UserTimeouts = new Dictionary<string, float>();
|
private static readonly Dictionary<string, float> UserTimeouts = new();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Enums
|
||||||
|
|
||||||
|
private enum MessageType : byte
|
||||||
|
{
|
||||||
|
SyncHeight = 0, // just send height
|
||||||
|
RequestHeight = 1 // send height, request height back
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -36,26 +59,63 @@ public static class ModNetwork
|
||||||
{
|
{
|
||||||
ModNetworkManager.Subscribe(ModId, OnMessageReceived);
|
ModNetworkManager.Subscribe(ModId, OnMessageReceived);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Update()
|
internal static void Update()
|
||||||
{
|
{
|
||||||
ProcessOutboundQueue();
|
ProcessOutboundQueue();
|
||||||
ProcessInboundQueue();
|
ProcessInboundQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SendMessageToAll(float height)
|
private static void SendMessage(MessageType messageType, float height, string playerId = null)
|
||||||
{
|
{
|
||||||
using ModNetworkMessage modMsg = new ModNetworkMessage(ModId);
|
if (!IsConnectedToGameNetwork())
|
||||||
modMsg.Write(height);
|
return;
|
||||||
modMsg.Send();
|
|
||||||
//MelonLogger.Msg($"Sending height: {height}");
|
if (!string.IsNullOrEmpty(playerId))
|
||||||
|
{
|
||||||
|
// to specific user
|
||||||
|
using ModNetworkMessage modMsg = new(ModId, playerId);
|
||||||
|
modMsg.Write((byte)messageType);
|
||||||
|
modMsg.Write(height);
|
||||||
|
modMsg.Send();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// to all users
|
||||||
|
using ModNetworkMessage modMsg = new(ModId);
|
||||||
|
modMsg.Write((byte)messageType);
|
||||||
|
modMsg.Write(height);
|
||||||
|
modMsg.Send();
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeDesc = messageType == MessageType.SyncHeight ? "height" : "height request";
|
||||||
|
MelonLogger.Msg($"Sending {typeDesc}: {height}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnMessageReceived(ModNetworkMessage msg)
|
private static void OnMessageReceived(ModNetworkMessage msg)
|
||||||
{
|
{
|
||||||
|
msg.Read(out byte msgTypeRaw);
|
||||||
msg.Read(out float receivedHeight);
|
msg.Read(out float receivedHeight);
|
||||||
ProcessReceivedHeight(msg.Sender, receivedHeight);
|
|
||||||
//MelonLogger.Msg($"Received height from {msg.Sender}: {receivedHeight}");
|
if (!Enum.IsDefined(typeof(MessageType), msgTypeRaw))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// User is in timeout
|
||||||
|
if (UserTimeouts.TryGetValue(msg.Sender, out var timeoutEnd) && Time.time < timeoutEnd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (IsRateLimited(msg.Sender))
|
||||||
|
return;
|
||||||
|
|
||||||
|
QueuedMessage inboundMessage = new()
|
||||||
|
{
|
||||||
|
Type = (MessageType)msgTypeRaw,
|
||||||
|
Height = receivedHeight,
|
||||||
|
Sender = msg.Sender
|
||||||
|
};
|
||||||
|
|
||||||
|
InboundQueue[msg.Sender] = inboundMessage;
|
||||||
|
MelonLogger.Msg($"Received message from {msg.Sender}: {receivedHeight}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -64,7 +124,14 @@ public static class ModNetwork
|
||||||
|
|
||||||
public static void SendNetworkHeight(float newHeight)
|
public static void SendNetworkHeight(float newHeight)
|
||||||
{
|
{
|
||||||
OutboundQueue = newHeight;
|
OutboundQueue["global"] = new QueuedMessage { Type = MessageType.SyncHeight, Height = newHeight };
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RequestHeightSync()
|
||||||
|
{
|
||||||
|
var myCurrentHeight = AvatarScaleManager.Instance.GetHeight();
|
||||||
|
if (myCurrentHeight > 0)
|
||||||
|
OutboundQueue["global"] = new QueuedMessage { Type = MessageType.RequestHeight, Height = myCurrentHeight };
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -73,32 +140,27 @@ public static class ModNetwork
|
||||||
|
|
||||||
private static void ProcessOutboundQueue()
|
private static void ProcessOutboundQueue()
|
||||||
{
|
{
|
||||||
if (!OutboundQueue.HasValue)
|
if (OutboundQueue.Count == 0 || Time.time - LastSentTime < SendRateLimit)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!(Time.time - LastSentTime >= SendRateLimit))
|
foreach (QueuedMessage message in OutboundQueue.Values)
|
||||||
return;
|
SendMessage(message.Type, message.Height, message.TargetPlayer);
|
||||||
|
|
||||||
SendMessageToAll(OutboundQueue.Value);
|
OutboundQueue.Clear();
|
||||||
LastSentTime = Time.time;
|
LastSentTime = Time.time;
|
||||||
OutboundQueue = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Inbound Height Queue
|
#region Inbound Height Queue
|
||||||
|
|
||||||
private static void ProcessReceivedHeight(string userId, float receivedHeight)
|
private static bool IsRateLimited(string userId)
|
||||||
{
|
{
|
||||||
// User is in timeout
|
|
||||||
if (UserTimeouts.TryGetValue(userId, out float timeoutEnd) && Time.time < timeoutEnd)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Rate-limit checking
|
// Rate-limit checking
|
||||||
if (LastReceivedTimes.TryGetValue(userId, out float lastReceivedTime) &&
|
if (LastReceivedTimes.TryGetValue(userId, out var lastReceivedTime) &&
|
||||||
Time.time - lastReceivedTime < ReceiveRateLimit)
|
Time.time - lastReceivedTime < ReceiveRateLimit)
|
||||||
{
|
{
|
||||||
if (UserWarnings.TryGetValue(userId, out int warnings))
|
if (UserWarnings.TryGetValue(userId, out var warnings))
|
||||||
{
|
{
|
||||||
warnings++;
|
warnings++;
|
||||||
UserWarnings[userId] = warnings;
|
UserWarnings[userId] = warnings;
|
||||||
|
@ -107,34 +169,63 @@ public static class ModNetwork
|
||||||
{
|
{
|
||||||
UserTimeouts[userId] = Time.time + TimeoutDuration;
|
UserTimeouts[userId] = Time.time + TimeoutDuration;
|
||||||
MelonLogger.Msg($"User is sending height updates too fast! Applying 10s timeout... : {userId}");
|
MelonLogger.Msg($"User is sending height updates too fast! Applying 10s timeout... : {userId}");
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UserWarnings[userId] = 1;
|
UserWarnings[userId] = 1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
return true;
|
||||||
{
|
|
||||||
LastReceivedTimes[userId] = Time.time;
|
|
||||||
UserWarnings.Remove(userId); // Reset warnings
|
|
||||||
// MelonLogger.Msg($"Clearing timeout from user : {userId}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InboundQueue[userId] = receivedHeight;
|
LastReceivedTimes[userId] = Time.time;
|
||||||
|
UserWarnings.Remove(userId); // Reset warnings
|
||||||
|
// MelonLogger.Msg($"Clearing timeout from user : {userId}");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ProcessInboundQueue()
|
private static void ProcessInboundQueue()
|
||||||
{
|
{
|
||||||
foreach (var (userId, height) in InboundQueue)
|
foreach (QueuedMessage message in InboundQueue.Values)
|
||||||
{
|
switch (message.Type)
|
||||||
MelonLogger.Msg($"Applying inbound queued height {height} from : {userId}");
|
{
|
||||||
AvatarScaleManager.Instance.OnNetworkHeightUpdateReceived(userId, height);
|
case MessageType.RequestHeight:
|
||||||
}
|
{
|
||||||
|
var myCurrentHeight = AvatarScaleManager.Instance.GetHeight();
|
||||||
|
if (myCurrentHeight > 0)
|
||||||
|
OutboundQueue[message.Sender] = new QueuedMessage
|
||||||
|
{
|
||||||
|
Type = MessageType.SyncHeight,
|
||||||
|
Height = myCurrentHeight,
|
||||||
|
TargetPlayer = message.Sender
|
||||||
|
};
|
||||||
|
|
||||||
|
AvatarScaleManager.Instance.OnNetworkHeightUpdateReceived(message.Sender, message.Height);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MessageType.SyncHeight:
|
||||||
|
AvatarScaleManager.Instance.OnNetworkHeightUpdateReceived(message.Sender, message.Height);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
AvatarScaleMod.Logger.Error($"Invalid message type received from: {message.Sender}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
InboundQueue.Clear();
|
InboundQueue.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Private Methods
|
||||||
|
|
||||||
|
private static bool IsConnectedToGameNetwork()
|
||||||
|
{
|
||||||
|
return NetworkManager.Instance != null
|
||||||
|
&& NetworkManager.Instance.GameNetwork != null
|
||||||
|
&& NetworkManager.Instance.GameNetwork.ConnectionState == ConnectionState.Connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
|
@ -16,22 +16,26 @@ public static class ModNetworkDebugger
|
||||||
if (AvatarScaleManager.Instance == null)
|
if (AvatarScaleManager.Instance == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float currentHeight = AvatarScaleManager.Instance.GetHeight();
|
float currentHeight;
|
||||||
const float step = 0.1f;
|
const float step = 0.1f;
|
||||||
|
|
||||||
if (Input.GetKeyDown(KeyCode.Equals) || Input.GetKeyDown(KeyCode.KeypadPlus))
|
if (Input.GetKeyDown(KeyCode.Equals) || Input.GetKeyDown(KeyCode.KeypadPlus))
|
||||||
{
|
{
|
||||||
currentHeight += step;
|
currentHeight = AvatarScaleManager.Instance.GetHeight();
|
||||||
AvatarScaleMod.Logger.Msg($"Networking height: {currentHeight}");
|
AvatarScaleManager.Instance.SetHeight(currentHeight + step);
|
||||||
|
currentHeight = AvatarScaleManager.Instance.GetHeight();
|
||||||
|
|
||||||
ModNetwork.SendNetworkHeight(currentHeight);
|
ModNetwork.SendNetworkHeight(currentHeight);
|
||||||
AvatarScaleManager.Instance.SetHeight(currentHeight);
|
AvatarScaleMod.Logger.Msg($"Networking height: {currentHeight}");
|
||||||
}
|
}
|
||||||
else if (Input.GetKeyDown(KeyCode.Minus) || Input.GetKeyDown(KeyCode.KeypadMinus))
|
else if (Input.GetKeyDown(KeyCode.Minus) || Input.GetKeyDown(KeyCode.KeypadMinus))
|
||||||
{
|
{
|
||||||
currentHeight -= step;
|
currentHeight = AvatarScaleManager.Instance.GetHeight();
|
||||||
AvatarScaleMod.Logger.Msg($"Networking height: {currentHeight}");
|
AvatarScaleManager.Instance.SetHeight(currentHeight - step);
|
||||||
|
currentHeight = AvatarScaleManager.Instance.GetHeight();
|
||||||
|
|
||||||
ModNetwork.SendNetworkHeight(currentHeight);
|
ModNetwork.SendNetworkHeight(currentHeight);
|
||||||
AvatarScaleManager.Instance.SetHeight(currentHeight);
|
AvatarScaleMod.Logger.Msg($"Networking height: {currentHeight}");
|
||||||
}
|
}
|
||||||
else if (Input.GetKeyDown(KeyCode.Backspace))
|
else if (Input.GetKeyDown(KeyCode.Backspace))
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue