mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-03 14:59:23 +00:00
[LuaNetworkVariables] Remove exp stuff, fix context properties
This commit is contained in:
parent
ece15e0dfc
commit
e85c1e2f25
10 changed files with 95 additions and 514 deletions
|
@ -18,24 +18,22 @@ public class LuaNetVarsMod : MelonMod
|
||||||
public override void OnInitializeMelon()
|
public override void OnInitializeMelon()
|
||||||
{
|
{
|
||||||
Logger = LoggerInstance;
|
Logger = LoggerInstance;
|
||||||
|
|
||||||
ApplyPatches(typeof(Patches.LuaScriptFactory_Patches));
|
ApplyPatches(typeof(Patches.LuaScriptFactory_Patches));
|
||||||
ApplyPatches(typeof(Patches.CVRSyncHelper_Patches));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnUpdate()
|
// public override void OnUpdate()
|
||||||
{
|
// {
|
||||||
// if (Input.GetKeyDown(KeyCode.F1))
|
// // if (Input.GetKeyDown(KeyCode.F1))
|
||||||
// {
|
// // {
|
||||||
// PlayerSetup.Instance.DropProp("be0b5acc-a987-48dc-a28b-62bd912fe3a0");
|
// // PlayerSetup.Instance.DropProp("be0b5acc-a987-48dc-a28b-62bd912fe3a0");
|
||||||
// }
|
// // }
|
||||||
//
|
// //
|
||||||
// if (Input.GetKeyDown(KeyCode.F2))
|
// // if (Input.GetKeyDown(KeyCode.F2))
|
||||||
// {
|
// // {
|
||||||
// GameObject go = new("TestSyncedObject");
|
// // GameObject go = new("TestSyncedObject");
|
||||||
// go.AddComponent<TestSyncedObject>();
|
// // go.AddComponent<TestSyncedObject>();
|
||||||
// }
|
// // }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#endregion Melon Events
|
#endregion Melon Events
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
using ABI_RC.Core.Base;
|
using ABI_RC.Core.Base;
|
||||||
using ABI.Scripting.CVRSTL.Common;
|
using ABI.Scripting.CVRSTL.Common;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using NAK.LuaNetVars;
|
|
||||||
using MoonSharp.Interpreter;
|
using MoonSharp.Interpreter;
|
||||||
|
|
||||||
namespace NAK.LuaNetVars.Modules;
|
namespace NAK.LuaNetVars.Modules;
|
||||||
|
|
||||||
[PublicAPI] // Indicates that this class is used and should not be considered unused
|
[PublicAPI]
|
||||||
public class LuaNetModule : BaseScriptedStaticWrapper
|
public class LuaNetModule : BaseScriptedStaticWrapper
|
||||||
{
|
{
|
||||||
public const string MODULE_ID = "NetworkModule";
|
public const string MODULE_ID = "NetworkModule";
|
||||||
|
@ -101,6 +100,25 @@ public class LuaNetModule : BaseScriptedStaticWrapper
|
||||||
|
|
||||||
_controller.SendLuaEvent(eventName, args);
|
_controller.SendLuaEvent(eventName, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a Lua event to other clients.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventName">The name of the event to send.</param>
|
||||||
|
/// <param name="args">Optional arguments to send with the event.</param>
|
||||||
|
public void SendLuaEventToUser(string eventName, string userId, params DynValue[] args)
|
||||||
|
{
|
||||||
|
CheckIfCanAccessMethod(nameof(SendLuaEventToUser), false,
|
||||||
|
CVRLuaEnvironmentContext.CLIENT, CVRLuaObjectContext.ALL_BUT_EVENTS, CVRLuaOwnerContext.ANY);
|
||||||
|
|
||||||
|
if (_controller == null)
|
||||||
|
{
|
||||||
|
LuaNetVarsMod.Logger.Error("LuaNetVarController is null.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_controller.SendLuaEventToUser(eventName, userId, args);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the current client is the owner of the synchronized object.
|
/// Checks if the current client is the owner of the synchronized object.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using MoonSharp.Interpreter;
|
using ABI_RC.Core.Networking;
|
||||||
|
using MoonSharp.Interpreter;
|
||||||
using ABI_RC.Core.Player;
|
using ABI_RC.Core.Player;
|
||||||
using ABI_RC.Core.Savior;
|
|
||||||
|
|
||||||
namespace NAK.LuaNetVars;
|
namespace NAK.LuaNetVars;
|
||||||
|
|
||||||
|
@ -12,9 +12,9 @@ public struct LuaEventContext
|
||||||
private double TimeSinceLastInvoke { get; set; }
|
private double TimeSinceLastInvoke { get; set; }
|
||||||
private bool IsLocal { get; set; }
|
private bool IsLocal { get; set; }
|
||||||
|
|
||||||
public static LuaEventContext Create(string senderId, DateTime lastInvokeTime)
|
public static LuaEventContext Create(bool isLocal, string senderId, DateTime lastInvokeTime)
|
||||||
{
|
{
|
||||||
var playerName = CVRPlayerManager.Instance.TryGetPlayerName(senderId);
|
var playerName = isLocal ? AuthManager.Username : CVRPlayerManager.Instance.TryGetPlayerName(senderId);
|
||||||
|
|
||||||
return new LuaEventContext
|
return new LuaEventContext
|
||||||
{
|
{
|
||||||
|
@ -22,7 +22,7 @@ public struct LuaEventContext
|
||||||
SenderName = playerName ?? "Unknown",
|
SenderName = playerName ?? "Unknown",
|
||||||
LastInvokeTime = lastInvokeTime,
|
LastInvokeTime = lastInvokeTime,
|
||||||
TimeSinceLastInvoke = (DateTime.Now - lastInvokeTime).TotalSeconds,
|
TimeSinceLastInvoke = (DateTime.Now - lastInvokeTime).TotalSeconds,
|
||||||
IsLocal = senderId == MetaPort.Instance.ownerId
|
IsLocal = isLocal
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,11 +30,11 @@ public struct LuaEventContext
|
||||||
{
|
{
|
||||||
Table table = new(script)
|
Table table = new(script)
|
||||||
{
|
{
|
||||||
["senderId"] = SenderId,
|
["SenderId"] = SenderId,
|
||||||
["senderName"] = SenderName,
|
["SenderName"] = SenderName,
|
||||||
["lastInvokeTime"] = LastInvokeTime.ToUniversalTime().ToString("O"),
|
["LastInvokeTime"] = LastInvokeTime.ToUniversalTime().ToString("O"),
|
||||||
["timeSinceLastInvoke"] = TimeSinceLastInvoke,
|
["TimeSinceLastInvoke"] = TimeSinceLastInvoke,
|
||||||
["isLocal"] = IsLocal
|
["IsLocal"] = IsLocal
|
||||||
};
|
};
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ using ABI.CCK.Components;
|
||||||
using ABI.Scripting.CVRSTL.Common;
|
using ABI.Scripting.CVRSTL.Common;
|
||||||
using MoonSharp.Interpreter;
|
using MoonSharp.Interpreter;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Coroutine = UnityEngine.Coroutine;
|
||||||
|
|
||||||
namespace NAK.LuaNetVars;
|
namespace NAK.LuaNetVars;
|
||||||
|
|
||||||
|
@ -26,21 +27,23 @@ public partial class LuaNetVarController : MonoBehaviour
|
||||||
private bool _requestInitialSync;
|
private bool _requestInitialSync;
|
||||||
private CVRSpawnable _spawnable;
|
private CVRSpawnable _spawnable;
|
||||||
private CVRObjectSync _objectSync;
|
private CVRObjectSync _objectSync;
|
||||||
|
|
||||||
|
private bool _isInitialized;
|
||||||
|
private Coroutine _syncCoroutine;
|
||||||
|
|
||||||
#region Unity Events
|
#region Unity Events
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
=> _isInitialized = Initialize();
|
||||||
if (!Initialize())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// TODO: a manager script should be in charge of this
|
|
||||||
// TODO: disabling object will kill coroutine
|
|
||||||
StartCoroutine(SendVariableUpdatesCoroutine());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
=> Cleanup();
|
=> Cleanup();
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
=> StartStopVariableUpdatesCoroutine(true);
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
=> StartStopVariableUpdatesCoroutine(false);
|
||||||
|
|
||||||
#endregion Unity Events
|
#endregion Unity Events
|
||||||
|
|
||||||
|
@ -102,9 +105,16 @@ public partial class LuaNetVarController : MonoBehaviour
|
||||||
_hashes.Remove(_uniquePathHash);
|
_hashes.Remove(_uniquePathHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void StartStopVariableUpdatesCoroutine(bool start)
|
||||||
|
{
|
||||||
|
if (_syncCoroutine != null) StopCoroutine(_syncCoroutine);
|
||||||
|
_syncCoroutine = null;
|
||||||
|
if (start) _syncCoroutine = StartCoroutine(SendVariableUpdatesCoroutine());
|
||||||
|
}
|
||||||
|
|
||||||
private System.Collections.IEnumerator SendVariableUpdatesCoroutine()
|
private System.Collections.IEnumerator SendVariableUpdatesCoroutine()
|
||||||
{
|
{
|
||||||
while (true)
|
while (isActiveAndEnabled)
|
||||||
{
|
{
|
||||||
yield return new WaitForSeconds(0.1f);
|
yield return new WaitForSeconds(0.1f);
|
||||||
if (IsSyncOwner()) SendVariableUpdates();
|
if (IsSyncOwner()) SendVariableUpdates();
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using ABI_RC.Core.Savior;
|
using ABI_RC.Core.Savior;
|
||||||
using ABI_RC.Systems.ModNetwork;
|
using ABI_RC.Systems.ModNetwork;
|
||||||
using MoonSharp.Interpreter;
|
using MoonSharp.Interpreter;
|
||||||
using Unity.Services.Authentication.Internal;
|
|
||||||
|
|
||||||
namespace NAK.LuaNetVars
|
namespace NAK.LuaNetVars
|
||||||
{
|
{
|
||||||
|
@ -66,7 +65,7 @@ namespace NAK.LuaNetVars
|
||||||
msg.Read(out int argsCount);
|
msg.Read(out int argsCount);
|
||||||
|
|
||||||
DateTime lastInvokeTime = _eventTracker.GetLastInvokeTimeForSender(eventName, senderId);
|
DateTime lastInvokeTime = _eventTracker.GetLastInvokeTimeForSender(eventName, senderId);
|
||||||
LuaEventContext context = LuaEventContext.Create(senderId, lastInvokeTime);
|
LuaEventContext context = LuaEventContext.Create(false, senderId, lastInvokeTime);
|
||||||
|
|
||||||
// Update tracking
|
// Update tracking
|
||||||
_eventTracker.UpdateInvokeTime(eventName, senderId);
|
_eventTracker.UpdateInvokeTime(eventName, senderId);
|
||||||
|
@ -187,7 +186,7 @@ namespace NAK.LuaNetVars
|
||||||
{
|
{
|
||||||
string senderId = MetaPort.Instance.ownerId;
|
string senderId = MetaPort.Instance.ownerId;
|
||||||
DateTime lastInvokeTime = _eventTracker.GetLastInvokeTimeForSender(eventName, senderId);
|
DateTime lastInvokeTime = _eventTracker.GetLastInvokeTimeForSender(eventName, senderId);
|
||||||
LuaEventContext context = LuaEventContext.Create(senderId, lastInvokeTime);
|
LuaEventContext context = LuaEventContext.Create(true, senderId, lastInvokeTime);
|
||||||
|
|
||||||
// Update tracking
|
// Update tracking
|
||||||
_eventTracker.UpdateInvokeTime(eventName, senderId);
|
_eventTracker.UpdateInvokeTime(eventName, senderId);
|
||||||
|
@ -209,6 +208,32 @@ namespace NAK.LuaNetVars
|
||||||
modMsg.Send();
|
modMsg.Send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void SendLuaEventToUser(string eventName, string userId, DynValue[] args)
|
||||||
|
{
|
||||||
|
string senderId = MetaPort.Instance.ownerId;
|
||||||
|
DateTime lastInvokeTime = _eventTracker.GetLastInvokeTimeForSender(eventName, senderId);
|
||||||
|
LuaEventContext context = LuaEventContext.Create(true, senderId, lastInvokeTime);
|
||||||
|
|
||||||
|
// Update tracking
|
||||||
|
_eventTracker.UpdateInvokeTime(eventName, senderId);
|
||||||
|
|
||||||
|
var argsWithContext = new DynValue[args.Length + 1];
|
||||||
|
argsWithContext[0] = DynValue.FromObject(_luaClientBehaviour.script, context.ToLuaTable(_luaClientBehaviour.script));
|
||||||
|
Array.Copy(args, 0, argsWithContext, 1, args.Length);
|
||||||
|
|
||||||
|
InvokeLuaEvent(eventName, argsWithContext);
|
||||||
|
|
||||||
|
using ModNetworkMessage modMsg = new(ModNetworkID, userId);
|
||||||
|
modMsg.Write((byte)MessageType.LuaEvent);
|
||||||
|
modMsg.Write(eventName);
|
||||||
|
modMsg.Write(args.Length);
|
||||||
|
|
||||||
|
foreach (DynValue arg in args)
|
||||||
|
SerializeDynValue(modMsg, arg);
|
||||||
|
|
||||||
|
modMsg.Send();
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,13 +1,8 @@
|
||||||
using ABI_RC.Core.Base;
|
using ABI.Scripting.CVRSTL.Client;
|
||||||
using ABI_RC.Core.Savior;
|
|
||||||
using ABI_RC.Core.Util;
|
|
||||||
using ABI.CCK.Components;
|
|
||||||
using ABI.Scripting.CVRSTL.Client;
|
|
||||||
using ABI.Scripting.CVRSTL.Common;
|
using ABI.Scripting.CVRSTL.Common;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using MoonSharp.Interpreter;
|
using MoonSharp.Interpreter;
|
||||||
using NAK.LuaNetVars.Modules;
|
using NAK.LuaNetVars.Modules;
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace NAK.LuaNetVars.Patches;
|
namespace NAK.LuaNetVars.Patches;
|
||||||
|
|
||||||
|
@ -28,38 +23,4 @@ internal static class LuaScriptFactory_Patches
|
||||||
__result = LuaNetModule.RegisterUserData(____script, ____context);
|
__result = LuaNetModule.RegisterUserData(____script, ____context);
|
||||||
__instance.RegisteredModules[LuaNetModule.MODULE_ID] = __result; // add module to cache
|
__instance.RegisteredModules[LuaNetModule.MODULE_ID] = __result; // add module to cache
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
internal static class CVRSyncHelper_Patches
|
|
||||||
{
|
|
||||||
[HarmonyPostfix]
|
|
||||||
[HarmonyPatch(typeof(CVRSyncHelper), nameof(CVRSyncHelper.UpdatePropValues))]
|
|
||||||
private static void Postfix_CVRSyncHelper_UpdatePropValues(
|
|
||||||
Vector3 position, Vector3 rotation, Vector3 scale,
|
|
||||||
float[] syncValues, string guid, string instanceId,
|
|
||||||
Span<float> subSyncValues, int numSyncValues, int syncType = 0)
|
|
||||||
{
|
|
||||||
CVRSyncHelper.PropData propData = CVRSyncHelper.Props.Find(prop => prop.InstanceId == instanceId);
|
|
||||||
if (propData == null) return;
|
|
||||||
|
|
||||||
// Update locally stored prop data with new values
|
|
||||||
// as GS does not reply with our own data...
|
|
||||||
|
|
||||||
propData.PositionX = position.x;
|
|
||||||
propData.PositionY = position.y;
|
|
||||||
propData.PositionZ = position.z;
|
|
||||||
propData.RotationX = rotation.x;
|
|
||||||
propData.RotationY = rotation.y;
|
|
||||||
propData.RotationZ = rotation.z;
|
|
||||||
propData.ScaleX = scale.x;
|
|
||||||
propData.ScaleY = scale.y;
|
|
||||||
propData.ScaleZ = scale.z;
|
|
||||||
propData.CustomFloatsAmount = numSyncValues;
|
|
||||||
for (int i = 0; i < numSyncValues; i++)
|
|
||||||
propData.CustomFloats[i] = syncValues[i];
|
|
||||||
|
|
||||||
//propData.SpawnedBy
|
|
||||||
propData.syncedBy = MetaPort.Instance.ownerId;
|
|
||||||
propData.syncType = syncType;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ using System.Reflection;
|
||||||
nameof(NAK.LuaNetVars),
|
nameof(NAK.LuaNetVars),
|
||||||
AssemblyInfoParams.Version,
|
AssemblyInfoParams.Version,
|
||||||
AssemblyInfoParams.Author,
|
AssemblyInfoParams.Author,
|
||||||
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/WhereAmIPointing"
|
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/LuaNetworkVariables"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
|
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
|
||||||
|
@ -27,6 +27,6 @@ using System.Reflection;
|
||||||
namespace NAK.LuaNetVars.Properties;
|
namespace NAK.LuaNetVars.Properties;
|
||||||
internal static class AssemblyInfoParams
|
internal static class AssemblyInfoParams
|
||||||
{
|
{
|
||||||
public const string Version = "1.0.0";
|
public const string Version = "1.0.1";
|
||||||
public const string Author = "NotAKidoS";
|
public const string Author = "NotAKidoS";
|
||||||
}
|
}
|
|
@ -1,326 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using ABI_RC.Core.Savior;
|
|
||||||
using ABI_RC.Systems.ModNetwork;
|
|
||||||
|
|
||||||
namespace NAK.LuaNetVars
|
|
||||||
{
|
|
||||||
public abstract class MNSyncedBehaviour : IDisposable
|
|
||||||
{
|
|
||||||
// Add static property for clarity
|
|
||||||
protected static string LocalUserId => MetaPort.Instance.ownerId;
|
|
||||||
|
|
||||||
protected enum MessageType : byte
|
|
||||||
{
|
|
||||||
OwnershipRequest,
|
|
||||||
OwnershipResponse,
|
|
||||||
OwnershipTransfer,
|
|
||||||
StateRequest,
|
|
||||||
StateUpdate,
|
|
||||||
CustomData
|
|
||||||
}
|
|
||||||
|
|
||||||
protected enum OwnershipResponse : byte
|
|
||||||
{
|
|
||||||
Accepted,
|
|
||||||
Rejected
|
|
||||||
}
|
|
||||||
|
|
||||||
protected readonly string networkId;
|
|
||||||
protected string currentOwnerId;
|
|
||||||
private readonly bool autoAcceptTransfers;
|
|
||||||
private readonly Dictionary<string, Action<bool>> pendingRequests;
|
|
||||||
private bool isInitialized;
|
|
||||||
private bool disposedValue;
|
|
||||||
private bool isSoftOwner = false;
|
|
||||||
private Timer stateRequestTimer;
|
|
||||||
private const int StateRequestTimeout = 3000; // 3 seconds
|
|
||||||
|
|
||||||
public string CurrentOwnerId => currentOwnerId;
|
|
||||||
public bool HasOwnership => currentOwnerId == LocalUserId;
|
|
||||||
|
|
||||||
protected MNSyncedBehaviour(string networkId, string currentOwnerId = "", bool autoAcceptTransfers = false)
|
|
||||||
{
|
|
||||||
this.networkId = networkId;
|
|
||||||
this.currentOwnerId = currentOwnerId;
|
|
||||||
this.autoAcceptTransfers = autoAcceptTransfers;
|
|
||||||
this.pendingRequests = new Dictionary<string, Action<bool>>();
|
|
||||||
|
|
||||||
ModNetworkManager.Subscribe(networkId, OnMessageReceived);
|
|
||||||
|
|
||||||
if (!HasOwnership)
|
|
||||||
RequestInitialState();
|
|
||||||
else
|
|
||||||
isInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RequestInitialState()
|
|
||||||
{
|
|
||||||
using ModNetworkMessage msg = new(networkId);
|
|
||||||
msg.Write((byte)MessageType.StateRequest);
|
|
||||||
msg.Send();
|
|
||||||
|
|
||||||
stateRequestTimer = new Timer(StateRequestTimeoutCallback, null, StateRequestTimeout, Timeout.Infinite);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StateRequestTimeoutCallback(object state)
|
|
||||||
{
|
|
||||||
// If isInitialized is still false, we assume soft ownership
|
|
||||||
if (!isInitialized)
|
|
||||||
{
|
|
||||||
currentOwnerId = LocalUserId;
|
|
||||||
isSoftOwner = true;
|
|
||||||
isInitialized = true;
|
|
||||||
OnOwnershipChanged(currentOwnerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
stateRequestTimer.Dispose();
|
|
||||||
stateRequestTimer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void RequestOwnership(Action<bool> callback = null)
|
|
||||||
{
|
|
||||||
if (HasOwnership)
|
|
||||||
{
|
|
||||||
callback?.Invoke(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using (ModNetworkMessage msg = new(networkId))
|
|
||||||
{
|
|
||||||
msg.Write((byte)MessageType.OwnershipRequest);
|
|
||||||
msg.Send();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callback != null)
|
|
||||||
{
|
|
||||||
pendingRequests[LocalUserId] = callback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void SendNetworkedData(Action<ModNetworkMessage> writeData)
|
|
||||||
{
|
|
||||||
if (!HasOwnership)
|
|
||||||
{
|
|
||||||
Debug.LogWarning($"[MNSyncedBehaviour] Cannot send data without ownership. NetworkId: {networkId}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using (ModNetworkMessage msg = new(networkId))
|
|
||||||
{
|
|
||||||
msg.Write((byte)MessageType.CustomData);
|
|
||||||
writeData(msg);
|
|
||||||
msg.Send();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnMessageReceived(ModNetworkMessage message)
|
|
||||||
{
|
|
||||||
message.Read(out byte type);
|
|
||||||
MessageType messageType = (MessageType)type;
|
|
||||||
|
|
||||||
if (!Enum.IsDefined(typeof(MessageType), messageType))
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (messageType)
|
|
||||||
{
|
|
||||||
case MessageType.OwnershipRequest:
|
|
||||||
if (!HasOwnership) break;
|
|
||||||
HandleOwnershipRequest(message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MessageType.OwnershipResponse:
|
|
||||||
if (message.Sender != currentOwnerId) break;
|
|
||||||
HandleOwnershipResponse(message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MessageType.OwnershipTransfer:
|
|
||||||
if (message.Sender != currentOwnerId) break;
|
|
||||||
currentOwnerId = message.Sender;
|
|
||||||
OnOwnershipChanged(currentOwnerId);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MessageType.StateRequest:
|
|
||||||
if (!HasOwnership) break; // this is the only safeguard against ownership hijacking... idk how to prevent it
|
|
||||||
// TODO: only respond to a StateUpdate if expecting one
|
|
||||||
HandleStateRequest(message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MessageType.StateUpdate:
|
|
||||||
// Accept state updates from current owner or if we have soft ownership
|
|
||||||
if (message.Sender != currentOwnerId && !isSoftOwner) break;
|
|
||||||
HandleStateUpdate(message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MessageType.CustomData:
|
|
||||||
if (message.Sender != currentOwnerId)
|
|
||||||
{
|
|
||||||
// If we have soft ownership and receive data from real owner, accept it
|
|
||||||
if (isSoftOwner && message.Sender != LocalUserId)
|
|
||||||
{
|
|
||||||
currentOwnerId = message.Sender;
|
|
||||||
isSoftOwner = false;
|
|
||||||
OnOwnershipChanged(currentOwnerId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Ignore data from non-owner
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HandleCustomData(message);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void HandleOwnershipRequest(ModNetworkMessage message)
|
|
||||||
{
|
|
||||||
if (!HasOwnership)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string requesterId = message.Sender;
|
|
||||||
var response = autoAcceptTransfers ? OwnershipResponse.Accepted :
|
|
||||||
OnOwnershipRequested(requesterId);
|
|
||||||
|
|
||||||
using (ModNetworkMessage responseMsg = new(networkId))
|
|
||||||
{
|
|
||||||
responseMsg.Write((byte)MessageType.OwnershipResponse);
|
|
||||||
responseMsg.Write((byte)response);
|
|
||||||
responseMsg.Send();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response == OwnershipResponse.Accepted)
|
|
||||||
{
|
|
||||||
TransferOwnership(requesterId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void HandleOwnershipResponse(ModNetworkMessage message)
|
|
||||||
{
|
|
||||||
message.Read(out byte responseByte);
|
|
||||||
OwnershipResponse response = (OwnershipResponse)responseByte;
|
|
||||||
|
|
||||||
if (pendingRequests.TryGetValue(LocalUserId, out var callback))
|
|
||||||
{
|
|
||||||
bool accepted = response == OwnershipResponse.Accepted;
|
|
||||||
callback(accepted);
|
|
||||||
pendingRequests.Remove(LocalUserId);
|
|
||||||
|
|
||||||
// Update ownership locally only if accepted
|
|
||||||
if (accepted)
|
|
||||||
{
|
|
||||||
currentOwnerId = LocalUserId;
|
|
||||||
OnOwnershipChanged(currentOwnerId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void HandleStateRequest(ModNetworkMessage message)
|
|
||||||
{
|
|
||||||
if (!HasOwnership)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using ModNetworkMessage response = new(networkId, message.Sender);
|
|
||||||
response.Write((byte)MessageType.StateUpdate);
|
|
||||||
WriteState(response);
|
|
||||||
response.Send();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void HandleStateUpdate(ModNetworkMessage message)
|
|
||||||
{
|
|
||||||
currentOwnerId = message.Sender;
|
|
||||||
isSoftOwner = false;
|
|
||||||
ReadState(message);
|
|
||||||
isInitialized = true;
|
|
||||||
|
|
||||||
// Dispose of the state request timer if it's still running
|
|
||||||
if (stateRequestTimer != null)
|
|
||||||
{
|
|
||||||
stateRequestTimer.Dispose();
|
|
||||||
stateRequestTimer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void HandleCustomData(ModNetworkMessage message)
|
|
||||||
{
|
|
||||||
if (!isInitialized)
|
|
||||||
{
|
|
||||||
Debug.LogWarning($"[MNSyncedBehaviour] Received custom data before initialization. NetworkId: {networkId}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.Sender != currentOwnerId)
|
|
||||||
{
|
|
||||||
// If we have soft ownership and receive data from real owner, accept it
|
|
||||||
if (isSoftOwner && message.Sender != LocalUserId)
|
|
||||||
{
|
|
||||||
currentOwnerId = message.Sender;
|
|
||||||
isSoftOwner = false;
|
|
||||||
OnOwnershipChanged(currentOwnerId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Ignore data from non-owner
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadCustomData(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void TransferOwnership(string newOwnerId)
|
|
||||||
{
|
|
||||||
using (ModNetworkMessage msg = new(networkId))
|
|
||||||
{
|
|
||||||
msg.Write((byte)MessageType.OwnershipTransfer);
|
|
||||||
msg.Write(newOwnerId); // Include the new owner ID in transfer message
|
|
||||||
msg.Send();
|
|
||||||
}
|
|
||||||
|
|
||||||
currentOwnerId = newOwnerId;
|
|
||||||
OnOwnershipChanged(newOwnerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual OwnershipResponse OnOwnershipRequested(string requesterId)
|
|
||||||
{
|
|
||||||
return OwnershipResponse.Rejected;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnOwnershipChanged(string newOwnerId)
|
|
||||||
{
|
|
||||||
// Override to handle ownership changes
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void WriteState(ModNetworkMessage message) { }
|
|
||||||
protected virtual void ReadState(ModNetworkMessage message) { }
|
|
||||||
protected virtual void ReadCustomData(ModNetworkMessage message) { }
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposedValue)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
ModNetworkManager.Unsubscribe(networkId);
|
|
||||||
pendingRequests.Clear();
|
|
||||||
|
|
||||||
if (stateRequestTimer != null)
|
|
||||||
{
|
|
||||||
stateRequestTimer.Dispose();
|
|
||||||
stateRequestTimer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
disposedValue = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(disposing: true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
using ABI_RC.Systems.ModNetwork;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace NAK.LuaNetVars;
|
|
||||||
|
|
||||||
|
|
||||||
// Test implementation
|
|
||||||
public class TestSyncedBehaviour : MNSyncedBehaviour
|
|
||||||
{
|
|
||||||
private readonly System.Random random = new();
|
|
||||||
private int testValue;
|
|
||||||
private int incrementValue;
|
|
||||||
|
|
||||||
public TestSyncedBehaviour(string networkId) : base(networkId, autoAcceptTransfers: true)
|
|
||||||
{
|
|
||||||
Debug.Log($"[TestSyncedBehaviour] Initialized. NetworkId: {networkId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SendTestMessage()
|
|
||||||
{
|
|
||||||
if (!HasOwnership) return;
|
|
||||||
|
|
||||||
SendNetworkedData(msg => {
|
|
||||||
testValue = random.Next(1000);
|
|
||||||
incrementValue++;
|
|
||||||
msg.Write(testValue);
|
|
||||||
msg.Write(incrementValue);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void WriteState(ModNetworkMessage message)
|
|
||||||
{
|
|
||||||
message.Write(testValue);
|
|
||||||
message.Write(incrementValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void ReadState(ModNetworkMessage message)
|
|
||||||
{
|
|
||||||
message.Read(out testValue);
|
|
||||||
message.Read(out incrementValue);
|
|
||||||
Debug.Log($"[TestSyncedBehaviour] State synchronized. TestValue: {testValue}, IncrementValue: {incrementValue}");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void ReadCustomData(ModNetworkMessage message)
|
|
||||||
{
|
|
||||||
message.Read(out int receivedValue);
|
|
||||||
message.Read(out int receivedIncrement);
|
|
||||||
testValue = receivedValue;
|
|
||||||
incrementValue = receivedIncrement;
|
|
||||||
Debug.Log($"[TestSyncedBehaviour] Received custom data: TestValue: {testValue}, IncrementValue: {incrementValue}");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnOwnershipChanged(string newOwnerId)
|
|
||||||
{
|
|
||||||
Debug.Log($"[TestSyncedBehaviour] Ownership changed to: {newOwnerId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override OwnershipResponse OnOwnershipRequested(string requesterId)
|
|
||||||
{
|
|
||||||
Debug.Log($"[TestSyncedBehaviour] Ownership requested by: {requesterId}");
|
|
||||||
return OwnershipResponse.Accepted;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
using ABI_RC.Core.Savior;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace NAK.LuaNetVars;
|
|
||||||
|
|
||||||
public class TestSyncedObject : MonoBehaviour
|
|
||||||
{
|
|
||||||
private const string TEST_NETWORK_ID = "test.synced.object.1";
|
|
||||||
private TestSyncedBehaviour syncBehaviour;
|
|
||||||
private float messageTimer = 0f;
|
|
||||||
private const float MESSAGE_INTERVAL = 2f;
|
|
||||||
|
|
||||||
private void Start()
|
|
||||||
{
|
|
||||||
syncBehaviour = new TestSyncedBehaviour(TEST_NETWORK_ID);
|
|
||||||
Debug.Log($"TestSyncedObject started. Local Player ID: {MetaPort.Instance.ownerId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
// Request ownership on Space key
|
|
||||||
if (Input.GetKeyDown(KeyCode.Space))
|
|
||||||
{
|
|
||||||
Debug.Log("Requesting ownership...");
|
|
||||||
syncBehaviour.RequestOwnership((success) =>
|
|
||||||
{
|
|
||||||
Debug.Log($"Ownership request {(success ? "accepted" : "rejected")}");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have ownership, send custom data periodically
|
|
||||||
if (syncBehaviour.HasOwnership)
|
|
||||||
{
|
|
||||||
messageTimer += Time.deltaTime;
|
|
||||||
if (messageTimer >= MESSAGE_INTERVAL)
|
|
||||||
{
|
|
||||||
messageTimer = 0f;
|
|
||||||
syncBehaviour.SendTestMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue