[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:
NotAKidoS 2023-09-20 20:26:38 -05:00
parent d599f57973
commit c7eb5715ba
7 changed files with 440 additions and 146 deletions

View file

@ -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,6 +11,8 @@ public class AvatarScaleManager : MonoBehaviour
{
public static AvatarScaleManager Instance;
public bool Setting_PersistantHeight = false;
private Dictionary<string, UniversalAvatarScaler> _networkedScalers;
private UniversalAvatarScaler _localAvatarScaler;
@ -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
@ -34,17 +60,26 @@ public class AvatarScaleManager : MonoBehaviour
if (playerSetup._avatar == null)
return;
if (_localAvatarScaler != null)
Destroy(_localAvatarScaler);
if (_localAvatarScaler == null)
{
_localAvatarScaler = playerSetup.gameObject.AddComponent<UniversalAvatarScaler>();
_localAvatarScaler.Initialize();
}
_localAvatarScaler = playerSetup._avatar.AddComponent<UniversalAvatarScaler>();
_localAvatarScaler.Initialize(playerSetup._initialAvatarHeight, playerSetup.initialScale);
_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)
@ -52,7 +87,7 @@ public class AvatarScaleManager : MonoBehaviour
if (_localAvatarScaler == null)
return;
_localAvatarScaler.SetHeight(targetHeight);
_localAvatarScaler.SetTargetHeight(targetHeight);
ModNetwork.SendNetworkHeight(targetHeight);
// immediately update play space scale
@ -67,40 +102,13 @@ public class AvatarScaleManager : MonoBehaviour
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))
@ -108,5 +116,81 @@ public class AvatarScaleManager : MonoBehaviour
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
}

View file

@ -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()

View file

@ -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,29 +40,30 @@ 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;
@ -64,21 +72,74 @@ public class UniversalAvatarScaler : MonoBehaviour
: 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)
{

View file

@ -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.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.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

View file

@ -1,4 +1,5 @@
using MelonLoader;
using NAK.AvatarScaleMod.AvatarScaling;
namespace NAK.AvatarScaleMod;
@ -9,11 +10,11 @@ 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;
}
}

View file

@ -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
@ -15,18 +20,36 @@ public static class ModNetwork
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
@ -43,19 +66,56 @@ public static class ModNetwork
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
}

View file

@ -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))
{