Added Unity project files
This commit is contained in:
@ -0,0 +1,406 @@
|
||||
#if VRC_ENABLE_PLAYER_PERSISTENCE
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRC.SDK3.ClientSim.Persistence
|
||||
{
|
||||
public class ClientSimPlayerDataConverter : JsonConverter<ClientSimPlayerDataTypeUnion>
|
||||
{
|
||||
public override ClientSimPlayerDataTypeUnion ReadJson(JsonReader reader, Type objectType, ClientSimPlayerDataTypeUnion existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
JObject jsonObject = JObject.Load(reader);
|
||||
|
||||
if (jsonObject["type"] == null)
|
||||
{
|
||||
Debug.LogError("Failed to deserialize PlayerData: No type found");
|
||||
return null;
|
||||
}
|
||||
if (jsonObject["value"] == null)
|
||||
{
|
||||
Debug.LogError("Failed to deserialize PlayerData: No value found");
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ClientSimPlayerDataType type = GetPlayerDataType(jsonObject["type"].ToString());
|
||||
object value = GetTypedValue(type, jsonObject["value"], reader, existingValue, serializer);
|
||||
return new ClientSimPlayerDataTypeUnion { Type = type, Value = value };
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Failed to parse PlayerData {objectType} : {e.Message}");
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
private ClientSimPlayerDataType GetPlayerDataType(string typeName)
|
||||
{
|
||||
return typeName switch
|
||||
{
|
||||
"Color" => ClientSimPlayerDataType.Color,
|
||||
"Color32" => ClientSimPlayerDataType.Color32,
|
||||
"Quaternion" => ClientSimPlayerDataType.Quaternion,
|
||||
"Vector2" => ClientSimPlayerDataType.Vector2,
|
||||
"Vector3" => ClientSimPlayerDataType.Vector3,
|
||||
"Vector4" => ClientSimPlayerDataType.Vector4,
|
||||
"Bool" => ClientSimPlayerDataType.WrappedBool,
|
||||
"UByte" => ClientSimPlayerDataType.WrappedUByte,
|
||||
"Byte" => ClientSimPlayerDataType.WrappedByte,
|
||||
"Bytes" => ClientSimPlayerDataType.WrappedBytes,
|
||||
"Float" => ClientSimPlayerDataType.WrappedFloat,
|
||||
"Double" => ClientSimPlayerDataType.WrappedDouble,
|
||||
"Long" => ClientSimPlayerDataType.WrappedLong,
|
||||
"ULong" => ClientSimPlayerDataType.WrappedULong,
|
||||
"Int" => ClientSimPlayerDataType.WrappedInt,
|
||||
"UInt" => ClientSimPlayerDataType.WrappedUInt,
|
||||
"Short" => ClientSimPlayerDataType.WrappedShort,
|
||||
"UShort" => ClientSimPlayerDataType.WrappedUShort,
|
||||
"String" => ClientSimPlayerDataType.WrappedString,
|
||||
_ => ClientSimPlayerDataType.Color
|
||||
};
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, ClientSimPlayerDataTypeUnion value, JsonSerializer serializer)
|
||||
{
|
||||
JsonConverter converter = null;
|
||||
if (value.Type == ClientSimPlayerDataType.Color) converter = new ColorConverter();
|
||||
else if (value.Type == ClientSimPlayerDataType.Color32) converter = new Color32Converter();
|
||||
else if (value.Type == ClientSimPlayerDataType.Quaternion) converter = new QuaternionConverter();
|
||||
else if (value.Type == ClientSimPlayerDataType.Vector2) converter = new Vector2Converter();
|
||||
else if (value.Type == ClientSimPlayerDataType.Vector3) converter = new Vector3Converter();
|
||||
else if (value.Type == ClientSimPlayerDataType.Vector4) converter = new Vector4Converter();
|
||||
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("type");
|
||||
writer.WriteValue(value.Type.ToString().Replace("Wrapped", ""));
|
||||
writer.WritePropertyName("value");
|
||||
if (converter != null)
|
||||
{
|
||||
converter.WriteJson(writer, value.Value, serializer);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValue(value.Value);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
private static object GetTypedValue(ClientSimPlayerDataType type, object deserializedValue, JsonReader reader, ClientSimPlayerDataTypeUnion existingValue, JsonSerializer serializer)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ClientSimPlayerDataType.Color:
|
||||
ColorUtility.TryParseHtmlString("#" + deserializedValue, out Color loadedColor);
|
||||
return loadedColor;
|
||||
|
||||
case ClientSimPlayerDataType.Color32:
|
||||
ColorUtility.TryParseHtmlString("#" + deserializedValue, out Color loadedColor32);
|
||||
return new Color32(
|
||||
(byte)(loadedColor32.r * 255),
|
||||
(byte)(loadedColor32.g * 255),
|
||||
(byte)(loadedColor32.b * 255),
|
||||
(byte)(loadedColor32.a * 255));
|
||||
|
||||
case ClientSimPlayerDataType.Quaternion:
|
||||
JObject quaternionObj = (JObject)deserializedValue;
|
||||
return new Quaternion(
|
||||
(float)quaternionObj["x"],
|
||||
(float)quaternionObj["y"],
|
||||
(float)quaternionObj["z"],
|
||||
(float)quaternionObj["w"]);
|
||||
|
||||
case ClientSimPlayerDataType.Vector2:
|
||||
JObject vector2Obj = (JObject)deserializedValue;
|
||||
return new Vector2(
|
||||
(float)vector2Obj["x"],
|
||||
(float)vector2Obj["y"]);
|
||||
|
||||
case ClientSimPlayerDataType.Vector3:
|
||||
JObject vector3Obj = (JObject)deserializedValue;
|
||||
return new Vector3(
|
||||
(float)vector3Obj["x"],
|
||||
(float)vector3Obj["y"],
|
||||
(float)vector3Obj["z"]);
|
||||
|
||||
case ClientSimPlayerDataType.Vector4:
|
||||
JObject vector4Obj = (JObject)deserializedValue;
|
||||
return new Vector4(
|
||||
(float)vector4Obj["x"],
|
||||
(float)vector4Obj["y"],
|
||||
(float)vector4Obj["z"],
|
||||
(float)vector4Obj["w"]);
|
||||
|
||||
case ClientSimPlayerDataType.WrappedBytes:
|
||||
JValue byteArrayObj = (JValue)deserializedValue;
|
||||
return Convert.FromBase64String((string)byteArrayObj.Value ?? string.Empty);
|
||||
|
||||
case ClientSimPlayerDataType.WrappedBool: return (bool)(JValue)deserializedValue;
|
||||
case ClientSimPlayerDataType.WrappedUByte: return (byte)(JValue)deserializedValue;
|
||||
case ClientSimPlayerDataType.WrappedByte: return (sbyte)(JValue)deserializedValue;
|
||||
case ClientSimPlayerDataType.WrappedFloat: return (float)(JValue)deserializedValue;
|
||||
case ClientSimPlayerDataType.WrappedDouble: return (double)(JValue)deserializedValue;
|
||||
case ClientSimPlayerDataType.WrappedLong: return (long)(JValue)deserializedValue;
|
||||
case ClientSimPlayerDataType.WrappedULong: return (ulong)(JValue)deserializedValue;
|
||||
case ClientSimPlayerDataType.WrappedInt: return (int)(JValue)deserializedValue;
|
||||
case ClientSimPlayerDataType.WrappedUInt: return (uint)(JValue)deserializedValue;
|
||||
case ClientSimPlayerDataType.WrappedShort: return (short)(JValue)deserializedValue;
|
||||
case ClientSimPlayerDataType.WrappedUShort: return (ushort)(JValue)deserializedValue;
|
||||
case ClientSimPlayerDataType.WrappedString: return (string)(JValue)deserializedValue;
|
||||
default: return deserializedValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ColorConverter : JsonConverter<Color>
|
||||
{
|
||||
public override Color ReadJson(JsonReader reader, Type objectType, Color existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
try
|
||||
{
|
||||
ColorUtility.TryParseHtmlString("#" + reader.Value, out Color loadedColor);
|
||||
return loadedColor;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Failed to parse color {objectType} : {ex.Message}");
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, Color color, JsonSerializer serializer)
|
||||
{
|
||||
string val = ColorUtility.ToHtmlStringRGBA(color);
|
||||
writer.WriteValue(val);
|
||||
}
|
||||
}
|
||||
|
||||
internal class Color32Converter : JsonConverter<Color32>
|
||||
{
|
||||
public override Color32 ReadJson(JsonReader reader, Type objectType, Color32 existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
try
|
||||
{
|
||||
ColorUtility.TryParseHtmlString("#" + reader.Value, out Color loadedColor);
|
||||
return loadedColor;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Failed to parse color {objectType} : {ex.Message}");
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, Color32 color, JsonSerializer serializer)
|
||||
{
|
||||
string val = ColorUtility.ToHtmlStringRGBA(color);
|
||||
writer.WriteValue(val);
|
||||
}
|
||||
}
|
||||
|
||||
internal class QuaternionConverter : JsonConverter<Quaternion>
|
||||
{
|
||||
public override Quaternion ReadJson(JsonReader reader, Type objectType, Quaternion existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
try
|
||||
{
|
||||
Debug.Log($"Quaternion: type={objectType} ({existingValue.GetType()}), val={existingValue}");
|
||||
|
||||
float x = 0f, y = 0f, z = 0f, w = 0f;
|
||||
while (reader.Read())
|
||||
{
|
||||
string propertyName = (string)reader.Value;
|
||||
|
||||
reader.Read();
|
||||
switch (propertyName)
|
||||
{
|
||||
case "x":
|
||||
x = Convert.ToSingle(reader.Value);
|
||||
break;
|
||||
case "y":
|
||||
y = Convert.ToSingle(reader.Value);
|
||||
break;
|
||||
case "z":
|
||||
z = Convert.ToSingle(reader.Value);
|
||||
break;
|
||||
case "w":
|
||||
w = Convert.ToSingle(reader.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Quaternion(x, y, z, w);
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Failed to parse quaternion {objectType} : {ex.Message}");
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, Quaternion value, JsonSerializer serializer)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("x");
|
||||
writer.WriteValue(value.x);
|
||||
writer.WritePropertyName("y");
|
||||
writer.WriteValue(value.y);
|
||||
writer.WritePropertyName("z");
|
||||
writer.WriteValue(value.z);
|
||||
writer.WritePropertyName("w");
|
||||
writer.WriteValue(value.w);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
public class Vector2Converter : JsonConverter<Vector2>
|
||||
{
|
||||
public override Vector2 ReadJson(JsonReader reader, Type objectType, Vector2 existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
try
|
||||
{
|
||||
float x = 0f, y = 0f;
|
||||
while (reader.Read())
|
||||
{
|
||||
string propertyName = (string)reader.Value;
|
||||
|
||||
reader.Read();
|
||||
switch (propertyName)
|
||||
{
|
||||
case "x":
|
||||
x = Convert.ToSingle(reader.Value);
|
||||
break;
|
||||
case "y":
|
||||
y = Convert.ToSingle(reader.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Failed to parse Vector2 {objectType} : {ex.Message}");
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, Vector2 value, JsonSerializer serializer)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("x");
|
||||
writer.WriteValue(value.x);
|
||||
writer.WritePropertyName("y");
|
||||
writer.WriteValue(value.y);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
public class Vector3Converter : JsonConverter<Vector3>
|
||||
{
|
||||
public override Vector3 ReadJson(JsonReader reader, Type objectType, Vector3 existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
try
|
||||
{
|
||||
float x = 0f, y = 0f, z = 0f;
|
||||
while (reader.Read())
|
||||
{
|
||||
string propertyName = (string)reader.Value;
|
||||
|
||||
reader.Read();
|
||||
switch (propertyName)
|
||||
{
|
||||
case "x":
|
||||
x = Convert.ToSingle(reader.Value);
|
||||
break;
|
||||
case "y":
|
||||
y = Convert.ToSingle(reader.Value);
|
||||
break;
|
||||
case "z":
|
||||
z = Convert.ToSingle(reader.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Vector3(x, y, z);
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Failed to parse Vector3 {objectType} : {ex.Message}");
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, Vector3 value, JsonSerializer serializer)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("x");
|
||||
writer.WriteValue(value.x);
|
||||
writer.WritePropertyName("y");
|
||||
writer.WriteValue(value.y);
|
||||
writer.WritePropertyName("z");
|
||||
writer.WriteValue(value.z);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
public class Vector4Converter : JsonConverter<Vector4>
|
||||
{
|
||||
public override Vector4 ReadJson(JsonReader reader, Type objectType, Vector4 existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
try
|
||||
{
|
||||
float x = 0f, y = 0f, z = 0f, w = 0f;
|
||||
while (reader.Read())
|
||||
{
|
||||
string propertyName = (string)reader.Value;
|
||||
|
||||
reader.Read();
|
||||
switch (propertyName)
|
||||
{
|
||||
case "x":
|
||||
x = Convert.ToSingle(reader.Value);
|
||||
break;
|
||||
case "y":
|
||||
y = Convert.ToSingle(reader.Value);
|
||||
break;
|
||||
case "z":
|
||||
z = Convert.ToSingle(reader.Value);
|
||||
break;
|
||||
case "w":
|
||||
w = Convert.ToSingle(reader.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Vector4(x, y, z, w);
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Failed to parse Vector4 {objectType} : {ex.Message}");
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, Vector4 value, JsonSerializer serializer)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("x");
|
||||
writer.WriteValue(value.x);
|
||||
writer.WritePropertyName("y");
|
||||
writer.WriteValue(value.y);
|
||||
writer.WritePropertyName("z");
|
||||
writer.WriteValue(value.z);
|
||||
writer.WritePropertyName("w");
|
||||
writer.WriteValue(value.w);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d47ee12e2ac84367b7e3ced0dc76be28
|
||||
timeCreated: 1708717453
|
||||
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
#if VRC_ENABLE_PLAYER_PERSISTENCE
|
||||
namespace VRC.SDK3.ClientSim.Persistence
|
||||
{
|
||||
public class ClientSimPlayerDataPair
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public ClientSimPlayerDataTypeUnion Value { get; set; }
|
||||
public DateTime LastUpdated;
|
||||
|
||||
|
||||
public ClientSimPlayerDataPair() {
|
||||
Key = null;
|
||||
Value = null;
|
||||
LastUpdated = DateTime.Now;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7da7fcefc4434d3e9b5e16edf46de990
|
||||
timeCreated: 1708638443
|
||||
@ -0,0 +1,884 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using VRC.SDKBase;
|
||||
|
||||
#if VRC_ENABLE_PLAYER_PERSISTENCE
|
||||
using UnityEngine.SceneManagement;
|
||||
using VRC.SDK3.Data;
|
||||
using VRC.SDK3.Persistence;
|
||||
using VRC.Udon;
|
||||
#endif
|
||||
|
||||
namespace VRC.SDK3.ClientSim.Persistence
|
||||
{
|
||||
[AddComponentMenu("")] // hides component in Add Component menu
|
||||
public class ClientSimPlayerDataStorage : ClientSimBehaviour
|
||||
{
|
||||
#if VRC_ENABLE_PLAYER_PERSISTENCE
|
||||
public static string PlayerDataFolder => Path.Combine("ClientSimStorage", "PlayerData");
|
||||
internal static string PlayerDataFilePath(VRCPlayerApi player)
|
||||
{
|
||||
string root = Path.GetDirectoryName(Application.dataPath);
|
||||
string path = Path.Combine(root, PlayerDataFolder);
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
return path + "/PlayerData_" + $"{player.playerId}" + $"_{SceneManager.GetActiveScene().name}" + ".json";
|
||||
}
|
||||
|
||||
private VRCPlayerApi _player;
|
||||
private IClientSimUdonEventSender _udonEventSender;
|
||||
private IClientSimEventDispatcher _eventDispatcher;
|
||||
|
||||
private readonly Dictionary<string, ClientSimPlayerDataPair> leData = new();
|
||||
private readonly Dictionary<string, PlayerData.Info> localInfoChanges = new();
|
||||
private readonly List<PlayerData.Info> queuedPlayerDataUpdates = new();
|
||||
|
||||
private bool doDecode;
|
||||
private bool isDoneDecoding;
|
||||
private bool hasPostedPlayerDataDecoded;
|
||||
private bool hasPostedPlayerRestored;
|
||||
|
||||
public int Size
|
||||
=> System.Text.Encoding.UTF8.GetByteCount(JsonConvert.SerializeObject(leData, Formatting.Indented, new JsonSerializerSettings
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
||||
}));
|
||||
|
||||
public IEnumerable<string> GetKeys()
|
||||
{
|
||||
return leData.Keys;
|
||||
}
|
||||
|
||||
public bool HasKey(string key) => leData.ContainsKey(key);
|
||||
|
||||
public Type GetType(string key)
|
||||
{
|
||||
if (!leData.TryGetValue(key, out ClientSimPlayerDataPair value))
|
||||
return null;
|
||||
|
||||
switch (value.Value.Type)
|
||||
{
|
||||
default:
|
||||
case ClientSimPlayerDataType.None:
|
||||
return null;
|
||||
case ClientSimPlayerDataType.Vector2:
|
||||
return typeof(Vector2);
|
||||
case ClientSimPlayerDataType.Vector3:
|
||||
return typeof(Vector3);
|
||||
case ClientSimPlayerDataType.Vector4:
|
||||
return typeof(Vector4);
|
||||
case ClientSimPlayerDataType.Quaternion:
|
||||
return typeof(Quaternion);
|
||||
case ClientSimPlayerDataType.Color:
|
||||
return typeof(Color);
|
||||
case ClientSimPlayerDataType.Color32:
|
||||
return typeof(Color32);
|
||||
case ClientSimPlayerDataType.WrappedString:
|
||||
return typeof(string);
|
||||
case ClientSimPlayerDataType.WrappedShort:
|
||||
return typeof(short);
|
||||
case ClientSimPlayerDataType.WrappedUShort:
|
||||
return typeof(ushort);
|
||||
case ClientSimPlayerDataType.WrappedInt:
|
||||
return typeof(int);
|
||||
case ClientSimPlayerDataType.WrappedUInt:
|
||||
return typeof(uint);
|
||||
case ClientSimPlayerDataType.WrappedLong:
|
||||
return typeof(long);
|
||||
case ClientSimPlayerDataType.WrappedULong:
|
||||
return typeof(ulong);
|
||||
case ClientSimPlayerDataType.WrappedFloat:
|
||||
return typeof(float);
|
||||
case ClientSimPlayerDataType.WrappedDouble:
|
||||
return typeof(double);
|
||||
case ClientSimPlayerDataType.WrappedBool:
|
||||
return typeof(bool);
|
||||
case ClientSimPlayerDataType.WrappedByte:
|
||||
return typeof(sbyte);
|
||||
case ClientSimPlayerDataType.WrappedUByte:
|
||||
return typeof(byte);
|
||||
case ClientSimPlayerDataType.WrappedBytes:
|
||||
return typeof(byte[]);
|
||||
}
|
||||
}
|
||||
|
||||
#region Setters
|
||||
|
||||
private PlayerData.State _SetWrapper(string key, Func<ClientSimPlayerDataPair, bool> set, bool isRestore, bool flushChanges, DateTime lastUpdated)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
throw new ArgumentException("Key was invalid");
|
||||
|
||||
if (!isRestore && leData.TryGetValue(key, out ClientSimPlayerDataPair value))
|
||||
{
|
||||
if (set(value))
|
||||
{
|
||||
value.LastUpdated = DateTime.Now;
|
||||
localInfoChanges[key] = new PlayerData.Info(key, PlayerData.State.Changed);
|
||||
if (flushChanges)
|
||||
{
|
||||
FlushLocalInfoChanges();
|
||||
}
|
||||
return PlayerData.State.Changed;
|
||||
}
|
||||
return PlayerData.State.Unchanged;
|
||||
}
|
||||
|
||||
value = new ClientSimPlayerDataPair { Key = key, LastUpdated = isRestore ? lastUpdated : DateTime.Now };
|
||||
leData[key] = value;
|
||||
set(value);
|
||||
|
||||
var state = isRestore ? PlayerData.State.Restored : PlayerData.State.Added;
|
||||
localInfoChanges[key] = new PlayerData.Info(key, state);
|
||||
if (flushChanges)
|
||||
{
|
||||
FlushLocalInfoChanges();
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
public PlayerData.State SetBool(string key, bool value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.WrappedBool)
|
||||
{
|
||||
if (pair.Value.AsWrappedBool() == value)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.WrappedBool,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetByte(string key, byte value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.WrappedUByte)
|
||||
{
|
||||
if (pair.Value.AsWrappedUByte() == value)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.WrappedUByte,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetSByte(string key, sbyte value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.WrappedByte)
|
||||
{
|
||||
if (pair.Value.AsWrappedSByte() == value)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.WrappedByte,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetBytes(string key, byte[] value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.WrappedBytes)
|
||||
{
|
||||
if (pair.Value.AsWrappedBytes().SequenceEqual(value))
|
||||
return false;
|
||||
|
||||
pair.Value.Value = value.ToArray();
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.WrappedBytes,
|
||||
Value = value.ToArray()
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetString(string key, string value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.WrappedString)
|
||||
{
|
||||
if (pair.Value.AsWrappedString() == value)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.WrappedString,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetShort(string key, short value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.WrappedShort)
|
||||
{
|
||||
if (pair.Value.AsWrappedShort() == value)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.WrappedShort,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetUShort(string key, ushort value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.WrappedUShort)
|
||||
{
|
||||
if (pair.Value.AsWrappedUShort() == value)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.WrappedUShort,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetInt(string key, int value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.WrappedInt)
|
||||
{
|
||||
if (pair.Value.AsWrappedInt() == value)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.WrappedInt,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetUInt(string key, uint value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.WrappedUInt)
|
||||
{
|
||||
if (pair.Value.AsWrappedUInt() == value)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.WrappedUInt,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetLong(string key, long value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.WrappedLong)
|
||||
{
|
||||
if (pair.Value.AsWrappedLong() == value)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.WrappedLong,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetULong(string key, ulong value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.WrappedULong)
|
||||
{
|
||||
if (pair.Value.AsWrappedULong() == value)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.WrappedULong,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetFloat(string key, float value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.WrappedFloat)
|
||||
{
|
||||
if (Math.Abs(pair.Value.AsWrappedFloat() - value) < 0.000001f)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.WrappedFloat,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetDouble(string key, double value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.WrappedDouble)
|
||||
{
|
||||
if (Math.Abs(pair.Value.AsWrappedDouble() - value) < 0.000001)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.WrappedDouble,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetVector2(string key, Vector2 value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.Vector2)
|
||||
{
|
||||
if (Math.Abs(pair.Value.AsVector2().x - value.x) < 0.000001f
|
||||
&& Math.Abs(pair.Value.AsVector2().y - value.y) < 0.000001f)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.Vector2,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetVector3(string key, Vector3 value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.Vector3)
|
||||
{
|
||||
if (Math.Abs(pair.Value.AsVector3().x - value.x) < 0.000001f
|
||||
&& Math.Abs(pair.Value.AsVector3().y - value.y) < 0.000001f
|
||||
&& Math.Abs(pair.Value.AsVector3().z - value.z) < 0.000001f)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.Vector3,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetVector4(string key, Vector4 value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.Vector4)
|
||||
{
|
||||
if (Math.Abs(pair.Value.AsVector4().x - value.x) < 0.000001f
|
||||
&& Math.Abs(pair.Value.AsVector4().y - value.y) < 0.000001f
|
||||
&& Math.Abs(pair.Value.AsVector4().z - value.z) < 0.000001f
|
||||
&& Math.Abs(pair.Value.AsVector4().w - value.w) < 0.000001f)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.Vector4,
|
||||
Value = value
|
||||
};
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetQuaternion(string key, Quaternion value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.Quaternion)
|
||||
{
|
||||
if (Math.Abs(pair.Value.AsQuaternion().x - value.x) < 0.000001f
|
||||
&& Math.Abs(pair.Value.AsQuaternion().y - value.y) < 0.000001f
|
||||
&& Math.Abs(pair.Value.AsQuaternion().z - value.z) < 0.000001f
|
||||
&& Math.Abs(pair.Value.AsQuaternion().w - value.w) < 0.000001f)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.Quaternion,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetColor(string key, Color value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.Color)
|
||||
{
|
||||
var color = pair.Value.AsColor();
|
||||
if (Math.Abs(color.r - value.r) < 0.01f
|
||||
&& Math.Abs(color.g - value.g) < 0.01f
|
||||
&& Math.Abs(color.b - value.b) < 0.01f
|
||||
&& Math.Abs(color.a - value.a) < 0.01f)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.Color,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public PlayerData.State SetColor32(string key, Color32 value, DateTime lastUpdated = new DateTime(), bool isRestore = false, bool flushChanges = true)
|
||||
{
|
||||
return _SetWrapper(key, Set, isRestore, flushChanges, lastUpdated);
|
||||
|
||||
bool Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
if (pair.Value?.Type == ClientSimPlayerDataType.Color32)
|
||||
{
|
||||
var color32 = pair.Value.AsColor32();
|
||||
if (Math.Abs(color32.r - value.r) < 0.000001f
|
||||
&& Math.Abs(color32.g - value.g) < 0.000001f
|
||||
&& Math.Abs(color32.b - value.b) < 0.000001f
|
||||
&& Math.Abs(color32.a - value.a) < 0.000001f)
|
||||
return false;
|
||||
pair.Value.Value = value;
|
||||
}
|
||||
else
|
||||
pair.Value = new ClientSimPlayerDataTypeUnion()
|
||||
{
|
||||
Type = ClientSimPlayerDataType.Color32,
|
||||
Value = value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Getters
|
||||
|
||||
private ClientSimPlayerDataPair _GetChecked(string key, ClientSimPlayerDataType expectedType)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
throw new ArgumentException("Key was invalid");
|
||||
if (!leData.TryGetValue(key, out ClientSimPlayerDataPair data) || data.Value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (data.Value.Type != expectedType)
|
||||
{
|
||||
this.LogError($"Data at {key} was not a {expectedType}, it was a {data.Value.Type}");
|
||||
return null;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public bool GetBool(string key)
|
||||
=> _GetChecked(key, ClientSimPlayerDataType.WrappedBool)?.Value.AsWrappedBool() ?? default;
|
||||
|
||||
public byte GetByte(string key)
|
||||
=> _GetChecked(key, ClientSimPlayerDataType.WrappedUByte)?.Value.AsWrappedUByte() ?? default;
|
||||
|
||||
public sbyte GetSByte(string key)
|
||||
=> _GetChecked(key, ClientSimPlayerDataType.WrappedByte)?.Value.AsWrappedSByte() ?? default;
|
||||
|
||||
public byte[] GetBytes(string key)
|
||||
=> _GetChecked(key, ClientSimPlayerDataType.WrappedBytes)?.Value.AsWrappedBytes().ToArray();
|
||||
|
||||
public string GetString(string key)
|
||||
=> _GetChecked(key, ClientSimPlayerDataType.WrappedString)?.Value.AsWrappedString();
|
||||
|
||||
public short GetShort(string key)
|
||||
=> _GetChecked(key, ClientSimPlayerDataType.WrappedShort)?.Value.AsWrappedShort() ?? default;
|
||||
|
||||
public ushort GetUShort(string key)
|
||||
=> _GetChecked(key, ClientSimPlayerDataType.WrappedUShort)?.Value.AsWrappedUShort() ?? default;
|
||||
|
||||
public int GetInt(string key)
|
||||
=> _GetChecked(key, ClientSimPlayerDataType.WrappedInt)?.Value.AsWrappedInt() ?? default;
|
||||
|
||||
public uint GetUInt(string key)
|
||||
=> _GetChecked(key, ClientSimPlayerDataType.WrappedUInt)?.Value.AsWrappedUInt() ?? default;
|
||||
|
||||
public long GetLong(string key)
|
||||
=> _GetChecked(key, ClientSimPlayerDataType.WrappedLong)?.Value.AsWrappedLong() ?? default;
|
||||
|
||||
public ulong GetULong(string key)
|
||||
=> _GetChecked(key, ClientSimPlayerDataType.WrappedULong)?.Value.AsWrappedULong() ?? default;
|
||||
|
||||
public float GetFloat(string key)
|
||||
=> _GetChecked(key, ClientSimPlayerDataType.WrappedFloat)?.Value.AsWrappedFloat() ?? default;
|
||||
|
||||
public double GetDouble(string key)
|
||||
=> _GetChecked(key, ClientSimPlayerDataType.WrappedDouble)?.Value.AsWrappedDouble() ?? default;
|
||||
|
||||
public Vector2 GetVector2(string key)
|
||||
{
|
||||
ClientSimPlayerDataPair data = _GetChecked(key, ClientSimPlayerDataType.Vector2);
|
||||
if (data != null)
|
||||
return new Vector2(data.Value.AsVector2().x, data.Value.AsVector2().y);
|
||||
return default;
|
||||
}
|
||||
|
||||
public Vector3 GetVector3(string key)
|
||||
{
|
||||
ClientSimPlayerDataPair data = _GetChecked(key, ClientSimPlayerDataType.Vector3);
|
||||
if (data != null)
|
||||
return new Vector3(data.Value.AsVector3().x, data.Value.AsVector3().y, data.Value.AsVector3().z);
|
||||
return default;
|
||||
}
|
||||
|
||||
public Vector4 GetVector4(string key)
|
||||
{
|
||||
ClientSimPlayerDataPair data = _GetChecked(key, ClientSimPlayerDataType.Vector4);
|
||||
if (data != null)
|
||||
return new Vector4(data.Value.AsVector4().x, data.Value.AsVector4().y, data.Value.AsVector4().z, data.Value.AsVector4().w);
|
||||
return default;
|
||||
}
|
||||
|
||||
public Quaternion GetQuaternion(string key)
|
||||
{
|
||||
ClientSimPlayerDataPair data = _GetChecked(key, ClientSimPlayerDataType.Quaternion);
|
||||
if (data != null)
|
||||
return new Quaternion(data.Value.AsQuaternion().x, data.Value.AsQuaternion().y, data.Value.AsQuaternion().z, data.Value.AsQuaternion().w);
|
||||
return default;
|
||||
}
|
||||
|
||||
public Color GetColor(string key)
|
||||
{
|
||||
ClientSimPlayerDataPair data = _GetChecked(key, ClientSimPlayerDataType.Color);
|
||||
if (data != null)
|
||||
return new Color(data.Value.AsColor().r, data.Value.AsColor().g, data.Value.AsColor().b, data.Value.AsColor().a);
|
||||
return default;
|
||||
}
|
||||
|
||||
public Color32 GetColor32(string key)
|
||||
{
|
||||
ClientSimPlayerDataPair data = _GetChecked(key, ClientSimPlayerDataType.Color32);
|
||||
if (data != null)
|
||||
return new Color32(data.Value.AsColor32().r, data.Value.AsColor32().g, data.Value.AsColor32().b, data.Value.AsColor32().a);
|
||||
return default;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DataPropagation
|
||||
|
||||
// can't decode json here because this is called before scene manager is ready
|
||||
public void Init(VRCPlayerApi player, IClientSimUdonEventSender udonEventSender, IClientSimEventDispatcher eventDispatcher)
|
||||
{
|
||||
_player = player;
|
||||
_udonEventSender = udonEventSender;
|
||||
_eventDispatcher = eventDispatcher;
|
||||
|
||||
_eventDispatcher.Subscribe<ClientSimOnPlayerJoinedEvent>(OnPlayerJoined);
|
||||
_eventDispatcher.Subscribe<ClientSimOnPlayerDataClearedEvent>(OnPlayerDataCleared);
|
||||
_eventDispatcher.Subscribe<ClientSimOnPlayerRestoredEvent>(OnPlayerRestored);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_eventDispatcher.Unsubscribe<ClientSimOnPlayerJoinedEvent>(OnPlayerJoined);
|
||||
_eventDispatcher.Unsubscribe<ClientSimOnPlayerDataClearedEvent>(OnPlayerDataCleared);
|
||||
_eventDispatcher.Unsubscribe<ClientSimOnPlayerRestoredEvent>(OnPlayerRestored);
|
||||
}
|
||||
|
||||
private void OnPlayerJoined(ClientSimOnPlayerJoinedEvent payload)
|
||||
{
|
||||
// remote player test data is decoded via ClientSimPlayerDataWindow.OnPlayerJoined
|
||||
if (payload.player.playerId == _player.playerId)
|
||||
doDecode = true;
|
||||
}
|
||||
|
||||
private void OnPlayerDataCleared(ClientSimOnPlayerDataClearedEvent payload)
|
||||
{
|
||||
leData.Clear();
|
||||
}
|
||||
|
||||
private void OnPlayerRestored(ClientSimOnPlayerRestoredEvent payload)
|
||||
{
|
||||
if (payload.player.isLocal)
|
||||
hasPostedPlayerRestored = true;
|
||||
}
|
||||
|
||||
private void Encode()
|
||||
{
|
||||
// PlayerData updates before OnPlayerRestored are ignored
|
||||
if (!hasPostedPlayerRestored)
|
||||
return;
|
||||
|
||||
string json = JsonConvert.SerializeObject(leData, Formatting.Indented, new JsonSerializerSettings
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
||||
});
|
||||
File.WriteAllText(PlayerDataFilePath(_player), json);
|
||||
}
|
||||
|
||||
internal void Decode(bool isRestore)
|
||||
{
|
||||
string path = PlayerDataFilePath(_player);
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
File.WriteAllText(path, "{}");
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
string json = File.ReadAllText(path);
|
||||
var data = JsonConvert.DeserializeObject<Dictionary<string, ClientSimPlayerDataPair>>(json);
|
||||
foreach (KeyValuePair<string, ClientSimPlayerDataPair> kvp in data)
|
||||
{
|
||||
Set(kvp.Value);
|
||||
}
|
||||
|
||||
FlushLocalInfoChanges(); // bulk flush changes
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.LogError($"Error initializing PlayerData: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
isDoneDecoding = true;
|
||||
|
||||
PlayerData.State Set(ClientSimPlayerDataPair pair)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (pair.Value.Type)
|
||||
{
|
||||
case ClientSimPlayerDataType.Color: return SetColor(pair.Key, pair.Value.AsColor(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.Color32: return SetColor32(pair.Key, pair.Value.AsColor32(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.Quaternion: return SetQuaternion(pair.Key, pair.Value.AsQuaternion(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.Vector2: return SetVector2(pair.Key, pair.Value.AsVector2(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.Vector3: return SetVector3(pair.Key, pair.Value.AsVector3(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.Vector4: return SetVector4(pair.Key, pair.Value.AsVector4(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.WrappedBool: return SetBool(pair.Key, pair.Value.AsWrappedBool(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.WrappedByte: return SetSByte(pair.Key, pair.Value.AsWrappedSByte(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.WrappedUByte: return SetByte(pair.Key, pair.Value.AsWrappedByte(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.WrappedBytes: return SetBytes(pair.Key, pair.Value.AsWrappedBytes(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.WrappedFloat: return SetFloat(pair.Key, pair.Value.AsWrappedFloat(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.WrappedDouble: return SetDouble(pair.Key, pair.Value.AsWrappedDouble(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.WrappedLong: return SetLong(pair.Key, pair.Value.AsWrappedLong(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.WrappedULong: return SetULong(pair.Key, pair.Value.AsWrappedULong(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.WrappedInt: return SetInt(pair.Key, pair.Value.AsWrappedInt(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.WrappedUInt: return SetUInt(pair.Key, pair.Value.AsWrappedUInt(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.WrappedShort: return SetShort(pair.Key, pair.Value.AsWrappedShort(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.WrappedUShort: return SetUShort(pair.Key, pair.Value.AsWrappedUShort(), pair.LastUpdated, isRestore, false);
|
||||
case ClientSimPlayerDataType.WrappedString: return SetString(pair.Key, pair.Value.AsWrappedString(), pair.LastUpdated, isRestore, false);
|
||||
|
||||
default:
|
||||
case ClientSimPlayerDataType.None: return PlayerData.State.Unchanged;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.LogError("Error reading PlayerData: " +
|
||||
$"key={pair.Key}, " +
|
||||
$"type={pair.Value.Type} ({pair.Value.Value.GetType()}), " +
|
||||
$"value={pair.Value.Value}, " +
|
||||
$"error={e.Message}");
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FlushLocalInfoChanges()
|
||||
{
|
||||
var infos = leData
|
||||
.Select(kvp =>
|
||||
localInfoChanges.TryGetValue(kvp.Key, out var value)
|
||||
? value
|
||||
: new PlayerData.Info(kvp.Key, PlayerData.State.Unchanged))
|
||||
.ToArray();
|
||||
|
||||
QueuePlayerDataUpdate(infos);
|
||||
|
||||
localInfoChanges.Clear();
|
||||
}
|
||||
|
||||
internal void QueuePlayerDataUpdate(PlayerData.Info[] infos)
|
||||
{
|
||||
if (infos.Length == 0)
|
||||
return;
|
||||
|
||||
queuedPlayerDataUpdates.AddRange(infos);
|
||||
}
|
||||
|
||||
private void RaisePlayerDataUpdated(PlayerData.Info[] infos)
|
||||
{
|
||||
_udonEventSender.RunEvent(UdonManager.UDON_EVENT_ONPLAYERDATAUPDATED, ("player", _player), ("infos", infos));
|
||||
_eventDispatcher.SendEvent(new ClientSimOnPlayerDataUpdatedEvent
|
||||
{
|
||||
player = _player,
|
||||
playerData = leData
|
||||
});
|
||||
}
|
||||
|
||||
private float lastCheckTime = 0;
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (queuedPlayerDataUpdates.Count > 0)
|
||||
{
|
||||
Encode();
|
||||
RaisePlayerDataUpdated(queuedPlayerDataUpdates.ToArray());
|
||||
queuedPlayerDataUpdates.Clear();
|
||||
}
|
||||
|
||||
if (doDecode)
|
||||
{
|
||||
doDecode = false;
|
||||
Decode(true);
|
||||
}
|
||||
else if (isDoneDecoding && !hasPostedPlayerDataDecoded)
|
||||
{
|
||||
hasPostedPlayerDataDecoded = true;
|
||||
_eventDispatcher.SendEvent(new ClientSimOnPlayerDataDecodedEvent { player = _player });
|
||||
}
|
||||
|
||||
if (Time.realtimeSinceStartup - lastCheckTime > 30f)
|
||||
{
|
||||
lastCheckTime = Time.realtimeSinceStartup;
|
||||
float dataLimit = ClientSimMain.TryGetInstance(out var instance) ? instance.GetPlayerDataUsageLimit() : 0;
|
||||
float sz = Size;
|
||||
|
||||
if (sz >= dataLimit)
|
||||
UdonManager.Instance.RunEvent(UdonManager.UDON_EVENT_ONPLAYERDATASTORAGEEXCEEDED, ("player", _player));
|
||||
else if (sz >= dataLimit * 0.7f)
|
||||
UdonManager.Instance.RunEvent(UdonManager.UDON_EVENT_ONPLAYERDATASTORAGEWARNING, ("player", _player));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ef04bdeba52437cb909dc841044fd57
|
||||
timeCreated: 1708619978
|
||||
@ -0,0 +1,28 @@
|
||||
#if VRC_ENABLE_PLAYER_PERSISTENCE
|
||||
namespace VRC.SDK3.ClientSim.Persistence
|
||||
{
|
||||
public enum ClientSimPlayerDataType : byte
|
||||
{
|
||||
None = 0,
|
||||
Vector2 = 1,
|
||||
Vector3 = 2,
|
||||
Vector4 = 3,
|
||||
Quaternion = 4,
|
||||
Color = 5,
|
||||
Color32 = 6,
|
||||
WrappedString = 7,
|
||||
WrappedShort = 8,
|
||||
WrappedInt = 9,
|
||||
WrappedFloat = 10,
|
||||
WrappedBool = 11,
|
||||
WrappedByte = 12,
|
||||
WrappedBytes = 13,
|
||||
WrappedUShort = 14,
|
||||
WrappedUByte = 15,
|
||||
WrappedUInt = 16,
|
||||
WrappedULong = 17,
|
||||
WrappedDouble = 18,
|
||||
WrappedLong = 19,
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee1e38bff02f4812896fb863095a29ad
|
||||
timeCreated: 1708638423
|
||||
@ -0,0 +1,39 @@
|
||||
#if VRC_ENABLE_PLAYER_PERSISTENCE
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRC.SDK3.ClientSim.Persistence
|
||||
{
|
||||
[JsonConverter(typeof(ClientSimPlayerDataConverter))]
|
||||
public class ClientSimPlayerDataTypeUnion
|
||||
{
|
||||
public object Value { get; set; }
|
||||
public ClientSimPlayerDataType Type { get; set; }
|
||||
public ClientSimPlayerDataTypeUnion() {
|
||||
this.Type = ClientSimPlayerDataType.None;
|
||||
this.Value = null;
|
||||
}
|
||||
|
||||
public Vector2 AsVector2() => (Vector2)Value;
|
||||
public Vector3 AsVector3() => (Vector3)Value;
|
||||
public Vector4 AsVector4() => (Vector4)Value;
|
||||
public Quaternion AsQuaternion() => (Quaternion)Value;
|
||||
public Color AsColor() => (Color)Value;
|
||||
public Color32 AsColor32() => (Color32)Value;
|
||||
public string AsWrappedString() { return (string)Value; }
|
||||
public short AsWrappedShort() { return (short)Value; }
|
||||
public ushort AsWrappedUShort() { return (ushort)Value; }
|
||||
public int AsWrappedInt() { return (int)Value; }
|
||||
public uint AsWrappedUInt() { return (uint)Value; }
|
||||
public long AsWrappedLong() { return (long)Value; }
|
||||
public ulong AsWrappedULong() { return ( ulong)Value; }
|
||||
public float AsWrappedFloat() { return ( float)Value; }
|
||||
public double AsWrappedDouble() { return (double)Value; }
|
||||
public bool AsWrappedBool() { return (bool)Value; }
|
||||
public byte AsWrappedByte() { return (byte)Value ; }
|
||||
public sbyte AsWrappedSByte() { return (sbyte)Value; }
|
||||
public byte[] AsWrappedBytes() { return (byte[])Value; }
|
||||
public byte AsWrappedUByte() { return (byte)Value; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53d85c02f89642f49608b67f2788c77a
|
||||
timeCreated: 1708638433
|
||||
@ -0,0 +1,472 @@
|
||||
#if VRC_ENABLE_PLAYER_PERSISTENCE
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using VRC.SDKBase;
|
||||
|
||||
namespace VRC.SDK3.ClientSim.Persistence
|
||||
{
|
||||
public static class ClientSimPlayerDataWrapper
|
||||
{
|
||||
public static void ConfigureSDK()
|
||||
{
|
||||
System.Func<VRCPlayerApi, IEnumerable<string>> _getKeys = GetKeys;
|
||||
|
||||
FieldInfo getKeysInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getKeys", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getKeysInfo?.SetValue(null, _getKeys);
|
||||
|
||||
System.Func<VRCPlayerApi, string, bool> _hasKey = HasKey;
|
||||
|
||||
FieldInfo hasKeyInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_hasKey", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
hasKeyInfo?.SetValue(null, _hasKey);
|
||||
|
||||
System.Func<VRCPlayerApi, string, Type> _getType = GetType;
|
||||
|
||||
FieldInfo getTypeInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getType", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getTypeInfo?.SetValue(null, _getType);
|
||||
|
||||
System.Action<string, bool> _setBool = SetBool;
|
||||
System.Func<VRCPlayerApi, string, bool> _getBool = GetBool;
|
||||
|
||||
FieldInfo setBoolInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setBool", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setBoolInfo?.SetValue(null, _setBool);
|
||||
|
||||
FieldInfo getBoolInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getBool", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getBoolInfo?.SetValue(null, _getBool);
|
||||
|
||||
System.Action<string, byte> _setByte = SetByte;
|
||||
System.Func<VRCPlayerApi, string, byte> _getByte = GetByte;
|
||||
|
||||
FieldInfo setByteInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setByte", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setByteInfo?.SetValue(null, _setByte);
|
||||
|
||||
FieldInfo getByteInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getByte", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getByteInfo?.SetValue(null, _getByte);
|
||||
|
||||
System.Action<string, sbyte> _setUByte = SetSByte;
|
||||
System.Func<VRCPlayerApi, string, sbyte> _getUByte = GetSByte;
|
||||
|
||||
FieldInfo setUByteInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setSByte", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setUByteInfo?.SetValue(null, _setUByte);
|
||||
|
||||
FieldInfo getUByteInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getSByte", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getUByteInfo?.SetValue(null, _getUByte);
|
||||
|
||||
System.Action<string, byte[]> _setBytes = SetBytes;
|
||||
System.Func<VRCPlayerApi, string, byte[]> _getBytes = GetBytes;
|
||||
|
||||
FieldInfo setBytesInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setBytes", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setBytesInfo?.SetValue(null, _setBytes);
|
||||
|
||||
FieldInfo getBytesInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getBytes", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getBytesInfo?.SetValue(null, _getBytes);
|
||||
|
||||
System.Action<string, string> _setString = SetString;
|
||||
System.Func<VRCPlayerApi, string, string> _getString = GetString;
|
||||
|
||||
FieldInfo setStringInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setString", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setStringInfo?.SetValue(null, _setString);
|
||||
|
||||
FieldInfo getStringInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getString", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getStringInfo?.SetValue(null, _getString);
|
||||
|
||||
System.Action<string, short> _setShort = SetShort;
|
||||
System.Func<VRCPlayerApi, string, short> _getShort = GetShort;
|
||||
|
||||
FieldInfo setShortInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setShort", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setShortInfo?.SetValue(null, _setShort);
|
||||
|
||||
FieldInfo getShortInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getShort", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getShortInfo?.SetValue(null, _getShort);
|
||||
|
||||
System.Action<string, ushort> _setUShort = SetUShort;
|
||||
System.Func<VRCPlayerApi, string, ushort> _getUShort = GetUShort;
|
||||
|
||||
FieldInfo setUShortInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setUShort", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setUShortInfo?.SetValue(null, _setUShort);
|
||||
|
||||
FieldInfo getUShortInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getUShort", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getUShortInfo?.SetValue(null, _getUShort);
|
||||
|
||||
System.Action<string, int> _setInt = SetInt;
|
||||
System.Func<VRCPlayerApi, string, int> _getInt = GetInt;
|
||||
|
||||
FieldInfo setIntInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setInt", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setIntInfo?.SetValue(null, _setInt);
|
||||
|
||||
FieldInfo getIntInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getInt", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getIntInfo?.SetValue(null, _getInt);
|
||||
|
||||
System.Action<string, uint> _setUInt = SetUInt;
|
||||
System.Func<VRCPlayerApi, string, uint> _getUInt = GetUInt;
|
||||
|
||||
FieldInfo setUIntInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setUInt", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setUIntInfo?.SetValue(null, _setUInt);
|
||||
|
||||
FieldInfo getUIntInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getUInt", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getUIntInfo?.SetValue(null, _getUInt);
|
||||
|
||||
System.Action<string, long> _setLong = SetLong;
|
||||
System.Func<VRCPlayerApi, string, long> _getLong = GetLong;
|
||||
|
||||
FieldInfo setLongInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setLong", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setLongInfo?.SetValue(null, _setLong);
|
||||
|
||||
FieldInfo getLongInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getLong", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getLongInfo?.SetValue(null, _getLong);
|
||||
|
||||
System.Action<string, ulong> _setULong = SetULong;
|
||||
System.Func<VRCPlayerApi, string, ulong> _getULong = GetULong;
|
||||
|
||||
FieldInfo setULongInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setULong", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setULongInfo?.SetValue(null, _setULong);
|
||||
|
||||
FieldInfo getULongInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getULong", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getULongInfo?.SetValue(null, _getULong);
|
||||
|
||||
System.Action<string, double> _setDouble = SetDouble;
|
||||
System.Func<VRCPlayerApi, string, double> _getDouble = GetDouble;
|
||||
|
||||
FieldInfo setDoubleInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setDouble", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setDoubleInfo?.SetValue(null, _setDouble);
|
||||
|
||||
FieldInfo getDoubleInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getDouble", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getDoubleInfo?.SetValue(null, _getDouble);
|
||||
|
||||
System.Action<string, float> _setFloat = SetFloat;
|
||||
System.Func<VRCPlayerApi, string, float> _getFloat = GetFloat;
|
||||
|
||||
FieldInfo setFloatInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setFloat", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setFloatInfo?.SetValue(null, _setFloat);
|
||||
|
||||
FieldInfo getFloatInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getFloat", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getFloatInfo?.SetValue(null, _getFloat);
|
||||
|
||||
System.Action<string, Vector2> _setVector2 = SetVector2;
|
||||
System.Func<VRCPlayerApi, string, Vector2> _getVector2 = GetVector2;
|
||||
|
||||
FieldInfo setVector2Info = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setVector2", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setVector2Info?.SetValue(null, _setVector2);
|
||||
|
||||
FieldInfo getVector2Info = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getVector2", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getVector2Info?.SetValue(null, _getVector2);
|
||||
|
||||
System.Action<string, Vector3> _setVector3 = SetVector3;
|
||||
System.Func<VRCPlayerApi, string, Vector3> _getVector3 = GetVector3;
|
||||
|
||||
FieldInfo setVector3Info = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setVector3", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setVector3Info?.SetValue(null, _setVector3);
|
||||
|
||||
FieldInfo getVector3Info = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getVector3", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getVector3Info?.SetValue(null, _getVector3);
|
||||
|
||||
System.Action<string, Vector4> _setVector4 = SetVector4;
|
||||
System.Func<VRCPlayerApi, string, Vector4> _getVector4 = GetVector4;
|
||||
|
||||
FieldInfo setVector4Info = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setVector4", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setVector4Info?.SetValue(null, _setVector4);
|
||||
|
||||
FieldInfo getVector4Info = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getVector4", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getVector4Info?.SetValue(null, _getVector4);
|
||||
|
||||
System.Action<string, Quaternion> _setQuaternion = SetQuaternion;
|
||||
System.Func<VRCPlayerApi, string, Quaternion> _getQuaternion = GetQuaternion;
|
||||
|
||||
FieldInfo setQuaternionInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setQuaternion", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setQuaternionInfo?.SetValue(null, _setQuaternion);
|
||||
|
||||
FieldInfo getQuaternionInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getQuaternion", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getQuaternionInfo?.SetValue(null, _getQuaternion);
|
||||
|
||||
System.Action<string, UnityEngine.Color> _setColor = SetColor;
|
||||
System.Func<VRCPlayerApi, string, UnityEngine.Color> _getColor = GetColor;
|
||||
|
||||
FieldInfo setColorInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setColor", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setColorInfo?.SetValue(null, _setColor);
|
||||
|
||||
FieldInfo getColorInfo = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getColor", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getColorInfo?.SetValue(null, _getColor);
|
||||
|
||||
System.Action<string, UnityEngine.Color32> _setColor32 = SetColor32;
|
||||
System.Func<VRCPlayerApi, string, UnityEngine.Color32> _getColor32 = GetColor32;
|
||||
|
||||
FieldInfo setColor32Info = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_setColor32", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
setColor32Info?.SetValue(null, _setColor32);
|
||||
|
||||
FieldInfo getColor32Info = typeof(VRC.SDK3.Persistence.PlayerData).GetField("_getColor32", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
getColor32Info?.SetValue(null, _getColor32);
|
||||
}
|
||||
|
||||
public static int GetUsage(VRCPlayerApi player)
|
||||
=> FindStorage(player, out ClientSimPlayerDataStorage storage) ? storage.Size : 0;
|
||||
|
||||
private static bool FindStorage(VRCPlayerApi playerApi, out ClientSimPlayerDataStorage storage)
|
||||
{
|
||||
ClientSimPlayer player = playerApi.GetClientSimPlayer();
|
||||
if (!player)
|
||||
{
|
||||
UnityEngine.Debug.LogError("Could not locate player with id " + playerApi.playerId + " for PlayerData storage.");
|
||||
storage = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
storage = player.PlayerDataObject;
|
||||
if (!storage)
|
||||
{
|
||||
UnityEngine.Debug.LogError("Could not locate PlayerData storage for player " + playerApi.playerId);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetKeys(VRCPlayerApi playerApi)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return Enumerable.Empty<string>();
|
||||
return storage.GetKeys();
|
||||
}
|
||||
|
||||
public static bool HasKey(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return false;
|
||||
return storage.HasKey(key);
|
||||
}
|
||||
|
||||
public static Type GetType(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return null;
|
||||
return storage.GetType(key);
|
||||
}
|
||||
|
||||
public static void SetBool(string key, bool data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetBool(key, data);
|
||||
}
|
||||
|
||||
public static bool GetBool(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetBool(key);
|
||||
}
|
||||
|
||||
public static void SetByte(string key, byte data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetByte(key, data);
|
||||
}
|
||||
|
||||
public static byte GetByte(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetByte(key);
|
||||
}
|
||||
|
||||
public static void SetSByte(string key, sbyte data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetSByte(key, data);
|
||||
}
|
||||
|
||||
public static sbyte GetSByte(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetSByte(key);
|
||||
}
|
||||
|
||||
public static void SetBytes(string key, byte[] data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetBytes(key, data);
|
||||
}
|
||||
|
||||
public static byte[] GetBytes(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetBytes(key);
|
||||
}
|
||||
|
||||
public static void SetString(string key, string data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetString(key, data);
|
||||
}
|
||||
|
||||
public static string GetString(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetString(key);
|
||||
}
|
||||
|
||||
public static void SetShort(string key, short data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetShort(key, data);
|
||||
}
|
||||
|
||||
public static void SetUShort(string key, ushort data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetUShort(key, data);
|
||||
}
|
||||
|
||||
public static short GetShort(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetShort(key);
|
||||
}
|
||||
|
||||
public static ushort GetUShort(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetUShort(key);
|
||||
}
|
||||
|
||||
public static void SetInt(string key, int data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetInt(key, data);
|
||||
}
|
||||
|
||||
public static void SetUInt(string key, uint data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetUInt(key, data);
|
||||
}
|
||||
|
||||
public static int GetInt(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetInt(key);
|
||||
}
|
||||
|
||||
public static uint GetUInt(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetUInt(key);
|
||||
}
|
||||
|
||||
public static void SetLong(string key, long data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetLong(key, data);
|
||||
}
|
||||
|
||||
public static long GetLong(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetLong(key);
|
||||
}
|
||||
|
||||
public static void SetULong(string key, ulong data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetULong(key, data);
|
||||
}
|
||||
|
||||
public static ulong GetULong(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetULong(key);
|
||||
}
|
||||
|
||||
public static void SetFloat(string key, float data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetFloat(key, data);
|
||||
}
|
||||
|
||||
public static float GetFloat(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetFloat(key);
|
||||
}
|
||||
|
||||
public static void SetDouble(string key, double data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetDouble(key, data);
|
||||
}
|
||||
|
||||
public static double GetDouble(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetDouble(key);
|
||||
}
|
||||
|
||||
public static void SetVector2(string key, Vector2 data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetVector2(key, data);
|
||||
}
|
||||
|
||||
public static Vector2 GetVector2(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetVector2(key);
|
||||
}
|
||||
|
||||
public static void SetVector3(string key, Vector3 data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetVector3(key, data);
|
||||
}
|
||||
|
||||
public static Vector3 GetVector3(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetVector3(key);
|
||||
}
|
||||
|
||||
public static void SetVector4(string key, Vector4 data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetVector4(key, data);
|
||||
}
|
||||
|
||||
public static Vector4 GetVector4(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetVector4(key);
|
||||
}
|
||||
|
||||
public static void SetQuaternion(string key, Quaternion data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetQuaternion(key, data);
|
||||
}
|
||||
|
||||
public static Quaternion GetQuaternion(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetQuaternion(key);
|
||||
}
|
||||
|
||||
public static void SetColor(string key, UnityEngine.Color data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetColor(key, data);
|
||||
}
|
||||
|
||||
public static UnityEngine.Color GetColor(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetColor(key);
|
||||
}
|
||||
|
||||
public static void SetColor32(string key, UnityEngine.Color32 data)
|
||||
{
|
||||
if (!FindStorage(Networking.LocalPlayer, out ClientSimPlayerDataStorage storage)) return;
|
||||
storage.SetColor32(key, data);
|
||||
}
|
||||
|
||||
public static UnityEngine.Color32 GetColor32(VRCPlayerApi playerApi, string key)
|
||||
{
|
||||
if (!FindStorage(playerApi, out ClientSimPlayerDataStorage storage)) return default;
|
||||
return storage.GetColor32(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf5156727fe54ec1b396e496f91f03d4
|
||||
timeCreated: 1708639223
|
||||
@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using VRC.SDK3.ClientSim.Interfaces;
|
||||
using VRC.SDK3.Data;
|
||||
using VRC.SDKBase;
|
||||
using VRC.Udon;
|
||||
|
||||
namespace VRC.SDK3.ClientSim.Persistence
|
||||
{
|
||||
[AddComponentMenu("")] // hides component in Add Component menu
|
||||
public class ClientSimPlayerObjectStorage : ClientSimBehaviour
|
||||
{
|
||||
#if VRC_ENABLE_PLAYER_PERSISTENCE
|
||||
public static string PlayerObjectsFolder => Path.Combine("ClientSimStorage", "PlayerObjects");
|
||||
internal static string ActiveSceneName;
|
||||
internal static string PlayerDataFilePath(VRCPlayerApi player)
|
||||
{
|
||||
string root = Path.GetDirectoryName(Application.dataPath);
|
||||
string path = Path.Combine(root, PlayerObjectsFolder);
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
return path + "/PlayerObject_" + $"{player.playerId}" + $"_{ActiveSceneName}" + ".json";
|
||||
}
|
||||
|
||||
private VRCPlayerApi _player;
|
||||
private IClientSimUdonEventSender _udonEventSender;
|
||||
private IClientSimEventDispatcher _eventDispatcher;
|
||||
|
||||
private Dictionary<int,IClientSimNetworkView> _persistentObjects = new Dictionary<int, IClientSimNetworkView>();
|
||||
private DataDictionary _persistentObjectData = new DataDictionary();
|
||||
|
||||
private bool _isInitialized = false;
|
||||
private bool _HasJoined = false;
|
||||
|
||||
private Coroutine _ContinuousUpdate;
|
||||
|
||||
private const float _updateInterval = 1 / 4f;
|
||||
|
||||
private bool hadUpdate = false;
|
||||
|
||||
public int Size =>
|
||||
VRCJson.TrySerializeToJson(_persistentObjectData, JsonExportType.Beautify, out DataToken json)
|
||||
? System.Text.Encoding.UTF8.GetByteCount(json.String)
|
||||
: 0;
|
||||
|
||||
public void Init(VRCPlayerApi player, IClientSimUdonEventSender udonEventSender, IClientSimEventDispatcher eventDispatcher)
|
||||
{
|
||||
_player = player;
|
||||
_udonEventSender = udonEventSender;
|
||||
_eventDispatcher = eventDispatcher;
|
||||
|
||||
ActiveSceneName = SceneManager.GetActiveScene().name;
|
||||
|
||||
_eventDispatcher.Subscribe<ClientSimOnPlayerJoinedEvent>(OnPlayerJoined);
|
||||
UdonBehaviour.RequestSerializationHook += RequestSerializationHook;
|
||||
_isInitialized = true;
|
||||
|
||||
_ContinuousUpdate = StartCoroutine(UpdateContinuous());
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if(_eventDispatcher != null)
|
||||
_eventDispatcher.Unsubscribe<ClientSimOnPlayerJoinedEvent>(OnPlayerJoined);
|
||||
|
||||
if(_ContinuousUpdate != null)
|
||||
StopCoroutine(_ContinuousUpdate);
|
||||
}
|
||||
|
||||
public IEnumerator UpdateContinuous()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (_HasJoined && _isInitialized)
|
||||
{
|
||||
Encode();
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(_updateInterval);
|
||||
}
|
||||
}
|
||||
|
||||
public void RequestSerializationHook(UdonBehaviour udonBehaviour)
|
||||
{
|
||||
ClientSimPersistenceEventSending.Instance.QueueRequest(udonBehaviour, this);
|
||||
}
|
||||
|
||||
private void OnPlayerJoined(ClientSimOnPlayerJoinedEvent payload)
|
||||
{
|
||||
if (payload.player.playerId == _player.playerId)
|
||||
{
|
||||
_HasJoined = true;
|
||||
Decode();
|
||||
}
|
||||
}
|
||||
|
||||
public void Encode(GameObject gameObject = null)
|
||||
{
|
||||
if (!_isInitialized || !_HasJoined) return;
|
||||
|
||||
if(_persistentObjectData == null)
|
||||
_persistentObjectData = new DataDictionary();
|
||||
|
||||
foreach (var keyValuePersistentObject in _persistentObjects)
|
||||
{
|
||||
DataToken key = keyValuePersistentObject.Key.ToString();
|
||||
if (!_persistentObjectData.ContainsKey(key))
|
||||
_persistentObjectData.Add(key, new DataList());
|
||||
_persistentObjectData[key] = keyValuePersistentObject.Value.Encode(gameObject);
|
||||
}
|
||||
|
||||
hadUpdate = true;
|
||||
}
|
||||
|
||||
private async UniTask SaveToFile(string data)
|
||||
{
|
||||
await UniTask.SwitchToTaskPool();
|
||||
try{
|
||||
await File.WriteAllTextAsync(PlayerDataFilePath(_player), data);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.LogError($"Error saving PlayerObjects: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void Decode()
|
||||
{
|
||||
string path = PlayerDataFilePath(_player);
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
File.WriteAllText(path, "{}");
|
||||
}
|
||||
|
||||
string json = File.ReadAllText(path);
|
||||
if (!VRCJson.TryDeserializeFromJson(json, out DataToken token))
|
||||
{
|
||||
this.LogError($"Error initializing PlayerObjects: {token.Error}");
|
||||
return;
|
||||
}
|
||||
|
||||
_persistentObjectData = token.DataDictionary;
|
||||
ClientSimPlayer player = _player.GetClientSimPlayer();
|
||||
foreach (GameObject persistantObject in player.PlayerPersistenceObjects)
|
||||
{
|
||||
IClientSimNetworkId networkId = persistantObject.GetComponent<IClientSimNetworkId>();
|
||||
if (networkId == null) continue;
|
||||
int id = networkId.GetNetworkId();
|
||||
|
||||
IClientSimNetworkView serializer = persistantObject.GetComponent<IClientSimNetworkView>();
|
||||
|
||||
_persistentObjects.TryAdd(id, serializer);
|
||||
if (_persistentObjectData.TryGetValue(id.ToString(), out var data))
|
||||
{
|
||||
_persistentObjects[id].Decode(data.DataList);
|
||||
}
|
||||
}
|
||||
|
||||
_eventDispatcher.SendEvent(new ClientSimOnPlayerObjectsDecodedEvent { player = _player });
|
||||
}
|
||||
|
||||
private float lastCheckTime = 0;
|
||||
public void LateUpdate()
|
||||
{
|
||||
if (hadUpdate)
|
||||
{
|
||||
hadUpdate = false;
|
||||
_eventDispatcher.SendEvent(new ClientSimOnPlayerObjectUpdateEndedEvent());
|
||||
|
||||
VRCJson.TrySerializeToJson(_persistentObjectData, JsonExportType.Beautify, out DataToken json);
|
||||
SaveToFile(json.String).Forget();
|
||||
}
|
||||
|
||||
if (Time.realtimeSinceStartup - lastCheckTime > 30f)
|
||||
{
|
||||
lastCheckTime = Time.realtimeSinceStartup;
|
||||
float objLimit = ClientSimMain.TryGetInstance(out var instance) ? instance.GetPlayerObjectsUsageLimit() : 0;
|
||||
float sz = Size;
|
||||
|
||||
if (sz >= objLimit)
|
||||
UdonManager.Instance.RunEvent(UdonManager.UDON_EVENT_ONPLAYEROBJECTSTORAGEEXCEEDED, ("player", _player));
|
||||
else if (sz >= objLimit * 0.7f)
|
||||
UdonManager.Instance.RunEvent(UdonManager.UDON_EVENT_ONPLAYERDATASTORAGEWARNING, ("player", _player));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06c904c1b8134763a58e319723ec67ad
|
||||
timeCreated: 1709650412
|
||||
Reference in New Issue
Block a user