using System; using System.Collections.Generic; using System.IO; using System.Linq; using Newtonsoft.Json; using UnityEngine; using VelNet; namespace VELConnect { public class VelNetPersist : NetworkComponent { private const float interval = 5f; private double nextUpdate; private bool loading; private const bool debugLogs = false; public string persistId; private void Update() { if (Time.timeAsDouble > nextUpdate && VelNetManager.InRoom && !loading) { nextUpdate = Time.timeAsDouble + interval + UnityEngine.Random.Range(0, interval); if (networkObject.IsMine) { Save(); } } } private void OnEnable() { VelNetManager.OnJoinedRoom += OnJoinedRoom; } private void OnDisable() { VelNetManager.OnJoinedRoom -= OnJoinedRoom; } private void OnJoinedRoom(string roomName) { Load(); } private void Load() { loading = true; if (debugLogs) Debug.Log($"[VelNetPersist] Loading {name}"); if (networkObject.isSceneObject) { // It looks like a PocketBase bug is preventing full filtering from happening: // $"/api/collections/PersistObject/records?filter=(app='{Application.productName}' && room='{VelNetManager.Room}' && network_id='{networkObject.sceneNetworkId}')", VELConnectManager.GetRequestCallback( VELConnectManager.VelConnectUrl + $"/api/collections/PersistObject/records?filter=(app='{Application.productName}')", s => { VELConnectManager.RecordList obj = JsonConvert.DeserializeObject>(s); obj.items = obj.items.Where(i => i.network_id == networkObject.sceneNetworkId.ToString() && i.room == VelNetManager.Room).ToList(); if (obj.items.Count < 1) { Debug.LogError("[VelNetPersist] No data found for " + name); loading = false; return; } else if (obj.items.Count > 1) { Debug.LogError( $"[VelNetPersist] Multiple records found for app='{Application.productName}' && room='{VelNetManager.Room}' && network_id='{networkObject.sceneNetworkId}'. Using the first one."); } LoadData(obj.items.FirstOrDefault()); }, s => { loading = false; }); } else { VELConnectManager.GetRequestCallback(VELConnectManager.VelConnectUrl + "/api/collections/PersistObject/records/" + persistId, s => { VELConnectManager.PersistObject obj = JsonConvert.DeserializeObject(s); LoadData(obj); }, s => { loading = false; }); } } public void LoadData(VELConnectManager.PersistObject obj) { if (string.IsNullOrEmpty(obj.data)) { Debug.LogError($"[VelNetPersist] No data found for {name}"); loading = false; return; } persistId = obj.id; using BinaryReader reader = new BinaryReader(new MemoryStream(Convert.FromBase64String(obj.data))); networkObject.UnpackState(reader); if (debugLogs) Debug.Log($"[VelNetPersist] Loaded {name}"); loading = false; } public void Save(Action successCallback = null) { if (debugLogs) Debug.Log($"[VelNetPersist] Saving {name}"); List syncStateComponents = networkObject.syncedComponents.OfType().ToList(); if (networkObject == null) { Debug.LogError("NetworkObject is null on SyncState", this); return; } using BinaryWriter writer = new BinaryWriter(new MemoryStream()); networkObject.PackState(writer); string data = Convert.ToBase64String(((MemoryStream)writer.BaseStream).ToArray()); // if we have a persistId, update the record, otherwise create a new one if (string.IsNullOrEmpty(persistId)) { Debug.LogWarning($"We don't have an existing persistId, so we are creating a new record for {networkObject.name}"); VELConnectManager.PostRequestCallback(VELConnectManager.VelConnectUrl + "/api/collections/PersistObject/records", JsonConvert.SerializeObject( new VELConnectManager.PersistObject() { app = Application.productName, room = VelNetManager.Room, network_id = networkObject.sceneNetworkId.ToString(), spawned = !networkObject.isSceneObject, name = networkObject.isSceneObject ? networkObject.name : networkObject.prefabName, data = data, }), null, s => { VELConnectManager.PersistObject resp = JsonConvert.DeserializeObject(s); persistId = resp.id; successCallback?.Invoke(resp); }); } else { VELConnectManager.PostRequestCallback(VELConnectManager.VelConnectUrl + "/api/collections/PersistObject/records/" + persistId, JsonConvert.SerializeObject( new VELConnectManager.PersistObject() { app = Application.productName, room = VelNetManager.Room, network_id = networkObject.sceneNetworkId.ToString(), spawned = !networkObject.isSceneObject, name = networkObject.prefabName, data = data, }), null, s => { VELConnectManager.PersistObject resp = JsonConvert.DeserializeObject(s); successCallback?.Invoke(resp); }, method: "PATCH"); } } public void Delete(Action successCallback = null) { if (string.IsNullOrEmpty(persistId)) { Debug.LogError("We can't delete an object that doesn't have a persistId"); return; } VELConnectManager.PostRequestCallback(VELConnectManager.VelConnectUrl + "/api/collections/PersistObject/records/" + persistId, null, null, s => { VELConnectManager.PersistObject resp = JsonConvert.DeserializeObject(s); successCallback?.Invoke(resp); }, Debug.LogError, method: "DELETE"); } public override void ReceiveBytes(byte[] message) { throw new NotImplementedException(); } } }