using CIG3.ExtensionMethods; using SUISS.Cloud; using SUISS.Cloud.Boomlagoon.JSON; using SUISS.Core; using SUISS.Storage; using SUISSEngine; using System; using System.Collections; using System.Collections.Generic; using System.Threading; using UnityEngine; public sealed class CIGVisitingIslandsManager : SingletonMonobehaviour { private class IslandData : StorableDictionary { } private class StorableStringDict : StorableDictionary { } public static readonly TimeSpan CacheRetention = TimeSpan.FromDays(1.0); private const string IslandsEndPoint = "https://sparkling-society-islands.herokuapp.com"; private const string LoginUrl = "https://sparkling-society-usernames.herokuapp.com/login/gamestates"; public const string StorageKey = "OtherIslandsData"; public const string IslandCacheKey = "IslandCache"; public const string LastSentIslandDataKey = "LastSentIslandData"; public GameObject[] islands; public GameObject[] otherIslands; private CIGSparkSocServices _services; private IAccessToken _accessToken; private Island _currentIsland; private string _currentIslandUuid; private string _islandsUrl = "{0}/islands/{1}/{2}?access_token={3}"; private Dictionary storage => Storage.Get(StorageLifecycle.Game).GetDictionary("OtherIslandsData"); private IslandData IslandCache { get { if (!storage.ContainsKey("IslandCache")) { storage["IslandCache"] = new IslandData(); } return (IslandData)storage["IslandCache"]; } set { storage["IslandCache"] = value; } } private StorableStringDict LastSentIslandData { get { if (!storage.ContainsKey("LastSentIslandData")) { storage["LastSentIslandData"] = new StorableStringDict(); } return (StorableStringDict)storage["LastSentIslandData"]; } set { storage["LastSentIslandData"] = value; } } public bool IsLoggedIn => _accessToken != null && !_accessToken.HasExpired; public int SentIslandCount => LastSentIslandData.Count; public event Action DataDownloadedEvent; private void FireDataDownloadedEventEvent(Island island, string uuid) { if (this.DataDownloadedEvent != null) { this.DataDownloadedEvent(island, uuid); } } private void Start() { _services = SingletonMonobehaviour.Instance; if (_services == null) { UnityEngine.Debug.LogError("CIGVisitingIslandsManager requires CIGSparkSocServices, but was unable to find it!"); } StartCoroutine(SyncMyIslands()); } protected override void Awake() { base.Awake(); } protected override void OnDestroy() { base.OnDestroy(); } public bool IslandIsSent() { return LastSentIslandData.Count > 0; } public bool LoadVisitingIslandData(Island island, string uuid) { if (IslandCache.ContainsKey(uuid + island.GetSceneName())) { VisitingIsland visitingIsland = IslandCache[uuid + island.GetSceneName()]; ImportIsland(island, JsonToGrid(visitingIsland.Data)); return true; } return false; } public bool VisitingIslandDataAvaible(Island island, string uuid) { return IslandCache.ContainsKey(uuid + island.GetSceneName()); } public bool IsUnlocked(Island island, string uuid) { if (IslandCache.ContainsKey(uuid + island.GetSceneName())) { return IslandCache[uuid + island.GetSceneName()].UnLocked; } return false; } public void DownLoadIslandData(Island island, string uuid) { if (!VisitingIslandDataAvaible(island, uuid) || !(IslandCache[uuid + island.GetSceneName()].Time + CacheRetention > DateTime.UtcNow)) { LoadIslandData(island, uuid); } } public void DownloadAllIslandsData(string uuid) { foreach (Island island in IslandExtensions.GetIslands()) { DownLoadIslandData(island, uuid); } } public GameObject GetReadOnlyIslandPrefabByName(Island island) { GameObject[] array = otherIslands; foreach (GameObject gameObject in array) { if (gameObject.name == island.GetSceneName() + "ReadOnly") { return gameObject; } } return null; } private bool ShouldSync() { if (!SingletonMonobehaviour.IsAvailable || !SingletonMonobehaviour.IsAvailable || !SingletonMonobehaviour.IsAvailable) { return false; } CIGSparkSocServices instance = SingletonMonobehaviour.Instance; CIGGameStats instance2 = SingletonMonobehaviour.Instance; CIGWebService instance3 = SingletonMonobehaviour.Instance; if (!instance.IsLinkedToPlayername()) { return false; } if (instance.IsTopPlayer()) { return true; } if (instance2.GetTotalBuildingCount() < 20) { return false; } if (!ShouldSyncUuid(instance.GetInstallUuid(), instance3.IslandSyncChangePercent)) { return false; } return true; } private bool ShouldSyncUuid(string uuid, int syncPercent) { float num = Mathf.Clamp01((float)syncPercent / 100f); uint num2 = (uint)(4.2949673E+09f * num); uint hashCode = (uint)uuid.GetHashCode(); return hashCode <= num2; } private IEnumerator SyncMyIslands() { yield return null; while (true) { if (ShouldSync()) { foreach (Island island in IslandExtensions.GetIslands()) { JSONValue data = StorableToJson(ExportIsland(island)); CloudRequest sendData = SendIslandData(island, data); yield return sendData; if (sendData.Error != null) { UnityEngine.Debug.LogWarning($"Failed to update {island.GetSceneName()} island. Message {sendData.Error.Error}"); } } yield return new WaitForSeconds(300f); } else { yield return new WaitForSeconds(10f); } } } private GameObject FindIslandPrefabByName(Island island) { GameObject[] array = islands; foreach (GameObject gameObject in array) { if (gameObject.name == island.GetSceneName()) { return gameObject; } } return null; } private bool HasIslandChanged(Island island, JSONValue data) { if (!LastSentIslandData.ContainsKey(island.GetSceneName())) { return true; } return LastSentIslandData[island.GetSceneName()] != data.ToString(); } private string GetGridStorageKey(GameObject islandPrefab) { if (islandPrefab != null) { IsometricIsland component = islandPrefab.GetComponent(); if (component != null) { IsometricGrid grid = component.grid; if (grid != null) { Serializing serializing = grid.serializing; if (serializing != null) { return (serializing.StorageKey == null) ? string.Empty : serializing.StorageKey; } } } } return string.Empty; } private Dictionary ExportIsland(Island island) { string gridStorageKey = GetGridStorageKey(FindIslandPrefabByName(island)); if (gridStorageKey != null && gridStorageKey.Length > 0) { return Storage.Get(StorageLifecycle.Game).GetDictionary(gridStorageKey); } return new Dictionary(); } private void ImportIsland(Island island, Dictionary grid) { string gridStorageKey = GetGridStorageKey(GetReadOnlyIslandPrefabByName(island)); if (gridStorageKey != null && gridStorageKey.Length > 0) { Storage storage = Storage.Get(StorageLifecycle.Game); storage.Root[gridStorageKey] = grid; } } private Dictionary JsonToGrid(JSONObject dataObj) { Dictionary dictionary = new Dictionary(); foreach (KeyValuePair item in dataObj) { if (item.Value.Type != 0) { UnityEngine.Debug.LogError("Grid data should only have string values! But found:" + item.Value.Type.ToString()); } else { dictionary.Add(item.Key, item.Value.Str); } } return dictionary; } private JSONValue StorableToJson(object data) { if (object.ReferenceEquals(data, null)) { return new JSONValue((JSONObject)null); } if (data is Dictionary) { Dictionary dictionary = (Dictionary)data; JSONObject jSONObject = new JSONObject(); foreach (KeyValuePair item in dictionary) { jSONObject.Add(item.Key, StorableToJson(item.Value)); } return new JSONValue(jSONObject); } if (data is List) { List list = (List)data; JSONArray jSONArray = new JSONArray(); foreach (object item2 in list) { jSONArray.Add(StorableToJson(item2)); } return new JSONValue(jSONArray); } if (data is object[]) { object[] array = (object[])data; JSONArray jSONArray2 = new JSONArray(); object[] array2 = array; foreach (object data2 in array2) { jSONArray2.Add(StorableToJson(data2)); } return new JSONValue(jSONArray2); } if (data is string) { return new JSONValue((string)data); } if (data is bool) { return new JSONValue((bool)data); } if (data is char) { return new JSONValue(((char)data).ToString()); } if (data is float) { return new JSONValue(Convert.ToDouble((float)data)); } if (data is double) { return new JSONValue((double)data); } if (data is sbyte) { return new JSONValue(((sbyte)data).ToString()); } if (data is byte) { return new JSONValue(((byte)data).ToString()); } if (data is short) { return new JSONValue(Convert.ToDouble((short)data)); } if (data is ushort) { return new JSONValue(Convert.ToDouble((ushort)data)); } if (data is int) { return new JSONValue(Convert.ToDouble((int)data)); } if (data is uint) { return new JSONValue(Convert.ToDouble((uint)data)); } if (data is long) { return new JSONValue(Convert.ToDouble((long)data)); } if (data is ulong) { return new JSONValue(Convert.ToDouble((ulong)data)); } if (data is decimal) { return new JSONValue(Convert.ToDouble((decimal)data)); } if (data is IStorable) { return StorableToJson(((IStorable)data).ToStorage()); } return new JSONValue((JSONObject)null); } private CloudRequest LoginIslandServer() { return new CloudRequest(CoLoginIslandServer()); } private IEnumerator CoLoginIslandServer() { if (_services.IsRegisteredInstall()) { CloudRequest request = _services.GetVisitingIslandsAccessToken(); yield return request; if (request.Error.HasValue) { yield return new YieldError(new ApiError { Error = request.Error.ToString(), ErrorCode = (int)request.Error.Value, Status = 0, ServerError = false, NoInternet = false }); } else { _accessToken = request.Result; } } } private CloudRequest SendIslandData(Island island, JSONValue data) { return new CloudRequest(CoSendIslandData(island, data)); } private IEnumerator CoSendIslandData(Island island, JSONValue data) { if (!HasIslandChanged(island, data)) { yield break; } if (!IsLoggedIn) { CloudRequest login = LoginIslandServer(); yield return login; if (login.Error != null) { UnityEngine.Debug.LogWarning("[Login] Could not send island data. " + login.Error.Error); yield return new YieldError(login.Error); yield break; } } string url = string.Format(_islandsUrl, "https://sparkling-society-islands.herokuapp.com", _services.GetInstallUuid(), island.GetSceneName(), _accessToken.Token); ApiRequest request = ApiRequest.Post(url, data); yield return request; if (request.Error != null) { UnityEngine.Debug.LogError("Unable to send island data. Server message:" + request.Error.Error); yield return new YieldError(request.Error); } else { UnityEngine.Debug.Log("Island: " + island.GetSceneName() + " data send to server!"); LastSentIslandData[island.GetSceneName()] = data.ToString(); } } private CloudRequest LoadIslandData(Island island, string installUuid) { return new CloudRequest(CoLoadIslandData(island, installUuid)); } private IEnumerator CoLoadIslandData(Island island, string installUuid) { if (!IsLoggedIn) { CloudRequest login = LoginIslandServer(); yield return login; if (login.Error != null) { UnityEngine.Debug.LogError("[Login] Could not load island data. " + login.Error.Error); yield return new YieldError(login.Error); yield break; } } string url = string.Format(_islandsUrl, "https://sparkling-society-islands.herokuapp.com", installUuid, island.GetSceneName(), _accessToken.Token); ApiRequest request = ApiRequest.Get(url); yield return request; if (request.Error != null) { if (request.Error.Status == 404) { UnityEngine.Debug.LogWarning("Trying to get data from a player who has never sent us gamestate! (http 404)"); IslandCache[installUuid + island.GetSceneName()] = new VisitingIsland(island, installUuid, new JSONObject()); FireDataDownloadedEventEvent(island, installUuid); } UnityEngine.Debug.LogError("[Island Data] Could not load island data. " + request.Error.Error); yield return new YieldError(request.Error); yield break; } JSONObject data = new JSONObject(); if (request.Result.Data.Type != JSONValueType.Object || !request.Result.Data.Obj.ContainsKey("data")) { UnityEngine.Debug.LogWarning("Get Island data expected 'data' field. But the server didn't provide it."); } else { JSONValue jSONValue = request.Result.Data.Obj["data"]; if (jSONValue.Type != JSONValueType.Object) { UnityEngine.Debug.LogError("Island data expected to be json Object found: " + jSONValue.Type.ToString()); } else { data = jSONValue.Obj; } } IslandCache[installUuid + island.GetSceneName()] = new VisitingIsland(island, installUuid, data); FireDataDownloadedEventEvent(island, installUuid); } }