diff --git a/Assets/PlayroomKit/PlayroomKit.cs b/Assets/PlayroomKit/PlayroomKit.cs index 20555ea9..fa47bbc2 100644 --- a/Assets/PlayroomKit/PlayroomKit.cs +++ b/Assets/PlayroomKit/PlayroomKit.cs @@ -10,11 +10,10 @@ namespace Playroom { - public class PlayroomKit + public partial class PlayroomKit { private static bool isPlayRoomInitialized; - /// /// Required Mock Mode: /// @@ -30,8 +29,6 @@ public class PlayroomKit private static readonly Dictionary Players = new(); - - [Serializable] public class InitOptions { @@ -1056,213 +1053,6 @@ public class Dpad } - // RPC: - public enum RpcMode - { - ALL, - OTHERS, - HOST - } - - private static Dictionary> rpcRegisterCallbacks = new(); - private static List rpcCalledEvents = new(); - - [DllImport("__Internal")] - private extern static void RpcRegisterInternal(string name, Action rpcRegisterCallback, string onResponseReturn = null); - - public static void RpcRegister(string name, Action rpcRegisterCallback, string onResponseReturn = null) - { - if (IsRunningInBrowser()) - { - rpcRegisterCallbacks.Add(name, rpcRegisterCallback); - RpcRegisterInternal(name, InvokeRpcRegisterCallBack, onResponseReturn); - } - else - { - Debug.LogError("[Mock Mode] RPC is currently not supported in Mock Mode!\n Please build the project to test RPC."); - } - } - - [MonoPInvokeCallback(typeof(Action))] - private static void InvokeRpcRegisterCallBack(string dataJson, string senderJson) - { - try - { - if (!Players.ContainsKey(senderJson)) - { - var player = new Player(senderJson); - Players.Add(senderJson, player); - } - - } - catch (Exception ex) - { - Debug.LogError(ex.Message); - } - - - List updatedRpcCalledEvents = new(); - // This state is required to update the called rpc events list, (Temp fix see RpcCall for more) - string nameJson = GetState("rpcCalledEventName"); - - JSONArray jsonArray = JSON.Parse(nameJson).AsArray; - foreach (JSONNode node in jsonArray) - { - string item = node.Value; - updatedRpcCalledEvents.Add(item); - } - - foreach (string name in updatedRpcCalledEvents) - { - if (rpcRegisterCallbacks.TryGetValue(name, out Action callback)) - { - callback?.Invoke(dataJson, senderJson); - } - } - - } - - [DllImport("__Internal")] - private extern static void RpcCallInternal(string name, string data, RpcMode mode, Action callbackOnResponse); - - private static Dictionary> OnResponseCallbacks = new Dictionary>(); - - public static void RpcCall(string name, object data, RpcMode mode, Action callbackOnResponse) - { - - if (IsRunningInBrowser()) - { - string jsonData = ConvertToJson(data); - if (OnResponseCallbacks.ContainsKey(name)) - { - OnResponseCallbacks[name].Add(callbackOnResponse); - } - else - { - OnResponseCallbacks.Add(name, new List { callbackOnResponse }); - if (!rpcCalledEvents.Contains(name)) - { - rpcCalledEvents.Add(name); - } - } - JSONArray jsonArray = new JSONArray(); - foreach (string item in rpcCalledEvents) - { - jsonArray.Add(item); - } - string jsonString = jsonArray.ToString(); - /* - This is requrired to sync the rpc events between all players, without this players won't know which event has been called. - this is a temporary fix, RPC's need to be handled within JS for better control. - */ - SetState("rpcCalledEventName", jsonString, reliable: true); - - RpcCallInternal(name, jsonData, mode, InvokeOnResponseCallback); - } - else - { - Debug.LogError("[Mock Mode] RPC Calls are not supported in Mock Mode! yet.\nPlease make a build to test RPC."); - } - - } - - // Default Mode - public static void RpcCall(string name, object data, Action callbackOnResponse) - { - RpcCall(name, data, RpcMode.ALL, callbackOnResponse); - } - - [MonoPInvokeCallback(typeof(Action))] - private static void InvokeOnResponseCallback() - { - var namesToRemove = new List(); - - foreach (string name in rpcCalledEvents) - { - try - { - if (OnResponseCallbacks.TryGetValue(name, out List callbacks)) - { - foreach (var callback in callbacks) - { - callback?.Invoke(); - } - - namesToRemove.Add(name); - } - } - catch (Exception ex) - { - Debug.LogError($"C#: Error in Invoking callback for RPC event name: '{name}': {ex.Message}"); - } - } - - foreach (var name in namesToRemove) - { - rpcCalledEvents.Remove(name); - OnResponseCallbacks.Remove(name); - } - } - - - private static string ConvertToJson(object data) - { - if (data == null) - { - return null; - } - else if (data.GetType().IsPrimitive || data is string) - { - return data.ToString(); - } - if (data is Vector2 vector2) - { - return JsonUtility.ToJson(vector2); - } - else if (data is Vector3 vector3) - { - return JsonUtility.ToJson(vector3); - } - else if (data is Vector4 vector4) - { - return JsonUtility.ToJson(vector4); - } - else if (data is Quaternion quaternion) - { - return JsonUtility.ToJson(quaternion); - } - else - { - return ConvertComplexToJson(data); - } - } - - private static string ConvertComplexToJson(object data) - { - if (data is IDictionary dictionary) - { - JSONObject dictNode = new JSONObject(); - foreach (DictionaryEntry entry in dictionary) - { - dictNode[entry.Key.ToString()] = ConvertToJson(entry.Value); - } - return dictNode.ToString(); - } - else if (data is IEnumerable enumerable) - { - JSONArray arrayNode = new JSONArray(); - foreach (object element in enumerable) - { - arrayNode.Add(ConvertToJson(element)); - } - return arrayNode.ToString(); - } - else - { - Debug.Log($"{data} is '{data.GetType()}' which is currently not supported by RPC!"); - return JSON.Parse("{}").ToString(); - } - } [DllImport("__Internal")] private static extern void StartMatchmakingInternal(Action callback); @@ -1287,628 +1077,7 @@ private static void InvokeStartMatchmakingCallback() startMatchmakingCallback?.Invoke(); } - // Player class - - public interface IPlayerInteraction - { - void InvokeOnQuitWrapperCallback(); - } - - public class Player : IPlayerInteraction - { - - [Serializable] - public class Profile - { - [NonSerialized] - public UnityEngine.Color color; - - public JsonColor jsonColor; - public string name; - public string photo; - - [Serializable] - public class JsonColor - { - public int r; - public int g; - public int b; - public string hexString; - public int hex; - } - - - - } - - - public string id; - private static int totalObjects = 0; - - - public Player(string id) - { - this.id = id; - totalObjects++; - - if (IsRunningInBrowser()) - { - // OnQuitCallbacks.Add(OnQuitDefaultCallback); - } - else - { - if (!isPlayRoomInitialized)/// - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - else - Debug.Log("Mock Player Created"); - } - } - - private List> OnQuitCallbacks = new(); - - - private void OnQuitDefaultCallback() - { - if (!isPlayRoomInitialized) - { - Debug.LogError("Playroom not initialized yet! Please call InsertCoin."); - } - - Players.Remove(id); - } - - [MonoPInvokeCallback(typeof(Action))] - private void OnQuitWrapperCallback() - { - if (OnQuitCallbacks != null) - foreach (var callback in OnQuitCallbacks) - callback?.Invoke(id); - } - - void IPlayerInteraction.InvokeOnQuitWrapperCallback() - { - OnQuitWrapperCallback(); - } - - public Action OnQuit(Action callback) - { - if (!isPlayRoomInitialized) - { - Debug.LogError("PlayroomKit is not loaded!. Please make sure to call InsertCoin first."); - return null; - } - else - { - OnQuitCallbacks.Add(callback); - - void Unsubscribe() - { - OnQuitCallbacks.Remove(callback); - } - - return Unsubscribe; - } - } - - public void SetState(string key, int value, bool reliable = false) - { - if (IsRunningInBrowser()) - { - SetPlayerStateByPlayerId(id, key, value, reliable); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - } - else - { - Debug.Log($"PlayerState Set! Key: {key}, Value: {value}"); - MockSetState(key, value); - } - } - } - - - public void SetState(string key, float value, bool reliable = false) - { - if (IsRunningInBrowser()) - { - SetPlayerStateFloatByPlayerId(id, key, value.ToString(CultureInfo.InvariantCulture), reliable); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - } - else - { - Debug.Log($"PlayerState Set! Key: {key}, Value: {value}"); - MockSetState(key, value); - } - } - } - - public void SetState(string key, bool value, bool reliable = false) - { - if (IsRunningInBrowser()) - { - SetPlayerStateByPlayerId(id, key, value, reliable); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - } - else - { - Debug.Log($"PlayerState Set! Key: {key}, Value: {value}"); - MockSetState(key, value); - } - } - } - - public void SetState(string key, string value, bool reliable = false) - { - if (IsRunningInBrowser()) - { - SetPlayerStateStringById(id, key, value, reliable); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - } - else - { - Debug.Log($"PlayerState Set! Key: {key}, Value: {value}"); - MockSetState(key, value); - } - } - } - public void SetState(string key, object value, bool reliable = false) - { - if (IsRunningInBrowser()) - { - string jsonString = JsonUtility.ToJson(value); - SetPlayerStateStringById(id, key, jsonString, reliable); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - } - else - { - Debug.Log($"State Set! Key: {key}, Value: {value}"); - MockSetState(key, value); - } - } - } - - public T GetState(string key) - { - if (IsRunningInBrowser()) - { - Type type = typeof(T); - if (type == typeof(int)) return (T)(object)GetPlayerStateIntById(id, key); - else if (type == typeof(float)) return (T)(object)GetPlayerStateFloatById(id, key); - else if (type == typeof(bool)) return (T)(object)GetPlayerStateBoolById(id, key); - else if (type == typeof(string)) return (T)(object)GetPlayerStateStringById(id, key); - else if (type == typeof(Vector3)) return (T)(object)JsonUtility.FromJson(GetPlayerStateStringById(id, key)); - else if (type == typeof(Vector2)) return (T)(object)JsonUtility.FromJson(GetPlayerStateStringById(id, key)); - else if (type == typeof(Quaternion)) return (T)(object)JsonUtility.FromJson(GetPlayerStateStringById(id, key)); - else throw new NotSupportedException($"Type {typeof(T)} is not supported by GetState"); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - return default; - } - else - { - return MockGetState(key); - } - } - } - - - public Dictionary GetState(string key, bool isReturnDictionary = false) - { - - if (IsRunningInBrowser() && isReturnDictionary) - { - var jsonString = GetPlayerStateDictionary(id, key); - return ParseJsonToDictionary(jsonString); - } - else - { - if (isPlayRoomInitialized) - { - if (isReturnDictionary) - { - return MockGetState>(key); - } - else - { - return default; - } - } - else - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - return default; - } - } - } - - private int GetPlayerStateInt(string key) - { - if (IsRunningInBrowser()) - { - return GetPlayerStateIntById(id, key); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - return default; - } - else - { - return MockGetState(key); - } - } - } - - private float GetPlayerStateFloat(string key) - { - if (IsRunningInBrowser()) - { - return GetPlayerStateFloatById(id, key); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - return default; - } - else - { - return MockGetState(key); - } - } - } - - private string GetPlayerStateString(string key) - { - if (IsRunningInBrowser()) - { - return GetPlayerStateStringById(id, key); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - return default; - } - else - { - return MockGetState(key); - } - } - } - - private bool GetPlayerStateBool(string key) - { - if (IsRunningInBrowser()) - { - if (GetPlayerStateIntById(id, key) == 1) - { - return true; - } - else if (GetPlayerStateIntById(id, key) == 0) - { - return false; - } - else - { - Debug.LogError("GetPlayerStateByPlayerId: " + key + " is not a bool"); - return false; - } - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - return default; - } - else - { - return MockGetState(key); - } - } - } - - // Dictionaries: - public void SetState(string key, Dictionary values, bool reliable = false) - { - if (IsRunningInBrowser()) - { - SetStateHelper(id, key, values, reliable); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - } - else - { - Debug.Log($"PlayerState Set! Key: {key}, Value: {values}"); - MockSetState(key, values); - } - } - } - - public void SetState(string key, Dictionary values, bool reliable = false) - { - if (IsRunningInBrowser()) - { - SetStateHelper(id, key, values, reliable); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - } - else - { - Debug.Log($"PlayerState Set! Key: {key}, Value: {values}"); - MockSetState(key, values); - } - } - } - - public void SetState(string key, Dictionary values, bool reliable = false) - { - if (IsRunningInBrowser()) - { - SetStateHelper(id, key, values, reliable); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - } - else - { - Debug.Log($"PlayerState Set! Key: {key}, Value: {values}"); - MockSetState(key, values); - } - } - } - - public void SetState(string key, Dictionary values, bool reliable = false) - { - if (IsRunningInBrowser()) - { - SetStateHelper(id, key, values, reliable); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - } - else - { - Debug.Log($"PlayerState Set! Key: {key}, Value: {values}"); - MockSetState(key, values); - } - } - } - - public void WaitForState(string StateKey, Action onStateSetCallback = null) - { - if (IsRunningInBrowser()) - { - WaitForPlayerStateInternal(id, StateKey, onStateSetCallback); - } - } - - public void Kick(Action OnKickCallBack = null) - { - if (IsRunningInBrowser()) - { - OnKickCallBack = onKickCallBack; - KickInternal(id, InvokeKickCallBack); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - return; - } - else - { - var player = GetPlayer(PlayerId); - Players.Remove(player.id); - onKickCallBack?.Invoke(); - } - } - } - - [DllImport("__Internal")] - private static extern void SetPlayerStateByPlayerId(string playerID, string key, int value, - bool reliable = false); - - [DllImport("__Internal")] - private static extern void SetPlayerStateFloatByPlayerId(string playerID, string key, string value, - bool reliable = false); - - [DllImport("__Internal")] - private static extern void SetPlayerStateByPlayerId(string playerID, string key, bool value, - bool reliable = false); - - [DllImport("__Internal")] - private static extern void SetPlayerStateStringById(string playerID, string key, string value, - bool reliable = false); - - [DllImport("__Internal")] - private static extern string GetProfileByPlayerId(string playerID); - - private static Profile ParseProfile(string json) - { - var jsonNode = JSON.Parse(json); - var profileData = new Profile(); - profileData.jsonColor = new Profile.JsonColor - { - r = jsonNode["color"]["r"].AsInt, - g = jsonNode["color"]["g"].AsInt, - b = jsonNode["color"]["b"].AsInt, - hexString = jsonNode["color"]["hexString"].Value, - hex = jsonNode["color"]["hex"].AsInt - }; - - ColorUtility.TryParseHtmlString(profileData.jsonColor.hexString, out UnityEngine.Color color1); - profileData.color = color1; - profileData.name = jsonNode["name"].Value; - profileData.photo = jsonNode["photo"].Value; - - return profileData; - } - - public Profile GetProfile() - { - if (IsRunningInBrowser()) - { - var jsonString = GetProfileByPlayerId(id); - var profileData = ParseProfile(jsonString); - return profileData; - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - return default; - } - else - { - Profile.JsonColor mockJsonColor = new() - { - r = 166, - g = 0, - b = 142, - hexString = "#a6008e" - }; - ColorUtility.TryParseHtmlString(mockJsonColor.hexString, out UnityEngine.Color color1); - var testProfile = new Profile() - { - color = color1, - name = "CoolPlayTest", - jsonColor = mockJsonColor, - photo = "testPhoto" - - }; - return testProfile; - } - } - } - - - private static Action onKickCallBack = null; - - [MonoPInvokeCallback(typeof(Action))] - private static void InvokeKickCallBack() - { - onKickCallBack?.Invoke(); - } - - [DllImport("__Internal")] - private static extern void KickInternal(string playerID, Action onKickCallBack = null); - - [DllImport("__Internal")] - private static extern void WaitForPlayerStateInternal(string playerID, string stateKey, Action onStateSetCallback = null); - - - private static bool GetPlayerStateBoolById(string id, string key) - { - if (IsRunningInBrowser()) - { - var stateValue = GetPlayerStateIntById(id, key); - return stateValue == 1 ? true : - stateValue == 0 ? false : - throw new InvalidOperationException($"GetStateBool: {key} is not a bool"); - } - else - { - if (!isPlayRoomInitialized) - { - Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); - return false; - } - else - { - return MockGetState(key); - } - } - } - - [DllImport("__Internal")] - private static extern int GetPlayerStateIntById(string playerID, string key); - - [DllImport("__Internal")] - private static extern float GetPlayerStateFloatById(string playerID, string key); - - [DllImport("__Internal")] - private static extern string GetPlayerStateStringById(string playerID, string key); - - // Helpers - [DllImport("__Internal")] - private static extern void SetPlayerStateDictionary(string playerID, string key, string jsonValues, - bool reliable = false); - - [DllImport("__Internal")] - private static extern string GetPlayerStateDictionary(string playerID, string key); - - private void SetStateHelper(string id, string key, Dictionary values, bool reliable = false) - { - var jsonObject = new JSONObject(); - - // Add key-value pairs to the JSON object - foreach (var kvp in values) - { - // Convert the value to double before adding to JSONNode - var value = Convert.ToDouble(kvp.Value); - jsonObject.Add(kvp.Key, value); - } - - // Serialize the JSON object to a string - var jsonString = jsonObject.ToString(); - - // Output the JSON string - SetPlayerStateDictionary(id, key, jsonString, reliable); - } - - - } } diff --git a/Assets/PlayroomKit/modules.meta b/Assets/PlayroomKit/modules.meta new file mode 100644 index 00000000..c9d80163 --- /dev/null +++ b/Assets/PlayroomKit/modules.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 79a6747e9bd7d634ab4eda87b3573984 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/PlayroomKit/modules/Player.cs b/Assets/PlayroomKit/modules/Player.cs new file mode 100644 index 00000000..a0a815f0 --- /dev/null +++ b/Assets/PlayroomKit/modules/Player.cs @@ -0,0 +1,641 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.InteropServices; +using AOT; +using SimpleJSON; +using UnityEngine; + +namespace Playroom +{ + // Player class + + + + public partial class PlayroomKit + { + + public interface IPlayerInteraction + { + void InvokeOnQuitWrapperCallback(); + } + + public class Player : IPlayerInteraction + { + [Serializable] + public class Profile + { + [NonSerialized] + public UnityEngine.Color color; + + public JsonColor jsonColor; + public string name; + public string photo; + + [Serializable] + public class JsonColor + { + public int r; + public int g; + public int b; + public string hexString; + public int hex; + } + + + + } + + + public string id; + private static int totalObjects = 0; + + + public Player(string id) + { + this.id = id; + totalObjects++; + + if (IsRunningInBrowser()) + { + // OnQuitCallbacks.Add(OnQuitDefaultCallback); + } + else + { + if (!isPlayRoomInitialized)/// + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + else + Debug.Log("Mock Player Created"); + } + } + + private List> OnQuitCallbacks = new(); + + + private void OnQuitDefaultCallback() + { + if (!isPlayRoomInitialized) + { + Debug.LogError("Playroom not initialized yet! Please call InsertCoin."); + } + + Players.Remove(id); + } + + [MonoPInvokeCallback(typeof(Action))] + private void OnQuitWrapperCallback() + { + if (OnQuitCallbacks != null) + foreach (var callback in OnQuitCallbacks) + callback?.Invoke(id); + } + + void IPlayerInteraction.InvokeOnQuitWrapperCallback() + { + OnQuitWrapperCallback(); + } + + public Action OnQuit(Action callback) + { + if (!isPlayRoomInitialized) + { + Debug.LogError("PlayroomKit is not loaded!. Please make sure to call InsertCoin first."); + return null; + } + else + { + OnQuitCallbacks.Add(callback); + + void Unsubscribe() + { + OnQuitCallbacks.Remove(callback); + } + + return Unsubscribe; + } + } + + public void SetState(string key, int value, bool reliable = false) + { + if (IsRunningInBrowser()) + { + SetPlayerStateByPlayerId(id, key, value, reliable); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + } + else + { + Debug.Log($"PlayerState Set! Key: {key}, Value: {value}"); + MockSetState(key, value); + } + } + } + + + public void SetState(string key, float value, bool reliable = false) + { + if (IsRunningInBrowser()) + { + SetPlayerStateFloatByPlayerId(id, key, value.ToString(CultureInfo.InvariantCulture), reliable); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + } + else + { + Debug.Log($"PlayerState Set! Key: {key}, Value: {value}"); + MockSetState(key, value); + } + } + } + + public void SetState(string key, bool value, bool reliable = false) + { + if (IsRunningInBrowser()) + { + SetPlayerStateByPlayerId(id, key, value, reliable); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + } + else + { + Debug.Log($"PlayerState Set! Key: {key}, Value: {value}"); + MockSetState(key, value); + } + } + } + + public void SetState(string key, string value, bool reliable = false) + { + if (IsRunningInBrowser()) + { + SetPlayerStateStringById(id, key, value, reliable); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + } + else + { + Debug.Log($"PlayerState Set! Key: {key}, Value: {value}"); + MockSetState(key, value); + } + } + } + + public void SetState(string key, object value, bool reliable = false) + { + if (IsRunningInBrowser()) + { + string jsonString = JsonUtility.ToJson(value); + SetPlayerStateStringById(id, key, jsonString, reliable); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + } + else + { + Debug.Log($"State Set! Key: {key}, Value: {value}"); + MockSetState(key, value); + } + } + } + + public T GetState(string key) + { + if (IsRunningInBrowser()) + { + Type type = typeof(T); + if (type == typeof(int)) return (T)(object)GetPlayerStateIntById(id, key); + else if (type == typeof(float)) return (T)(object)GetPlayerStateFloatById(id, key); + else if (type == typeof(bool)) return (T)(object)GetPlayerStateBoolById(id, key); + else if (type == typeof(string)) return (T)(object)GetPlayerStateStringById(id, key); + else if (type == typeof(Vector3)) return (T)(object)JsonUtility.FromJson(GetPlayerStateStringById(id, key)); + else if (type == typeof(Vector2)) return (T)(object)JsonUtility.FromJson(GetPlayerStateStringById(id, key)); + else if (type == typeof(Quaternion)) return (T)(object)JsonUtility.FromJson(GetPlayerStateStringById(id, key)); + else throw new NotSupportedException($"Type {typeof(T)} is not supported by GetState"); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + return default; + } + else + { + return MockGetState(key); + } + } + } + + + public Dictionary GetState(string key, bool isReturnDictionary = false) + { + + if (IsRunningInBrowser() && isReturnDictionary) + { + var jsonString = GetPlayerStateDictionary(id, key); + return ParseJsonToDictionary(jsonString); + } + else + { + if (isPlayRoomInitialized) + { + if (isReturnDictionary) + { + return MockGetState>(key); + } + else + { + return default; + } + } + else + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + return default; + } + } + } + + private int GetPlayerStateInt(string key) + { + if (IsRunningInBrowser()) + { + return GetPlayerStateIntById(id, key); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + return default; + } + else + { + return MockGetState(key); + } + } + } + + private float GetPlayerStateFloat(string key) + { + if (IsRunningInBrowser()) + { + return GetPlayerStateFloatById(id, key); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + return default; + } + else + { + return MockGetState(key); + } + } + } + + private string GetPlayerStateString(string key) + { + if (IsRunningInBrowser()) + { + return GetPlayerStateStringById(id, key); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + return default; + } + else + { + return MockGetState(key); + } + } + } + + private bool GetPlayerStateBool(string key) + { + if (IsRunningInBrowser()) + { + if (GetPlayerStateIntById(id, key) == 1) + { + return true; + } + else if (GetPlayerStateIntById(id, key) == 0) + { + return false; + } + else + { + Debug.LogError("GetPlayerStateByPlayerId: " + key + " is not a bool"); + return false; + } + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + return default; + } + else + { + return MockGetState(key); + } + } + } + + // Dictionaries: + public void SetState(string key, Dictionary values, bool reliable = false) + { + if (IsRunningInBrowser()) + { + SetStateHelper(id, key, values, reliable); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + } + else + { + Debug.Log($"PlayerState Set! Key: {key}, Value: {values}"); + MockSetState(key, values); + } + } + } + + public void SetState(string key, Dictionary values, bool reliable = false) + { + if (IsRunningInBrowser()) + { + SetStateHelper(id, key, values, reliable); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + } + else + { + Debug.Log($"PlayerState Set! Key: {key}, Value: {values}"); + MockSetState(key, values); + } + } + } + + public void SetState(string key, Dictionary values, bool reliable = false) + { + if (IsRunningInBrowser()) + { + SetStateHelper(id, key, values, reliable); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + } + else + { + Debug.Log($"PlayerState Set! Key: {key}, Value: {values}"); + MockSetState(key, values); + } + } + } + + public void SetState(string key, Dictionary values, bool reliable = false) + { + if (IsRunningInBrowser()) + { + SetStateHelper(id, key, values, reliable); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + } + else + { + Debug.Log($"PlayerState Set! Key: {key}, Value: {values}"); + MockSetState(key, values); + } + } + } + + public void WaitForState(string StateKey, Action onStateSetCallback = null) + { + if (IsRunningInBrowser()) + { + WaitForPlayerStateInternal(id, StateKey, onStateSetCallback); + } + } + + public void Kick(Action OnKickCallBack = null) + { + if (IsRunningInBrowser()) + { + OnKickCallBack = onKickCallBack; + KickInternal(id, InvokeKickCallBack); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + return; + } + else + { + var player = GetPlayer(PlayerId); + Players.Remove(player.id); + onKickCallBack?.Invoke(); + } + } + } + + + + private static Profile ParseProfile(string json) + { + var jsonNode = JSON.Parse(json); + var profileData = new Profile(); + profileData.jsonColor = new Profile.JsonColor + { + r = jsonNode["color"]["r"].AsInt, + g = jsonNode["color"]["g"].AsInt, + b = jsonNode["color"]["b"].AsInt, + hexString = jsonNode["color"]["hexString"].Value, + hex = jsonNode["color"]["hex"].AsInt + }; + + ColorUtility.TryParseHtmlString(profileData.jsonColor.hexString, out UnityEngine.Color color1); + profileData.color = color1; + profileData.name = jsonNode["name"].Value; + profileData.photo = jsonNode["photo"].Value; + + return profileData; + } + + public Profile GetProfile() + { + if (IsRunningInBrowser()) + { + var jsonString = GetProfileByPlayerId(id); + var profileData = ParseProfile(jsonString); + return profileData; + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + return default; + } + else + { + Profile.JsonColor mockJsonColor = new() + { + r = 166, + g = 0, + b = 142, + hexString = "#a6008e" + }; + ColorUtility.TryParseHtmlString(mockJsonColor.hexString, out UnityEngine.Color color1); + var testProfile = new Profile() + { + color = color1, + name = "CoolPlayTest", + jsonColor = mockJsonColor, + photo = "testPhoto" + + }; + return testProfile; + } + } + } + + + private static Action onKickCallBack = null; + + [MonoPInvokeCallback(typeof(Action))] + private static void InvokeKickCallBack() + { + onKickCallBack?.Invoke(); + } + + + private static bool GetPlayerStateBoolById(string id, string key) + { + if (IsRunningInBrowser()) + { + var stateValue = GetPlayerStateIntById(id, key); + return stateValue == 1 ? true : + stateValue == 0 ? false : + throw new InvalidOperationException($"GetStateBool: {key} is not a bool"); + } + else + { + if (!isPlayRoomInitialized) + { + Debug.LogError("[Mock Mode] Playroom not initialized yet! Please call InsertCoin."); + return false; + } + else + { + return MockGetState(key); + } + } + } + + + + private void SetStateHelper(string id, string key, Dictionary values, bool reliable = false) + { + var jsonObject = new JSONObject(); + + // Add key-value pairs to the JSON object + foreach (var kvp in values) + { + // Convert the value to double before adding to JSONNode + var value = Convert.ToDouble(kvp.Value); + jsonObject.Add(kvp.Key, value); + } + + // Serialize the JSON object to a string + var jsonString = jsonObject.ToString(); + + // Output the JSON string + SetPlayerStateDictionary(id, key, jsonString, reliable); + } + + [DllImport("__Internal")] + private static extern void KickInternal(string playerID, Action onKickCallBack = null); + + [DllImport("__Internal")] + private static extern void WaitForPlayerStateInternal(string playerID, string stateKey, Action onStateSetCallback = null); + + [DllImport("__Internal")] + private static extern void SetPlayerStateByPlayerId(string playerID, string key, int value, + bool reliable = false); + + [DllImport("__Internal")] + private static extern void SetPlayerStateFloatByPlayerId(string playerID, string key, string value, + bool reliable = false); + + [DllImport("__Internal")] + private static extern void SetPlayerStateByPlayerId(string playerID, string key, bool value, + bool reliable = false); + + [DllImport("__Internal")] + private static extern void SetPlayerStateDictionary(string playerID, string key, string jsonValues, + bool reliable = false); + + [DllImport("__Internal")] + private static extern void SetPlayerStateStringById(string playerID, string key, string value, + bool reliable = false); + + [DllImport("__Internal")] + private static extern int GetPlayerStateIntById(string playerID, string key); + + [DllImport("__Internal")] + private static extern float GetPlayerStateFloatById(string playerID, string key); + + [DllImport("__Internal")] + private static extern string GetPlayerStateStringById(string playerID, string key); + + + [DllImport("__Internal")] + private static extern string GetPlayerStateDictionary(string playerID, string key); + + [DllImport("__Internal")] + private static extern string GetProfileByPlayerId(string playerID); + } + } +} diff --git a/Assets/PlayroomKit/modules/Player.cs.meta b/Assets/PlayroomKit/modules/Player.cs.meta new file mode 100644 index 00000000..76a139f3 --- /dev/null +++ b/Assets/PlayroomKit/modules/Player.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dee2bc384854b2843a7a68de0436f253 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/PlayroomKit/modules/RPC.cs b/Assets/PlayroomKit/modules/RPC.cs new file mode 100644 index 00000000..75011d60 --- /dev/null +++ b/Assets/PlayroomKit/modules/RPC.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using AOT; +using SimpleJSON; +using UnityEngine; + +namespace Playroom +{ + public partial class PlayroomKit + { + // RPC: + public enum RpcMode + { + ALL, + OTHERS, + HOST + } + + private static Dictionary> rpcRegisterCallbacks = new(); + private static List rpcCalledEvents = new(); + + [DllImport("__Internal")] + private extern static void RpcRegisterInternal(string name, Action rpcRegisterCallback, string onResponseReturn = null); + + public static void RpcRegister(string name, Action rpcRegisterCallback, string onResponseReturn = null) + { + if (IsRunningInBrowser()) + { + rpcRegisterCallbacks.Add(name, rpcRegisterCallback); + RpcRegisterInternal(name, InvokeRpcRegisterCallBack, onResponseReturn); + } + else + { + Debug.LogError("[Mock Mode] RPC is currently not supported in Mock Mode!\n Please build the project to test RPC."); + } + } + + [MonoPInvokeCallback(typeof(Action))] + private static void InvokeRpcRegisterCallBack(string dataJson, string senderJson) + { + try + { + if (!Players.ContainsKey(senderJson)) + { + var player = new Player(senderJson); + Players.Add(senderJson, player); + } + + } + catch (Exception ex) + { + Debug.LogError(ex.Message); + } + + + List updatedRpcCalledEvents = new(); + // This state is required to update the called rpc events list, (Temp fix see RpcCall for more) + string nameJson = GetState("rpcCalledEventName"); + + JSONArray jsonArray = JSON.Parse(nameJson).AsArray; + foreach (JSONNode node in jsonArray) + { + string item = node.Value; + updatedRpcCalledEvents.Add(item); + } + + foreach (string name in updatedRpcCalledEvents) + { + if (rpcRegisterCallbacks.TryGetValue(name, out Action callback)) + { + callback?.Invoke(dataJson, senderJson); + } + } + + } + + [DllImport("__Internal")] + private extern static void RpcCallInternal(string name, string data, RpcMode mode, Action callbackOnResponse); + + private static Dictionary> OnResponseCallbacks = new Dictionary>(); + + public static void RpcCall(string name, object data, RpcMode mode, Action callbackOnResponse) + { + + if (IsRunningInBrowser()) + { + string jsonData = ConvertToJson(data); + if (OnResponseCallbacks.ContainsKey(name)) + { + OnResponseCallbacks[name].Add(callbackOnResponse); + } + else + { + OnResponseCallbacks.Add(name, new List { callbackOnResponse }); + if (!rpcCalledEvents.Contains(name)) + { + rpcCalledEvents.Add(name); + } + } + JSONArray jsonArray = new JSONArray(); + foreach (string item in rpcCalledEvents) + { + jsonArray.Add(item); + } + string jsonString = jsonArray.ToString(); + /* + This is requrired to sync the rpc events between all players, without this players won't know which event has been called. + this is a temporary fix, RPC's need to be handled within JS for better control. + */ + SetState("rpcCalledEventName", jsonString, reliable: true); + + RpcCallInternal(name, jsonData, mode, InvokeOnResponseCallback); + } + else + { + Debug.LogError("[Mock Mode] RPC Calls are not supported in Mock Mode! yet.\nPlease make a build to test RPC."); + } + + } + + // Default Mode + public static void RpcCall(string name, object data, Action callbackOnResponse) + { + RpcCall(name, data, RpcMode.ALL, callbackOnResponse); + } + + [MonoPInvokeCallback(typeof(Action))] + private static void InvokeOnResponseCallback() + { + var namesToRemove = new List(); + + foreach (string name in rpcCalledEvents) + { + try + { + if (OnResponseCallbacks.TryGetValue(name, out List callbacks)) + { + foreach (var callback in callbacks) + { + callback?.Invoke(); + } + + namesToRemove.Add(name); + } + } + catch (Exception ex) + { + Debug.LogError($"C#: Error in Invoking callback for RPC event name: '{name}': {ex.Message}"); + } + } + + foreach (var name in namesToRemove) + { + rpcCalledEvents.Remove(name); + OnResponseCallbacks.Remove(name); + } + } + + + private static string ConvertToJson(object data) + { + if (data == null) + { + return null; + } + else if (data.GetType().IsPrimitive || data is string) + { + return data.ToString(); + } + if (data is Vector2 vector2) + { + return JsonUtility.ToJson(vector2); + } + else if (data is Vector3 vector3) + { + return JsonUtility.ToJson(vector3); + } + else if (data is Vector4 vector4) + { + return JsonUtility.ToJson(vector4); + } + else if (data is Quaternion quaternion) + { + return JsonUtility.ToJson(quaternion); + } + else + { + return ConvertComplexToJson(data); + } + } + + private static string ConvertComplexToJson(object data) + { + if (data is IDictionary dictionary) + { + JSONObject dictNode = new JSONObject(); + foreach (DictionaryEntry entry in dictionary) + { + dictNode[entry.Key.ToString()] = ConvertToJson(entry.Value); + } + return dictNode.ToString(); + } + else if (data is IEnumerable enumerable) + { + JSONArray arrayNode = new JSONArray(); + foreach (object element in enumerable) + { + arrayNode.Add(ConvertToJson(element)); + } + return arrayNode.ToString(); + } + else + { + Debug.Log($"{data} is '{data.GetType()}' which is currently not supported by RPC!"); + return JSON.Parse("{}").ToString(); + } + } + } +} diff --git a/Assets/PlayroomKit/modules/RPC.cs.meta b/Assets/PlayroomKit/modules/RPC.cs.meta new file mode 100644 index 00000000..0de8f1fc --- /dev/null +++ b/Assets/PlayroomKit/modules/RPC.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: daadfc50e11096c4598b324f48edf1e9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: