using System.IO; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.Networking; using UnityEngine.Scripting; using Passer.Humanoid; namespace Passer { /// /// The Possession of a Humanoid Visitor /// /// /// \image html PawnPossessionsInspector.png /// /// * Default possessions, see PawnPossessions::defaultPossessions /// * Clear at start, see PawnPossessions::clearAtStart /// \version 4.0 and higher public class VisitorPossessions : MonoBehaviour { [System.Serializable] public class Possession { public string name; public Possessable.Type type; public bool persistent; public bool removable = true; public string siteLocation; public string assetPath; [System.NonSerialized] public Possessable scenePossession; } [System.Serializable] public class CachedPossession { public string siteLocation; public AssetBundle assetBundle; } protected static List cache = new List(); public class Possessions { public List list = new List(); } //// May include an amount later //public static Possessions possessions; public List possessions; public List myPossessions { get { return possessions; } } /// /// The possessions which are available from the start /// public Possessable[] defaultPossessions; /// /// Clear the possessions when the application is started /// /// Default possessions will not be cleared. /// This works only on real Humanoids, not on HumanoidInterfaces/Sites public bool clearOnAwake; private string filePath { get { string filePath = Path.Combine(Application.persistentDataPath, "MyPossessions.json"); return filePath; } } #region Init protected virtual void Awake() { // Only do this for real humanoids HumanoidControl humanoid = GetComponentInParent(); if (humanoid != null) { if (!clearOnAwake) { //Debug.Log("Retrieve possessions from " + filePath); if (File.Exists(filePath)) { string json = File.ReadAllText(filePath); Possessions possessionList = JsonUtility.FromJson(json); possessions = possessionList.list; //JsonUtility.FromJson(json); } else { Debug.Log("Cleared Possessions"); possessions = new List();//new Possessions(); } } else { Debug.Log("Cleared Possessions"); possessions = new List(); // new Possessions(); } } // Default Possessions cannot be deleted AddPossessions(defaultPossessions, false); } #endregion #region Stop protected virtual void OnDestroy() { //Debug.Log("Store possessions to " + filePath); string json = JsonUtility.ToJson(possessions); File.WriteAllText(filePath, json); //Debug.Log("Possesions stored"); } public static void DestroyScenePossessions() { //possessions.list.RemoveAll(poss => poss.persistent == false); } #endregion /// /// Try to add the gameObject to this humanoidpossessions. /// This only succeeds whent the hameObject has a Possessable component. /// /// The GameObject to add public void TryAddGameObject(GameObject gameObject) { Possessable possession = gameObject.GetComponentInChildren(); if (possession == null) return; Add(possession); } public void AddPossessions(Possessable[] scenePossessions, bool removable = true) { if (possessions == null || scenePossessions == null) return; foreach (Possessable possession in scenePossessions) { if (possession == null) continue; Possession persistentPossession = Add(possession); persistentPossession.scenePossession = PreservePossession(possession); persistentPossession.persistent = possession.crossSite; persistentPossession.removable = removable; } } private static Possessable PreservePossession(Possessable possession) { GameObject preservedPossession = Instantiate(possession.gameObject); preservedPossession.SetActive(false); Object.DontDestroyOnLoad(preservedPossession); return preservedPossession.GetComponent(); } /// /// Add the possessable object to the humanoidPossessions /// /// The possessable object to add /// The persistent possession public Possession Add(Possessable possessable) { if (possessable == null) return null; if (possessable.isUnique) { Possession foundPossession = possessions.Find( persistentPossession => persistentPossession.assetPath == possessable.assetPath ); if (foundPossession != null) return foundPossession; } Possession newPossession = new Possession() { name = possessable.name, siteLocation = possessable.siteLocation, assetPath = possessable.assetPath, type = possessable.possessionType, }; possessions.Add(newPossession); return newPossession; } public void DeletePossession(Possession possession) { Debug.Log("deleting " + possession); Possession foundPossession = possessions.Find( persistentPossession => persistentPossession.assetPath == possession.assetPath ); if (foundPossession == null) return; possessions.Remove(foundPossession); Debug.Log("deleted"); } private void RetrievePossession(Possession possession) { StartCoroutine(RetrievePossessionAsync(possession)); } //public void RetrievePossession(PersistentPossession possession, System.Action callback) { // StartCoroutine(RetrievePossessionAsync(possession, callback)); //} private static IEnumerator RetrievePossessionAsync(Possession possession) { string url = "https://" + possession.siteLocation + ".windows" + ".site"; Debug.Log("Loading possession: " + url); UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(url); yield return request.SendWebRequest(); AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(request); if (assetBundle == null) { Debug.LogError("Could not load " + url); yield break; } GameObject prefab = assetBundle.LoadAsset(possession.assetPath); if (prefab != null) Object.Instantiate(prefab); } private static AssetBundle lastAssetBundle; public static IEnumerator RetrievePossessionAsync(Possession possession, System.Action callback) { GameObject prefab; if (possession.siteLocation == "") { prefab = possession.scenePossession.gameObject; prefab.SetActive(true); callback(prefab); prefab.SetActive(false); } else { Debug.Log("Cache size: " + cache.Count); CachedPossession foundPossession = cache.Find(entry => entry.siteLocation == possession.siteLocation); if (foundPossession == null) { string url = "https://" + possession.siteLocation + ".windows" + ".site"; Debug.Log("Loading possession: " + url); UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(url); yield return request.SendWebRequest(); AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(request); if (assetBundle == null) { Debug.LogError("Could not load " + url); yield break; } lastAssetBundle = assetBundle; Debug.Log("Load: " + possession.assetPath); prefab = assetBundle.LoadAsset(possession.assetPath); if (prefab == null) { Debug.LogError("Could not load " + possession.assetPath); yield break; } CachedPossession cachedPossession = new CachedPossession() { siteLocation = possession.siteLocation, assetBundle = assetBundle, }; cache.Add(cachedPossession); } else { Debug.Log("Load from cache: " + possession.assetPath); prefab = foundPossession.assetBundle.LoadAsset(possession.assetPath); if (prefab == null) { Debug.LogError("Could not load " + possession.assetPath); yield break; } } callback(prefab); } } public static void UnloadPossession() { lastAssetBundle.Unload(true); } private int currentAvatarIndex = 0; public void UseNextAvatar() { List avatars = possessions.FindAll(possession => possession.type == Possessable.Type.Avatar); if (avatars.Count == 0) return; currentAvatarIndex = mod(currentAvatarIndex + 1, avatars.Count); UseAvatar(currentAvatarIndex); } public void UseAvatar(int avatarIndex) { List avatars = possessions.FindAll(possession => possession.type == Possessable.Type.Avatar); if (avatarIndex < 0 || avatarIndex > avatars.Count) return; HumanoidControl humanoid = FindObjectOfType(); if (humanoid == null) return; if (avatars[avatarIndex] != null) { if (avatars[avatarIndex].scenePossession != null) humanoid.ChangeAvatar(avatars[avatarIndex].scenePossession.gameObject); else StartCoroutine(RetrieveAvatarAsync(avatars[avatarIndex])); } } private static IEnumerator RetrieveAvatarAsync(Possession possession) { HumanoidControl humanoid = FindObjectOfType(); if (humanoid == null) yield break; string url = "https://" + possession.siteLocation + ".windows" + ".site"; Debug.Log("Loading possession: " + url); UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(url); yield return request.SendWebRequest(); AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(request); if (assetBundle == null) { Debug.LogError("Could not load " + url); yield break; } GameObject avatarPrefab = assetBundle.LoadAsset(possession.assetPath); if (avatarPrefab != null) humanoid.ChangeAvatar(avatarPrefab); } public static int mod(int k, int n) { k %= n; return (k < 0) ? k + n : k; } } }