diff --git a/.Experimental/LuaNetworkVariables/Main.cs b/.Experimental/LuaNetworkVariables/Main.cs index d8c5def..2a8b2fb 100644 --- a/.Experimental/LuaNetworkVariables/Main.cs +++ b/.Experimental/LuaNetworkVariables/Main.cs @@ -18,24 +18,22 @@ public class LuaNetVarsMod : MelonMod public override void OnInitializeMelon() { Logger = LoggerInstance; - ApplyPatches(typeof(Patches.LuaScriptFactory_Patches)); - ApplyPatches(typeof(Patches.CVRSyncHelper_Patches)); } - public override void OnUpdate() - { - // if (Input.GetKeyDown(KeyCode.F1)) - // { - // PlayerSetup.Instance.DropProp("be0b5acc-a987-48dc-a28b-62bd912fe3a0"); - // } - // - // if (Input.GetKeyDown(KeyCode.F2)) - // { - // GameObject go = new("TestSyncedObject"); - // go.AddComponent(); - // } - } + // public override void OnUpdate() + // { + // // if (Input.GetKeyDown(KeyCode.F1)) + // // { + // // PlayerSetup.Instance.DropProp("be0b5acc-a987-48dc-a28b-62bd912fe3a0"); + // // } + // // + // // if (Input.GetKeyDown(KeyCode.F2)) + // // { + // // GameObject go = new("TestSyncedObject"); + // // go.AddComponent(); + // // } + // } #endregion Melon Events diff --git a/.Experimental/LuaNetworkVariables/NetLuaModule.cs b/.Experimental/LuaNetworkVariables/NetLuaModule.cs index 319fefa..b13bc9b 100644 --- a/.Experimental/LuaNetworkVariables/NetLuaModule.cs +++ b/.Experimental/LuaNetworkVariables/NetLuaModule.cs @@ -1,12 +1,11 @@ using ABI_RC.Core.Base; using ABI.Scripting.CVRSTL.Common; using JetBrains.Annotations; -using NAK.LuaNetVars; using MoonSharp.Interpreter; namespace NAK.LuaNetVars.Modules; -[PublicAPI] // Indicates that this class is used and should not be considered unused +[PublicAPI] public class LuaNetModule : BaseScriptedStaticWrapper { public const string MODULE_ID = "NetworkModule"; @@ -101,6 +100,25 @@ public class LuaNetModule : BaseScriptedStaticWrapper _controller.SendLuaEvent(eventName, args); } + + /// + /// Sends a Lua event to other clients. + /// + /// The name of the event to send. + /// Optional arguments to send with the event. + 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); + } /// /// Checks if the current client is the owner of the synchronized object. diff --git a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaEventContext.cs b/.Experimental/LuaNetworkVariables/NetworkVariables/LuaEventContext.cs index 01fbdc4..6a3fd57 100644 --- a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaEventContext.cs +++ b/.Experimental/LuaNetworkVariables/NetworkVariables/LuaEventContext.cs @@ -1,6 +1,6 @@ -using MoonSharp.Interpreter; +using ABI_RC.Core.Networking; +using MoonSharp.Interpreter; using ABI_RC.Core.Player; -using ABI_RC.Core.Savior; namespace NAK.LuaNetVars; @@ -12,9 +12,9 @@ public struct LuaEventContext private double TimeSinceLastInvoke { 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 { @@ -22,7 +22,7 @@ public struct LuaEventContext SenderName = playerName ?? "Unknown", LastInvokeTime = lastInvokeTime, TimeSinceLastInvoke = (DateTime.Now - lastInvokeTime).TotalSeconds, - IsLocal = senderId == MetaPort.Instance.ownerId + IsLocal = isLocal }; } @@ -30,11 +30,11 @@ public struct LuaEventContext { Table table = new(script) { - ["senderId"] = SenderId, - ["senderName"] = SenderName, - ["lastInvokeTime"] = LastInvokeTime.ToUniversalTime().ToString("O"), - ["timeSinceLastInvoke"] = TimeSinceLastInvoke, - ["isLocal"] = IsLocal + ["SenderId"] = SenderId, + ["SenderName"] = SenderName, + ["LastInvokeTime"] = LastInvokeTime.ToUniversalTime().ToString("O"), + ["TimeSinceLastInvoke"] = TimeSinceLastInvoke, + ["IsLocal"] = IsLocal }; return table; } diff --git a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Base.cs b/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Base.cs index 589c405..811fa9b 100644 --- a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Base.cs +++ b/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Base.cs @@ -5,6 +5,7 @@ using ABI.CCK.Components; using ABI.Scripting.CVRSTL.Common; using MoonSharp.Interpreter; using UnityEngine; +using Coroutine = UnityEngine.Coroutine; namespace NAK.LuaNetVars; @@ -26,21 +27,23 @@ public partial class LuaNetVarController : MonoBehaviour private bool _requestInitialSync; private CVRSpawnable _spawnable; private CVRObjectSync _objectSync; - + + private bool _isInitialized; + private Coroutine _syncCoroutine; + #region Unity Events private void Awake() - { - if (!Initialize()) - return; - - // TODO: a manager script should be in charge of this - // TODO: disabling object will kill coroutine - StartCoroutine(SendVariableUpdatesCoroutine()); - } - + => _isInitialized = Initialize(); + private void OnDestroy() => Cleanup(); + + private void OnEnable() + => StartStopVariableUpdatesCoroutine(true); + + private void OnDisable() + => StartStopVariableUpdatesCoroutine(false); #endregion Unity Events @@ -102,9 +105,16 @@ public partial class LuaNetVarController : MonoBehaviour _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() { - while (true) + while (isActiveAndEnabled) { yield return new WaitForSeconds(0.1f); if (IsSyncOwner()) SendVariableUpdates(); diff --git a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Networking.cs b/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Networking.cs index 47f6415..bed3bb8 100644 --- a/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Networking.cs +++ b/.Experimental/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Networking.cs @@ -1,7 +1,6 @@ using ABI_RC.Core.Savior; using ABI_RC.Systems.ModNetwork; using MoonSharp.Interpreter; -using Unity.Services.Authentication.Internal; namespace NAK.LuaNetVars { @@ -66,7 +65,7 @@ namespace NAK.LuaNetVars msg.Read(out int argsCount); DateTime lastInvokeTime = _eventTracker.GetLastInvokeTimeForSender(eventName, senderId); - LuaEventContext context = LuaEventContext.Create(senderId, lastInvokeTime); + LuaEventContext context = LuaEventContext.Create(false, senderId, lastInvokeTime); // Update tracking _eventTracker.UpdateInvokeTime(eventName, senderId); @@ -187,7 +186,7 @@ namespace NAK.LuaNetVars { string senderId = MetaPort.Instance.ownerId; DateTime lastInvokeTime = _eventTracker.GetLastInvokeTimeForSender(eventName, senderId); - LuaEventContext context = LuaEventContext.Create(senderId, lastInvokeTime); + LuaEventContext context = LuaEventContext.Create(true, senderId, lastInvokeTime); // Update tracking _eventTracker.UpdateInvokeTime(eventName, senderId); @@ -209,6 +208,32 @@ namespace NAK.LuaNetVars 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 } } \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/Patches.cs b/.Experimental/LuaNetworkVariables/Patches.cs index ce861fe..672e1df 100644 --- a/.Experimental/LuaNetworkVariables/Patches.cs +++ b/.Experimental/LuaNetworkVariables/Patches.cs @@ -1,13 +1,8 @@ -using ABI_RC.Core.Base; -using ABI_RC.Core.Savior; -using ABI_RC.Core.Util; -using ABI.CCK.Components; -using ABI.Scripting.CVRSTL.Client; +using ABI.Scripting.CVRSTL.Client; using ABI.Scripting.CVRSTL.Common; using HarmonyLib; using MoonSharp.Interpreter; using NAK.LuaNetVars.Modules; -using UnityEngine; namespace NAK.LuaNetVars.Patches; @@ -28,38 +23,4 @@ internal static class LuaScriptFactory_Patches __result = LuaNetModule.RegisterUserData(____script, ____context); __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 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; - } } \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/Properties/AssemblyInfo.cs b/.Experimental/LuaNetworkVariables/Properties/AssemblyInfo.cs index 4cc49cf..84217ef 100644 --- a/.Experimental/LuaNetworkVariables/Properties/AssemblyInfo.cs +++ b/.Experimental/LuaNetworkVariables/Properties/AssemblyInfo.cs @@ -14,7 +14,7 @@ using System.Reflection; nameof(NAK.LuaNetVars), AssemblyInfoParams.Version, 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")] @@ -27,6 +27,6 @@ using System.Reflection; namespace NAK.LuaNetVars.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.0"; + public const string Version = "1.0.1"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/SyncedBehaviour/MNSyncedBehaviour.cs b/.Experimental/LuaNetworkVariables/SyncedBehaviour/MNSyncedBehaviour.cs deleted file mode 100644 index 3576155..0000000 --- a/.Experimental/LuaNetworkVariables/SyncedBehaviour/MNSyncedBehaviour.cs +++ /dev/null @@ -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> 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>(); - - 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 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 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); - } - } -} \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/SyncedBehaviour/TestSyncedBehaviour.cs b/.Experimental/LuaNetworkVariables/SyncedBehaviour/TestSyncedBehaviour.cs deleted file mode 100644 index 4941aaa..0000000 --- a/.Experimental/LuaNetworkVariables/SyncedBehaviour/TestSyncedBehaviour.cs +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/.Experimental/LuaNetworkVariables/SyncedBehaviour/TestSyncedObject.cs b/.Experimental/LuaNetworkVariables/SyncedBehaviour/TestSyncedObject.cs deleted file mode 100644 index 76d6281..0000000 --- a/.Experimental/LuaNetworkVariables/SyncedBehaviour/TestSyncedObject.cs +++ /dev/null @@ -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(); - } - } - } -} \ No newline at end of file