您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

538 行
17 KiB

  1. using CIG3.ExtensionMethods;
  2. using SUISS.Cloud;
  3. using SUISS.Cloud.Boomlagoon.JSON;
  4. using SUISS.Core;
  5. using SUISS.Storage;
  6. using SUISSEngine;
  7. using System;
  8. using System.Collections;
  9. using System.Collections.Generic;
  10. using System.Threading;
  11. using UnityEngine;
  12. public sealed class CIGVisitingIslandsManager : SingletonMonobehaviour<CIGVisitingIslandsManager>
  13. {
  14. private class IslandData : StorableDictionary<VisitingIsland>
  15. {
  16. }
  17. private class StorableStringDict : StorableDictionary<string>
  18. {
  19. }
  20. public static readonly TimeSpan CacheRetention = TimeSpan.FromDays(1.0);
  21. private const string IslandsEndPoint = "https://sparkling-society-islands.herokuapp.com";
  22. private const string LoginUrl = "https://sparkling-society-usernames.herokuapp.com/login/gamestates";
  23. public const string StorageKey = "OtherIslandsData";
  24. public const string IslandCacheKey = "IslandCache";
  25. public const string LastSentIslandDataKey = "LastSentIslandData";
  26. public GameObject[] islands;
  27. public GameObject[] otherIslands;
  28. private CIGSparkSocServices _services;
  29. private IAccessToken _accessToken;
  30. private Island _currentIsland;
  31. private string _currentIslandUuid;
  32. private string _islandsUrl = "{0}/islands/{1}/{2}?access_token={3}";
  33. private Dictionary<string, object> storage => Storage.Get(StorageLifecycle.Game).GetDictionary("OtherIslandsData");
  34. private IslandData IslandCache
  35. {
  36. get
  37. {
  38. if (!storage.ContainsKey("IslandCache"))
  39. {
  40. storage["IslandCache"] = new IslandData();
  41. }
  42. return (IslandData)storage["IslandCache"];
  43. }
  44. set
  45. {
  46. storage["IslandCache"] = value;
  47. }
  48. }
  49. private StorableStringDict LastSentIslandData
  50. {
  51. get
  52. {
  53. if (!storage.ContainsKey("LastSentIslandData"))
  54. {
  55. storage["LastSentIslandData"] = new StorableStringDict();
  56. }
  57. return (StorableStringDict)storage["LastSentIslandData"];
  58. }
  59. set
  60. {
  61. storage["LastSentIslandData"] = value;
  62. }
  63. }
  64. public bool IsLoggedIn => _accessToken != null && !_accessToken.HasExpired;
  65. public int SentIslandCount => LastSentIslandData.Count;
  66. public event Action<Island, string> DataDownloadedEvent;
  67. private void FireDataDownloadedEventEvent(Island island, string uuid)
  68. {
  69. if (this.DataDownloadedEvent != null)
  70. {
  71. this.DataDownloadedEvent(island, uuid);
  72. }
  73. }
  74. private void Start()
  75. {
  76. _services = SingletonMonobehaviour<CIGSparkSocServices>.Instance;
  77. if (_services == null)
  78. {
  79. UnityEngine.Debug.LogError("CIGVisitingIslandsManager requires CIGSparkSocServices, but was unable to find it!");
  80. }
  81. StartCoroutine(SyncMyIslands());
  82. }
  83. protected override void Awake()
  84. {
  85. base.Awake();
  86. }
  87. protected override void OnDestroy()
  88. {
  89. base.OnDestroy();
  90. }
  91. public bool IslandIsSent()
  92. {
  93. return LastSentIslandData.Count > 0;
  94. }
  95. public bool LoadVisitingIslandData(Island island, string uuid)
  96. {
  97. if (IslandCache.ContainsKey(uuid + island.GetSceneName()))
  98. {
  99. VisitingIsland visitingIsland = IslandCache[uuid + island.GetSceneName()];
  100. ImportIsland(island, JsonToGrid(visitingIsland.Data));
  101. return true;
  102. }
  103. return false;
  104. }
  105. public bool VisitingIslandDataAvaible(Island island, string uuid)
  106. {
  107. return IslandCache.ContainsKey(uuid + island.GetSceneName());
  108. }
  109. public bool IsUnlocked(Island island, string uuid)
  110. {
  111. if (IslandCache.ContainsKey(uuid + island.GetSceneName()))
  112. {
  113. return IslandCache[uuid + island.GetSceneName()].UnLocked;
  114. }
  115. return false;
  116. }
  117. public void DownLoadIslandData(Island island, string uuid)
  118. {
  119. if (!VisitingIslandDataAvaible(island, uuid) || !(IslandCache[uuid + island.GetSceneName()].Time + CacheRetention > DateTime.UtcNow))
  120. {
  121. LoadIslandData(island, uuid);
  122. }
  123. }
  124. public void DownloadAllIslandsData(string uuid)
  125. {
  126. foreach (Island island in IslandExtensions.GetIslands())
  127. {
  128. DownLoadIslandData(island, uuid);
  129. }
  130. }
  131. public GameObject GetReadOnlyIslandPrefabByName(Island island)
  132. {
  133. GameObject[] array = otherIslands;
  134. foreach (GameObject gameObject in array)
  135. {
  136. if (gameObject.name == island.GetSceneName() + "ReadOnly")
  137. {
  138. return gameObject;
  139. }
  140. }
  141. return null;
  142. }
  143. private bool ShouldSync()
  144. {
  145. if (!SingletonMonobehaviour<CIGSparkSocServices>.IsAvailable || !SingletonMonobehaviour<CIGGameStats>.IsAvailable || !SingletonMonobehaviour<CIGWebService>.IsAvailable)
  146. {
  147. return false;
  148. }
  149. CIGSparkSocServices instance = SingletonMonobehaviour<CIGSparkSocServices>.Instance;
  150. CIGGameStats instance2 = SingletonMonobehaviour<CIGGameStats>.Instance;
  151. CIGWebService instance3 = SingletonMonobehaviour<CIGWebService>.Instance;
  152. if (!instance.IsLinkedToPlayername())
  153. {
  154. return false;
  155. }
  156. if (instance.IsTopPlayer())
  157. {
  158. return true;
  159. }
  160. if (instance2.GetTotalBuildingCount() < 20)
  161. {
  162. return false;
  163. }
  164. if (!ShouldSyncUuid(instance.GetInstallUuid(), instance3.IslandSyncChangePercent))
  165. {
  166. return false;
  167. }
  168. return true;
  169. }
  170. private bool ShouldSyncUuid(string uuid, int syncPercent)
  171. {
  172. float num = Mathf.Clamp01((float)syncPercent / 100f);
  173. uint num2 = (uint)(4.2949673E+09f * num);
  174. uint hashCode = (uint)uuid.GetHashCode();
  175. return hashCode <= num2;
  176. }
  177. private IEnumerator SyncMyIslands()
  178. {
  179. yield return null;
  180. while (true)
  181. {
  182. if (ShouldSync())
  183. {
  184. foreach (Island island in IslandExtensions.GetIslands())
  185. {
  186. JSONValue data = StorableToJson(ExportIsland(island));
  187. CloudRequest<ApiError> sendData = SendIslandData(island, data);
  188. yield return sendData;
  189. if (sendData.Error != null)
  190. {
  191. UnityEngine.Debug.LogWarning($"Failed to update {island.GetSceneName()} island. Message {sendData.Error.Error}");
  192. }
  193. }
  194. yield return new WaitForSeconds(300f);
  195. }
  196. else
  197. {
  198. yield return new WaitForSeconds(10f);
  199. }
  200. }
  201. }
  202. private GameObject FindIslandPrefabByName(Island island)
  203. {
  204. GameObject[] array = islands;
  205. foreach (GameObject gameObject in array)
  206. {
  207. if (gameObject.name == island.GetSceneName())
  208. {
  209. return gameObject;
  210. }
  211. }
  212. return null;
  213. }
  214. private bool HasIslandChanged(Island island, JSONValue data)
  215. {
  216. if (!LastSentIslandData.ContainsKey(island.GetSceneName()))
  217. {
  218. return true;
  219. }
  220. return LastSentIslandData[island.GetSceneName()] != data.ToString();
  221. }
  222. private string GetGridStorageKey(GameObject islandPrefab)
  223. {
  224. if (islandPrefab != null)
  225. {
  226. IsometricIsland component = islandPrefab.GetComponent<IsometricIsland>();
  227. if (component != null)
  228. {
  229. IsometricGrid grid = component.grid;
  230. if (grid != null)
  231. {
  232. Serializing serializing = grid.serializing;
  233. if (serializing != null)
  234. {
  235. return (serializing.StorageKey == null) ? string.Empty : serializing.StorageKey;
  236. }
  237. }
  238. }
  239. }
  240. return string.Empty;
  241. }
  242. private Dictionary<string, object> ExportIsland(Island island)
  243. {
  244. string gridStorageKey = GetGridStorageKey(FindIslandPrefabByName(island));
  245. if (gridStorageKey != null && gridStorageKey.Length > 0)
  246. {
  247. return Storage.Get(StorageLifecycle.Game).GetDictionary(gridStorageKey);
  248. }
  249. return new Dictionary<string, object>();
  250. }
  251. private void ImportIsland(Island island, Dictionary<string, object> grid)
  252. {
  253. string gridStorageKey = GetGridStorageKey(GetReadOnlyIslandPrefabByName(island));
  254. if (gridStorageKey != null && gridStorageKey.Length > 0)
  255. {
  256. Storage storage = Storage.Get(StorageLifecycle.Game);
  257. storage.Root[gridStorageKey] = grid;
  258. }
  259. }
  260. private Dictionary<string, object> JsonToGrid(JSONObject dataObj)
  261. {
  262. Dictionary<string, object> dictionary = new Dictionary<string, object>();
  263. foreach (KeyValuePair<string, JSONValue> item in dataObj)
  264. {
  265. if (item.Value.Type != 0)
  266. {
  267. UnityEngine.Debug.LogError("Grid data should only have string values! But found:" + item.Value.Type.ToString());
  268. }
  269. else
  270. {
  271. dictionary.Add(item.Key, item.Value.Str);
  272. }
  273. }
  274. return dictionary;
  275. }
  276. private JSONValue StorableToJson(object data)
  277. {
  278. if (object.ReferenceEquals(data, null))
  279. {
  280. return new JSONValue((JSONObject)null);
  281. }
  282. if (data is Dictionary<string, object>)
  283. {
  284. Dictionary<string, object> dictionary = (Dictionary<string, object>)data;
  285. JSONObject jSONObject = new JSONObject();
  286. foreach (KeyValuePair<string, object> item in dictionary)
  287. {
  288. jSONObject.Add(item.Key, StorableToJson(item.Value));
  289. }
  290. return new JSONValue(jSONObject);
  291. }
  292. if (data is List<object>)
  293. {
  294. List<object> list = (List<object>)data;
  295. JSONArray jSONArray = new JSONArray();
  296. foreach (object item2 in list)
  297. {
  298. jSONArray.Add(StorableToJson(item2));
  299. }
  300. return new JSONValue(jSONArray);
  301. }
  302. if (data is object[])
  303. {
  304. object[] array = (object[])data;
  305. JSONArray jSONArray2 = new JSONArray();
  306. object[] array2 = array;
  307. foreach (object data2 in array2)
  308. {
  309. jSONArray2.Add(StorableToJson(data2));
  310. }
  311. return new JSONValue(jSONArray2);
  312. }
  313. if (data is string)
  314. {
  315. return new JSONValue((string)data);
  316. }
  317. if (data is bool)
  318. {
  319. return new JSONValue((bool)data);
  320. }
  321. if (data is char)
  322. {
  323. return new JSONValue(((char)data).ToString());
  324. }
  325. if (data is float)
  326. {
  327. return new JSONValue(Convert.ToDouble((float)data));
  328. }
  329. if (data is double)
  330. {
  331. return new JSONValue((double)data);
  332. }
  333. if (data is sbyte)
  334. {
  335. return new JSONValue(((sbyte)data).ToString());
  336. }
  337. if (data is byte)
  338. {
  339. return new JSONValue(((byte)data).ToString());
  340. }
  341. if (data is short)
  342. {
  343. return new JSONValue(Convert.ToDouble((short)data));
  344. }
  345. if (data is ushort)
  346. {
  347. return new JSONValue(Convert.ToDouble((ushort)data));
  348. }
  349. if (data is int)
  350. {
  351. return new JSONValue(Convert.ToDouble((int)data));
  352. }
  353. if (data is uint)
  354. {
  355. return new JSONValue(Convert.ToDouble((uint)data));
  356. }
  357. if (data is long)
  358. {
  359. return new JSONValue(Convert.ToDouble((long)data));
  360. }
  361. if (data is ulong)
  362. {
  363. return new JSONValue(Convert.ToDouble((ulong)data));
  364. }
  365. if (data is decimal)
  366. {
  367. return new JSONValue(Convert.ToDouble((decimal)data));
  368. }
  369. if (data is IStorable)
  370. {
  371. return StorableToJson(((IStorable)data).ToStorage());
  372. }
  373. return new JSONValue((JSONObject)null);
  374. }
  375. private CloudRequest<ApiError> LoginIslandServer()
  376. {
  377. return new CloudRequest<ApiError>(CoLoginIslandServer());
  378. }
  379. private IEnumerator CoLoginIslandServer()
  380. {
  381. if (_services.IsRegisteredInstall())
  382. {
  383. CloudRequest<IAccessToken, PlayernameErrors?> request = _services.GetVisitingIslandsAccessToken();
  384. yield return request;
  385. if (request.Error.HasValue)
  386. {
  387. yield return new YieldError<ApiError>(new ApiError
  388. {
  389. Error = request.Error.ToString(),
  390. ErrorCode = (int)request.Error.Value,
  391. Status = 0,
  392. ServerError = false,
  393. NoInternet = false
  394. });
  395. }
  396. else
  397. {
  398. _accessToken = request.Result;
  399. }
  400. }
  401. }
  402. private CloudRequest<ApiError> SendIslandData(Island island, JSONValue data)
  403. {
  404. return new CloudRequest<ApiError>(CoSendIslandData(island, data));
  405. }
  406. private IEnumerator CoSendIslandData(Island island, JSONValue data)
  407. {
  408. if (!HasIslandChanged(island, data))
  409. {
  410. yield break;
  411. }
  412. if (!IsLoggedIn)
  413. {
  414. CloudRequest<ApiError> login = LoginIslandServer();
  415. yield return login;
  416. if (login.Error != null)
  417. {
  418. UnityEngine.Debug.LogWarning("[Login] Could not send island data. " + login.Error.Error);
  419. yield return new YieldError<ApiError>(login.Error);
  420. yield break;
  421. }
  422. }
  423. string url = string.Format(_islandsUrl, "https://sparkling-society-islands.herokuapp.com", _services.GetInstallUuid(), island.GetSceneName(), _accessToken.Token);
  424. ApiRequest request = ApiRequest.Post(url, data);
  425. yield return request;
  426. if (request.Error != null)
  427. {
  428. UnityEngine.Debug.LogError("Unable to send island data. Server message:" + request.Error.Error);
  429. yield return new YieldError<ApiError>(request.Error);
  430. }
  431. else
  432. {
  433. UnityEngine.Debug.Log("Island: " + island.GetSceneName() + " data send to server!");
  434. LastSentIslandData[island.GetSceneName()] = data.ToString();
  435. }
  436. }
  437. private CloudRequest<ApiError> LoadIslandData(Island island, string installUuid)
  438. {
  439. return new CloudRequest<ApiError>(CoLoadIslandData(island, installUuid));
  440. }
  441. private IEnumerator CoLoadIslandData(Island island, string installUuid)
  442. {
  443. if (!IsLoggedIn)
  444. {
  445. CloudRequest<ApiError> login = LoginIslandServer();
  446. yield return login;
  447. if (login.Error != null)
  448. {
  449. UnityEngine.Debug.LogError("[Login] Could not load island data. " + login.Error.Error);
  450. yield return new YieldError<ApiError>(login.Error);
  451. yield break;
  452. }
  453. }
  454. string url = string.Format(_islandsUrl, "https://sparkling-society-islands.herokuapp.com", installUuid, island.GetSceneName(), _accessToken.Token);
  455. ApiRequest request = ApiRequest.Get(url);
  456. yield return request;
  457. if (request.Error != null)
  458. {
  459. if (request.Error.Status == 404)
  460. {
  461. UnityEngine.Debug.LogWarning("Trying to get data from a player who has never sent us gamestate! (http 404)");
  462. IslandCache[installUuid + island.GetSceneName()] = new VisitingIsland(island, installUuid, new JSONObject());
  463. FireDataDownloadedEventEvent(island, installUuid);
  464. }
  465. UnityEngine.Debug.LogError("[Island Data] Could not load island data. " + request.Error.Error);
  466. yield return new YieldError<ApiError>(request.Error);
  467. yield break;
  468. }
  469. JSONObject data = new JSONObject();
  470. if (request.Result.Data.Type != JSONValueType.Object || !request.Result.Data.Obj.ContainsKey("data"))
  471. {
  472. UnityEngine.Debug.LogWarning("Get Island data expected 'data' field. But the server didn't provide it.");
  473. }
  474. else
  475. {
  476. JSONValue jSONValue = request.Result.Data.Obj["data"];
  477. if (jSONValue.Type != JSONValueType.Object)
  478. {
  479. UnityEngine.Debug.LogError("Island data expected to be json Object found: " + jSONValue.Type.ToString());
  480. }
  481. else
  482. {
  483. data = jSONValue.Obj;
  484. }
  485. }
  486. IslandCache[installUuid + island.GetSceneName()] = new VisitingIsland(island, installUuid, data);
  487. FireDataDownloadedEventEvent(island, installUuid);
  488. }
  489. }