mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 06:19:22 +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 UnityEngine;
|
||||
|
||||
|
@ -8,9 +11,11 @@ public class AvatarScaleManager : MonoBehaviour
|
|||
{
|
||||
public static AvatarScaleManager Instance;
|
||||
|
||||
public bool Setting_PersistantHeight = false;
|
||||
|
||||
private Dictionary<string, UniversalAvatarScaler> _networkedScalers;
|
||||
private UniversalAvatarScaler _localAvatarScaler;
|
||||
|
||||
|
||||
#region Unity Methods
|
||||
|
||||
private void Awake()
|
||||
|
@ -25,6 +30,27 @@ public class AvatarScaleManager : MonoBehaviour
|
|||
_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
|
||||
|
||||
#region Local Methods
|
||||
|
@ -33,28 +59,37 @@ public class AvatarScaleManager : MonoBehaviour
|
|||
{
|
||||
if (playerSetup._avatar == null)
|
||||
return;
|
||||
|
||||
if (_localAvatarScaler != null)
|
||||
Destroy(_localAvatarScaler);
|
||||
|
||||
_localAvatarScaler = playerSetup._avatar.AddComponent<UniversalAvatarScaler>();
|
||||
_localAvatarScaler.Initialize(playerSetup._initialAvatarHeight, playerSetup.initialScale);
|
||||
if (_localAvatarScaler == null)
|
||||
{
|
||||
_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)
|
||||
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)
|
||||
{
|
||||
if (_localAvatarScaler == null)
|
||||
if (_localAvatarScaler == null)
|
||||
return;
|
||||
|
||||
_localAvatarScaler.SetHeight(targetHeight);
|
||||
|
||||
_localAvatarScaler.SetTargetHeight(targetHeight);
|
||||
ModNetwork.SendNetworkHeight(targetHeight);
|
||||
|
||||
|
||||
// immediately update play space scale
|
||||
PlayerSetup.Instance.CheckUpdateAvatarScaleToPlaySpaceRelation();
|
||||
}
|
||||
|
@ -64,49 +99,98 @@ public class AvatarScaleManager : MonoBehaviour
|
|||
if (_localAvatarScaler != null)
|
||||
_localAvatarScaler.ResetHeight();
|
||||
}
|
||||
|
||||
|
||||
public float GetHeight()
|
||||
{
|
||||
return (_localAvatarScaler != null) ? _localAvatarScaler.GetHeight() : -1f;
|
||||
return _localAvatarScaler != null ? _localAvatarScaler.GetHeight() : -1f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#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)
|
||||
{
|
||||
if (_networkedScalers.TryGetValue(playerId, out UniversalAvatarScaler scaler))
|
||||
return scaler.GetHeight();
|
||||
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
|
||||
}
|
|
@ -122,7 +122,7 @@ public class ScaledScaleConstraint
|
|||
public void Scale(float scaleFactor)
|
||||
{
|
||||
Component.scaleAtRest = InitialScaleAtRest * scaleFactor;
|
||||
Component.scaleOffset = InitialScaleOffset * scaleFactor;
|
||||
// Component.scaleOffset = InitialScaleOffset * scaleFactor;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using ABI_RC.Core;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI.CCK.Components;
|
||||
using NAK.AvatarScaleMod.ScaledComponents;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Animations;
|
||||
|
@ -22,6 +23,12 @@ public class UniversalAvatarScaler : MonoBehaviour
|
|||
|
||||
#region Variables
|
||||
|
||||
internal bool requestedInitial;
|
||||
|
||||
[NonSerialized]
|
||||
internal string ownerId;
|
||||
|
||||
private Transform _avatarTransform;
|
||||
private CVRAnimatorManager _animatorManager;
|
||||
|
||||
private float _initialHeight;
|
||||
|
@ -33,52 +40,106 @@ public class UniversalAvatarScaler : MonoBehaviour
|
|||
private bool _isLocalAvatar;
|
||||
private bool _heightWasUpdated;
|
||||
|
||||
private bool _isAvatarInstantiated;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity Methods
|
||||
|
||||
private async void Start()
|
||||
{
|
||||
await FindComponentsOfTypeAsync(scalableComponentTypes);
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
ScaleAvatarRoot(); // override animation-based scaling
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ClearComponentLists();
|
||||
if (!_isLocalAvatar) AvatarScaleManager.Instance.RemoveNetworkHeightScaler(ownerId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void Initialize(float initialHeight, Vector3 initialScale)
|
||||
public void Initialize(string playerId = null)
|
||||
{
|
||||
_initialHeight = _targetHeight = initialHeight;
|
||||
_initialScale = initialScale;
|
||||
_scaleFactor = 1f;
|
||||
|
||||
ownerId = playerId;
|
||||
|
||||
_isLocalAvatar = gameObject.layer == 8;
|
||||
|
||||
_animatorManager = _isLocalAvatar
|
||||
? GetComponentInParent<PlayerSetup>().animatorManager
|
||||
: GetComponentInParent<PuppetMaster>()._animatorManager;
|
||||
|
||||
|
||||
_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;
|
||||
|
||||
_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;
|
||||
if (!_isAvatarInstantiated)
|
||||
return;
|
||||
|
||||
_scaleFactor = _targetHeight / _initialHeight;
|
||||
ApplyScaling();
|
||||
}
|
||||
|
||||
public void ResetHeight()
|
||||
{
|
||||
if (Math.Abs(_initialHeight - _targetHeight) < float.Epsilon)
|
||||
return;
|
||||
|
||||
_targetHeight = _initialHeight;
|
||||
_scaleFactor = 1f;
|
||||
|
||||
_heightWasUpdated = true;
|
||||
if (!_isAvatarInstantiated)
|
||||
return;
|
||||
|
||||
_scaleFactor = 1f;
|
||||
ApplyScaling();
|
||||
}
|
||||
|
||||
|
@ -87,13 +148,21 @@ public class UniversalAvatarScaler : MonoBehaviour
|
|||
return _targetHeight;
|
||||
}
|
||||
|
||||
public bool IsValid()
|
||||
{
|
||||
return _isAvatarInstantiated;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private void ScaleAvatarRoot()
|
||||
{
|
||||
transform.localScale = _initialScale * _scaleFactor;
|
||||
if (_avatarTransform == null)
|
||||
return;
|
||||
|
||||
_avatarTransform.localScale = _initialScale * _scaleFactor;
|
||||
}
|
||||
|
||||
private void UpdateAnimatorParameter()
|
||||
|
@ -109,8 +178,9 @@ public class UniversalAvatarScaler : MonoBehaviour
|
|||
|
||||
private void ApplyScaling()
|
||||
{
|
||||
if (!_heightWasUpdated)
|
||||
if (_avatarTransform == null)
|
||||
return;
|
||||
|
||||
_heightWasUpdated = false;
|
||||
|
||||
ScaleAvatarRoot();
|
||||
|
@ -138,10 +208,19 @@ public class UniversalAvatarScaler : MonoBehaviour
|
|||
private readonly List<ScaledPositionConstraint> _scaledPositionConstraints = new List<ScaledPositionConstraint>();
|
||||
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)
|
||||
{
|
||||
var tasks = new List<Task>();
|
||||
var components = GetComponentsInChildren<Component>(true);
|
||||
var components = _avatarTransform.gameObject.GetComponentsInChildren<Component>(true);
|
||||
|
||||
foreach (Component component in components)
|
||||
{
|
||||
|
|
|
@ -8,38 +8,53 @@ using Object = UnityEngine.Object;
|
|||
namespace NAK.AvatarScaleMod.HarmonyPatches;
|
||||
|
||||
internal class PlayerSetupPatches
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.Start))]
|
||||
private static void Postfix_PlayerSetup_Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
GameObject scaleManager = new(nameof(AvatarScaleManager), typeof(AvatarScaleManager));
|
||||
Object.DontDestroyOnLoad(scaleManager);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
AvatarScaleMod.Logger.Error($"Error during the patched method {nameof(Postfix_PlayerSetup_Start)}");
|
||||
AvatarScaleMod.Logger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.SetupAvatar))]
|
||||
private static void Postfix_PlayerSetup_SetupAvatar(ref PlayerSetup __instance)
|
||||
{
|
||||
try
|
||||
{
|
||||
AvatarScaleManager.Instance.OnAvatarInstantiated(__instance);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
AvatarScaleMod.Logger.Error($"Error during the patched method {nameof(Postfix_PlayerSetup_SetupAvatar)}");
|
||||
AvatarScaleMod.Logger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.Start))]
|
||||
private static void Postfix_PlayerSetup_Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
GameObject scaleManager = new (nameof(AvatarScaleManager), typeof(AvatarScaleManager));
|
||||
Object.DontDestroyOnLoad(scaleManager);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
AvatarScaleMod.Logger.Error($"Error during the patched method {nameof(Postfix_PlayerSetup_Start)}");
|
||||
AvatarScaleMod.Logger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.SetupAvatar))]
|
||||
private static void Postfix_PlayerSetup_SetupAvatar(ref PlayerSetup __instance)
|
||||
{
|
||||
try
|
||||
{
|
||||
AvatarScaleManager.Instance.OnAvatarInstantiated(__instance);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
AvatarScaleMod.Logger.Error($"Error during the patched method {nameof(Postfix_PlayerSetup_SetupAvatar)}");
|
||||
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
|
||||
{
|
||||
|
@ -58,6 +73,22 @@ internal class PuppetMasterPatches
|
|||
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
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using MelonLoader;
|
||||
using NAK.AvatarScaleMod.AvatarScaling;
|
||||
|
||||
namespace NAK.AvatarScaleMod;
|
||||
|
||||
|
@ -8,12 +9,12 @@ internal static class ModSettings
|
|||
{
|
||||
public static readonly MelonPreferences_Category Category =
|
||||
MelonPreferences.CreateCategory(nameof(AvatarScaleMod));
|
||||
|
||||
public static MelonPreferences_Entry<bool> PersistantHeight;
|
||||
|
||||
// AvatarScaleTool supported scaling settings
|
||||
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.");
|
||||
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)
|
||||
public static readonly MelonPreferences_Entry<bool> EntryUniversalScaling =
|
||||
|
@ -37,7 +38,6 @@ internal static class ModSettings
|
|||
{
|
||||
EntryEnabled.OnEntryValueChanged.Subscribe(OnEntryEnabledChanged);
|
||||
EntryUseScaleGesture.OnEntryValueChanged.Subscribe(OnEntryUseScaleGestureChanged);
|
||||
EntryPersistAnyways.OnEntryValueChanged.Subscribe(OnEntryPersistAnywaysChanged);
|
||||
EntryUniversalScaling.OnEntryValueChanged.Subscribe(OnEntryUniversalScalingChanged);
|
||||
EntryScaleConstraints.OnEntryValueChanged.Subscribe(OnEntryScaleConstraintsChanged);
|
||||
}
|
||||
|
@ -52,10 +52,7 @@ internal static class ModSettings
|
|||
//AvatarScaleGesture.GestureEnabled = newValue;
|
||||
}
|
||||
|
||||
private static void OnEntryPersistAnywaysChanged(bool oldValue, bool newValue)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private static void OnEntryUniversalScalingChanged(bool oldValue, bool newValue)
|
||||
{
|
||||
|
@ -69,7 +66,15 @@ internal static class ModSettings
|
|||
|
||||
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;
|
||||
//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 NAK.AvatarScaleMod.AvatarScaling;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.AvatarScaleMod.Networking;
|
||||
|
||||
// overcomplicated, but functional
|
||||
// a
|
||||
|
||||
public static class ModNetwork
|
||||
{
|
||||
#region Constants
|
||||
|
@ -14,19 +19,37 @@ public static class ModNetwork
|
|||
private const float ReceiveRateLimit = 0.2f;
|
||||
private const int MaxWarnings = 2;
|
||||
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
|
||||
|
||||
#region Private State
|
||||
|
||||
private static float? OutboundQueue;
|
||||
private static readonly Dictionary<string, QueuedMessage> OutboundQueue = new();
|
||||
private static float LastSentTime;
|
||||
|
||||
private static readonly Dictionary<string, float> InboundQueue = new Dictionary<string, float>();
|
||||
private static readonly Dictionary<string, float> LastReceivedTimes = new Dictionary<string, float>();
|
||||
private static readonly Dictionary<string, QueuedMessage> InboundQueue = new();
|
||||
private static readonly Dictionary<string, float> LastReceivedTimes = new();
|
||||
|
||||
private static readonly Dictionary<string, int> UserWarnings = new Dictionary<string, int>();
|
||||
private static readonly Dictionary<string, float> UserTimeouts = new Dictionary<string, float>();
|
||||
private static readonly Dictionary<string, int> UserWarnings = new();
|
||||
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
|
||||
|
||||
|
@ -36,26 +59,63 @@ public static class ModNetwork
|
|||
{
|
||||
ModNetworkManager.Subscribe(ModId, OnMessageReceived);
|
||||
}
|
||||
|
||||
|
||||
internal static void Update()
|
||||
{
|
||||
ProcessOutboundQueue();
|
||||
ProcessInboundQueue();
|
||||
}
|
||||
|
||||
private static void SendMessageToAll(float height)
|
||||
private static void SendMessage(MessageType messageType, float height, string playerId = null)
|
||||
{
|
||||
using ModNetworkMessage modMsg = new ModNetworkMessage(ModId);
|
||||
modMsg.Write(height);
|
||||
modMsg.Send();
|
||||
//MelonLogger.Msg($"Sending height: {height}");
|
||||
if (!IsConnectedToGameNetwork())
|
||||
return;
|
||||
|
||||
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)
|
||||
{
|
||||
msg.Read(out byte msgTypeRaw);
|
||||
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
|
||||
|
@ -64,7 +124,14 @@ public static class ModNetwork
|
|||
|
||||
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
|
||||
|
@ -73,32 +140,27 @@ public static class ModNetwork
|
|||
|
||||
private static void ProcessOutboundQueue()
|
||||
{
|
||||
if (!OutboundQueue.HasValue)
|
||||
if (OutboundQueue.Count == 0 || Time.time - LastSentTime < SendRateLimit)
|
||||
return;
|
||||
|
||||
if (!(Time.time - LastSentTime >= SendRateLimit))
|
||||
return;
|
||||
foreach (QueuedMessage message in OutboundQueue.Values)
|
||||
SendMessage(message.Type, message.Height, message.TargetPlayer);
|
||||
|
||||
SendMessageToAll(OutboundQueue.Value);
|
||||
OutboundQueue.Clear();
|
||||
LastSentTime = Time.time;
|
||||
OutboundQueue = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#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
|
||||
if (LastReceivedTimes.TryGetValue(userId, out float lastReceivedTime) &&
|
||||
if (LastReceivedTimes.TryGetValue(userId, out var lastReceivedTime) &&
|
||||
Time.time - lastReceivedTime < ReceiveRateLimit)
|
||||
{
|
||||
if (UserWarnings.TryGetValue(userId, out int warnings))
|
||||
if (UserWarnings.TryGetValue(userId, out var warnings))
|
||||
{
|
||||
warnings++;
|
||||
UserWarnings[userId] = warnings;
|
||||
|
@ -107,34 +169,63 @@ public static class ModNetwork
|
|||
{
|
||||
UserTimeouts[userId] = Time.time + TimeoutDuration;
|
||||
MelonLogger.Msg($"User is sending height updates too fast! Applying 10s timeout... : {userId}");
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UserWarnings[userId] = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LastReceivedTimes[userId] = Time.time;
|
||||
UserWarnings.Remove(userId); // Reset warnings
|
||||
// MelonLogger.Msg($"Clearing timeout from user : {userId}");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
foreach (var (userId, height) in InboundQueue)
|
||||
{
|
||||
MelonLogger.Msg($"Applying inbound queued height {height} from : {userId}");
|
||||
AvatarScaleManager.Instance.OnNetworkHeightUpdateReceived(userId, height);
|
||||
}
|
||||
foreach (QueuedMessage message in InboundQueue.Values)
|
||||
switch (message.Type)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
#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)
|
||||
return;
|
||||
|
||||
float currentHeight = AvatarScaleManager.Instance.GetHeight();
|
||||
float currentHeight;
|
||||
const float step = 0.1f;
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.Equals) || Input.GetKeyDown(KeyCode.KeypadPlus))
|
||||
{
|
||||
currentHeight += step;
|
||||
AvatarScaleMod.Logger.Msg($"Networking height: {currentHeight}");
|
||||
currentHeight = AvatarScaleManager.Instance.GetHeight();
|
||||
AvatarScaleManager.Instance.SetHeight(currentHeight + step);
|
||||
currentHeight = AvatarScaleManager.Instance.GetHeight();
|
||||
|
||||
ModNetwork.SendNetworkHeight(currentHeight);
|
||||
AvatarScaleManager.Instance.SetHeight(currentHeight);
|
||||
AvatarScaleMod.Logger.Msg($"Networking height: {currentHeight}");
|
||||
}
|
||||
else if (Input.GetKeyDown(KeyCode.Minus) || Input.GetKeyDown(KeyCode.KeypadMinus))
|
||||
{
|
||||
currentHeight -= step;
|
||||
AvatarScaleMod.Logger.Msg($"Networking height: {currentHeight}");
|
||||
currentHeight = AvatarScaleManager.Instance.GetHeight();
|
||||
AvatarScaleManager.Instance.SetHeight(currentHeight - step);
|
||||
currentHeight = AvatarScaleManager.Instance.GetHeight();
|
||||
|
||||
ModNetwork.SendNetworkHeight(currentHeight);
|
||||
AvatarScaleManager.Instance.SetHeight(currentHeight);
|
||||
AvatarScaleMod.Logger.Msg($"Networking height: {currentHeight}");
|
||||
}
|
||||
else if (Input.GetKeyDown(KeyCode.Backspace))
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue