using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Text; using SUISS.Cloud.Boomlagoon.JSON; using SUISS.Cloud.Crypto; using SUISS.Storage; using UnityEngine; namespace SUISS.Cloud { public class LeaderboardController : ILeaderboardController { public LeaderboardController(IPlayernameController playernames, string boardName, string countryCode, int leaderboardTopSize, int syncLeaderboardsEverySeconds, string sparkSocGameId, int buildVersion) { this._leaderboardTopSize = leaderboardTopSize; this._refreshCacheEvery = syncLeaderboardsEverySeconds; this._sparkSocGameId = sparkSocGameId; this._buildVersion = buildVersion; this._boardName = boardName; this._countryCode = countryCode; this._playernames = playernames; this._serverUrl = "https://leaderboards.sparklingsociety.net"; this._loginUrl = "https://usernames.sparklingsociety.net/login/leaderboards"; } [Obsolete("No longer needed! Leaderboard Controller now uses IPlayernames for all login features.")] //[DebuggerBrowsable(DebuggerBrowsableState.Never)] public event Action LoginLostEvent; [Obsolete("No longer needed! Leaderboard Controller now uses IPlayernames for all login features.")] private void FireLoginLostEvent() { if (this.LoginLostEvent != null) { this.LoginLostEvent(); } } private Dictionary storage { get { return SUISS.Storage.Storage.Get(StorageLifecycle.Game).GetDictionary("leaderboardController"); } } private LeaderboardController.Leaderboards GlobalBoardsCache { get { if (!this.storage.ContainsKey("leaderboardpages1") || !(this.storage["leaderboardpages1"] is LeaderboardController.Leaderboards)) { this.storage["leaderboardpages1"] = new LeaderboardController.Leaderboards(); } return (LeaderboardController.Leaderboards)this.storage["leaderboardpages1"]; } set { this.storage["leaderboardpages1"] = value; } } private int Page { get { if (this.storage.ContainsKey("LeaderboardPage1")) { return (int)this.storage["LeaderboardPage1"]; } return -1; } set { this.storage["LeaderboardPage1"] = value; } } private bool HasAccessToken { get { return this._accessToken != null && !this._accessToken.HasExpired && !string.IsNullOrEmpty(this._accessToken.Token); } } private int LeaderboardTopPages { get { return (this._leaderboardTopSize + this._pageSize - 1) / this._pageSize; } } [Obsolete("Currently not used.")] public DateTime VotesResetAt { get { if (this.storage.ContainsKey("VotesResetAt")) { return (DateTime)this.storage["VotesResetAt"]; } return DateTime.UtcNow; } private set { this.storage["LeaderboardPage1"] = value; } } [Obsolete("Currently not used.")] public int VotesLeft { get { if (this.storage.ContainsKey("VotesLeft")) { return (int)this.storage["VotesLeft"]; } return -1; } private set { this.storage["LeaderboardPage1"] = value; } } private LeaderboardController.Leaderboards CountryBoardsCache { get { if (!this.storage.ContainsKey("CountryBoardsCache1") || !(this.storage["CountryBoardsCache1"] is LeaderboardController.Leaderboards)) { this.storage["CountryBoardsCache1"] = new LeaderboardController.Leaderboards(); } return (LeaderboardController.Leaderboards)this.storage["CountryBoardsCache1"]; } set { this.storage["CountryBoardsCache1"] = value; } } private int CountryPage { get { if (this.storage.ContainsKey("CountryPage1")) { return (int)this.storage["CountryPage1"]; } return -1; } set { this.storage["CountryPage1"] = value; } } [Obsolete] private string InstallUuid { get { if (!this.storage.ContainsKey("installUuid")) { this.storage["installUuid"] = string.Empty; } return (string)this.storage["installUuid"]; } set { this.storage["installUuid"] = value; } } [Obsolete("No longer needed! Leaderboard Controller now uses IPlayernames, and shouldn't need Refreshtokens")] private string RefreshToken { get { if (!this.storage.ContainsKey("refreshToken2")) { this.storage["refreshToken2"] = string.Empty; } return (string)this.storage["refreshToken2"]; } set { this.storage["refreshToken2"] = value; } } [Obsolete] private string Scope { get { if (!this.storage.ContainsKey("oauthScope")) { this.storage["oauthScope"] = string.Empty; } return (string)this.storage["oauthScope"]; } set { this.storage["oauthScope"] = value; } } [Obsolete("No longer needed! Leaderboard Controller now uses IPlayernames for all login features.")] public bool IsLoggedIn { get { return !string.IsNullOrEmpty(this.RefreshToken); } } public bool InCountryTop { get { return this.CountryRank >= 0 && this.CountryRank <= this._leaderboardTopSize; } } public bool InGlobalTop { get { return this.GlobalRank >= 0 && this.GlobalRank <= this._leaderboardTopSize; } } public int GlobalRank { get { if (this.storage.ContainsKey("GobalRank")) { return (int)this.storage["GobalRank"]; } return -1; } private set { this.storage["GobalRank"] = value; } } public int CountryRank { get { if (this.storage.ContainsKey("CountryRank")) { return (int)this.storage["CountryRank"]; } return -1; } private set { this.storage["CountryRank"] = value; } } [Obsolete("No longer needed! Leaderboard Controller now uses IPlayernames to get access tokens.")] public CloudRequest Login(string username, string password, string installUuid) { return new CloudRequest(this.CoLogin(username, password, installUuid)); } [Obsolete("No longer needed! Leaderboard Controller now uses IPlayernames to get access tokens.")] public void LogOut() { this._accessToken = new AccessToken(); this.RefreshToken = string.Empty; this.InstallUuid = string.Empty; this.Scope = string.Empty; this.Page = -1; this.CountryPage = -1; } public Leaderboard GetGlobalLocal() { if (this._playernames.IsPlayerLoggedIn && !this.InGlobalTop) { return this.CollectAndCreateBoard(60, this.GlobalBoardsCache, this.GlobalLocalPages()); } return new Leaderboard(); } public Leaderboard GetCountryLocal() { if (this.HasAccessToken && !this.InCountryTop) { return this.CollectAndCreateBoard(60, this.CountryBoardsCache, this.CountyLocalPages()); } return new Leaderboard(); } public Leaderboard GetGlobalTop() { return this.CollectAndCreateBoard(this._leaderboardTopSize, this.GlobalBoardsCache, this.TopPages()); } public Leaderboard GetCountryTop() { return this.CollectAndCreateBoard(this._leaderboardTopSize, this.CountryBoardsCache, this.TopPages()); } public CustomYieldInstruction UpdateLeaderboards() { return CloudRequestRunner.StartCoroutine(this.CoUpdateLeaderboards()); } public void StartLeaderboardSync() { CloudRequestRunner.StartCoroutine(this.periodicUpdate()); } public CustomYieldInstruction UpdateScore(IScoreRecord record) { return CloudRequestRunner.StartCoroutine(this.CoUpdateLeaderboardScore(record)); } public string SignRecord(IScoreRecord record, string leaderboardSecret) { string message = string.Concat(new string[] { record.PrimaryScore.ToString(), record.SecondaryScore.ToString(), record.TertiaryScore.ToString(), record.QuaternaryScore.ToString(), record.CountryCode, record.GameUuid, record.Sagid, "2" }); return this.CreateToken(message, leaderboardSecret); } public CloudRequest Vote(string installUuid, bool upVote) { return new CloudRequest(this.CoVote(installUuid, upVote)); } public IEnumerator CoVote(string installUuid, bool upVote) { if (!this.HasAccessToken) { CloudRequest loginRequest = this.Login(); yield return loginRequest; if (loginRequest.Error != null) { yield return new YieldError(loginRequest.Error); } } string call = string.Format("{0}/leaderboards/vote/{1}?access_token={2}&voteUp={3}", new object[] { this._serverUrl, this._playernames.InstallUuid, this._accessToken.Token, (!upVote) ? "false" : "true" }); ApiRequest request = ApiRequest.Post(call, new JSONObject { { "installUuid", installUuid } }, this._buildVersion.ToString(), this._sparkSocGameId); yield return request; if (request.Result != null) { JSONValue data = request.Result.Data; UnityEngine.Debug.LogWarning("TODO: save votes left: " + data.ToString()); } else if (request.Error != null) { if (request.Error.Status == 0) { UnityEngine.Debug.Log("Failed to update leaderboard record. Message:" + request.Error.Error); yield return new YieldError(new LeaderboardErrors?(LeaderboardErrors.NoInternet)); } else { UnityEngine.Debug.LogError(string.Concat(new object[] { "Post record (", request.Error.Status, "): ", request.Error.Error })); yield return new YieldError(new LeaderboardErrors?(LeaderboardErrors.GeneralError)); } } yield break; } private CloudRequest Login() { return new CloudRequest(this.CoLogin()); } private IEnumerator CoLogin() { if (!this.HasAccessToken) { CloudRequest login = this._playernames.RequestPlayerAccessToken("leaderboards"); yield return login; if (login.Result != null) { this._accessToken = login.Result; if (!this.HasAccessToken) { UnityEngine.Debug.LogError("Leaderboards received invalid access token from _playernames."); yield return new YieldError(new LeaderboardErrors?(LeaderboardErrors.GeneralError)); } } else { PlayernameErrors value = login.Error.Value; if (value != PlayernameErrors.NoInternet) { yield return new YieldError(new LeaderboardErrors?(LeaderboardErrors.GeneralError)); } else { yield return new YieldError(new LeaderboardErrors?(LeaderboardErrors.NoInternet)); } } } yield break; } [Obsolete("No longer needed! Leaderboard Controller now uses IPlayernames for all login features.")] private IEnumerator CoLogin(string username, string password, string installUuid) { if (!this.HasAccessToken) { string scope = "read-write:" + installUuid; OAuthRequest request = OAuthRequest.LoginPassword(this._loginUrl, username, password, scope); yield return request; if (request.Result != null) { this._accessToken = request.Result.AccessToken; this.RefreshToken = request.Result.RefreshToken; this.InstallUuid = installUuid; this.Scope = request.Result.Scope; } else { this._accessToken = new AccessToken(); if (request.Error.NoInternet) { yield return new YieldError(new LeaderboardErrors?(LeaderboardErrors.NoInternet)); } else { this.RefreshToken = string.Empty; this.InstallUuid = string.Empty; this.Scope = string.Empty; yield return new YieldError(new LeaderboardErrors?(LeaderboardErrors.GeneralError)); } } } yield break; } private IEnumerator CoFetchLeaderboard(string boardName, int page) { string call = string.Format("{0}/leaderboards/{1}/{2}/{3}", new object[] { this._serverUrl, boardName, this._sparkSocGameId, page }); ApiRequest request = ApiRequest.Get(call, this._buildVersion.ToString(), this._sparkSocGameId); yield return request; if (request.Result != null) { JSONValue data = request.Result.Data; List list = this.parseRecords(data); if (list.Count > 0) { int rank = list[0].Rank; List list2 = new List(); foreach (ScoreRecord scoreRecord in list) { list2.Add(new LeaderboardEntry(scoreRecord.Sagid, scoreRecord.Username, scoreRecord.PrimaryScore, scoreRecord.SecondaryScore, scoreRecord.TertiaryScore, scoreRecord.QuaternaryScore, scoreRecord.Votes, scoreRecord.IslandReady)); } this.GlobalBoardsCache[page.ToString()] = new Leaderboard(this._pageSize, rank, list2); } else { this.GlobalBoardsCache[page.ToString()] = new Leaderboard(); } } else if (request.Error.NoInternet) { UnityEngine.Debug.Log("Failed to update leaderboards. (No internet) Message:" + request.Error.Error); } else { UnityEngine.Debug.LogError(string.Concat(new object[] { "Get leaderboard (", request.Error.Status, "): ", request.Error.Error })); } yield break; } private CustomYieldInstruction FetchLeaderboard(string boardName, int page) { return CloudRequestRunner.StartCoroutine(this.CoFetchLeaderboard(boardName, page)); } private IEnumerator CoFetchCountryLeaderboard(string boardName, int page, string countryCode) { string call = string.Format("{0}/leaderboards/{1}/{2}/{3}/{4}", new object[] { this._serverUrl, boardName, this._sparkSocGameId, countryCode, page }); ApiRequest request = ApiRequest.Get(call, this._buildVersion.ToString(), this._sparkSocGameId); yield return request; if (request.Result != null) { JSONValue data = request.Result.Data; List list = this.parseRecords(data); if (list.Count > 0) { int countryRank = list[0].CountryRank; List list2 = new List(); foreach (ScoreRecord scoreRecord in list) { list2.Add(new LeaderboardEntry(scoreRecord.Sagid, scoreRecord.Username, scoreRecord.PrimaryScore, scoreRecord.SecondaryScore, scoreRecord.TertiaryScore, scoreRecord.QuaternaryScore, scoreRecord.Votes, scoreRecord.IslandReady)); } this.CountryBoardsCache[page.ToString()] = new Leaderboard(this._pageSize, countryRank, list2); } else { this.CountryBoardsCache[page.ToString()] = new Leaderboard(); } } else if (request.Error.Status == 0) { UnityEngine.Debug.Log("Failed to update leaderboards. (No internet) Message:" + request.Error.Error); } else { UnityEngine.Debug.LogError(string.Concat(new object[] { "Get leaderboard (", request.Error.Status, "): ", request.Error.Error })); } yield break; } private CustomYieldInstruction FetchCountryLeaderboard(string boardName, int page, string countryCode) { return CloudRequestRunner.StartCoroutine(this.CoFetchCountryLeaderboard(boardName, page, countryCode)); } private IEnumerator CoUpdateLeaderboardScore(IScoreRecord record) { if (!this.HasAccessToken) { CloudRequest loginRequest = this.Login(); yield return loginRequest; if (loginRequest.Error != null) { UnityEngine.Debug.LogError("Failed to update leaderboards: " + loginRequest.Error); yield break; } } string call = string.Format("{0}/leaderboards/records?access_token={1}", this._serverUrl, this._accessToken.Token); JSONValue jsonValue = new JSONValue(new JSONObject { { "username", new JSONValue(record.Username) }, { "sauid", new JSONValue(record.Sauid) }, { "sagid", new JSONValue(record.Sagid) }, { "gameUuid", new JSONValue(record.GameUuid) }, { "countryCode", new JSONValue(record.CountryCode) }, { "signature", new JSONValue(record.Signature) }, { "data", new JSONValue(record.Data) }, { "primaryScore", new JSONValue((double)record.PrimaryScore) }, { "secondaryScore", new JSONValue((double)record.SecondaryScore) }, { "tertiaryScore", new JSONValue((double)record.TertiaryScore) }, { "quaternaryScore", new JSONValue((double)record.QuaternaryScore) }, { "isCheater", new JSONValue(record.IsCheater) }, { "islandReady", new JSONValue(record.IslandReady) } }); ApiRequest request = ApiRequest.Post(call, jsonValue, this._buildVersion.ToString(), this._sparkSocGameId); yield return request; if (request.Result != null) { JSONValue data = request.Result.Data; ScoreRecord scoreRecord = ScoreRecord.fromJson(data); this.Page = scoreRecord.RankPage; this.CountryPage = scoreRecord.CountryRankPage; this.GlobalRank = scoreRecord.Rank; this.CountryRank = scoreRecord.CountryRank; } else if (request.Error != null) { if (request.Error.Status == 0) { UnityEngine.Debug.Log("Failed to update leaderboard record. Message:" + request.Error.Error); } else { UnityEngine.Debug.LogError(string.Concat(new object[] { "Post record (", request.Error.Status, "): ", request.Error.Error })); } } yield break; } private Leaderboard CollectAndCreateBoard(int size, LeaderboardController.Leaderboards fromCache, HashSet pages) { List list = new List(); int num = int.MaxValue; foreach (int num2 in pages) { if (fromCache.ContainsKey(num2.ToString())) { num = Math.Min(num, fromCache[num2.ToString()].FirstRank); foreach (LeaderboardEntry item in fromCache[num2.ToString()].Records) { list.Add(item); } } } if (list.Count > size) { list = list.GetRange(0, size); } return new Leaderboard(size, num, list); } private string CreateToken(string message, string secret) { secret = (secret ?? string.Empty); ASCIIEncoding asciiencoding = new ASCIIEncoding(); byte[] bytes = asciiencoding.GetBytes(secret); byte[] bytes2 = asciiencoding.GetBytes(message); HmacSha256 hmacSha = new HmacSha256(bytes); byte[] inArray = hmacSha.ComputeHash(bytes2); return Convert.ToBase64String(inArray); } private List parseRecords(JSONValue json) { if (json.Type != JSONValueType.Array) { UnityEngine.Debug.LogError("Exprected leaderboards json to be an array. But got " + json.Type.ToString()); } JSONArray array = json.Array; List list = new List(); foreach (JSONValue json2 in array) { ScoreRecord item = ScoreRecord.fromJson(json2); list.Add(item); } return list; } private IEnumerator periodicUpdate() { for (; ; ) { HashSet globalPages = this.TopPages(); HashSet countryPages = this.TopPages(); globalPages.UnionWith(this.GlobalLocalPages()); countryPages.UnionWith(this.CountyLocalPages()); foreach (int page in globalPages) { this.FetchLeaderboard(this._boardName, page); } foreach (int page2 in globalPages) { this.FetchCountryLeaderboard(this._boardName, page2, this._countryCode); } yield return new WaitForSeconds((float)this._refreshCacheEvery); } yield break; } private IEnumerator CoUpdateLeaderboards() { HashSet globalPages = this.TopPages(); HashSet countryPages = this.TopPages(); globalPages.UnionWith(this.GlobalLocalPages()); countryPages.UnionWith(this.CountyLocalPages()); foreach (int p in globalPages) { yield return this.FetchLeaderboard(this._boardName, p); } foreach (int p2 in globalPages) { yield return this.FetchCountryLeaderboard(this._boardName, p2, this._countryCode); } yield break; } private HashSet TopPages() { int leaderboardTopPages = this.LeaderboardTopPages; HashSet hashSet = new HashSet(); for (int i = 1; i <= leaderboardTopPages; i++) { hashSet.Add(i); } return hashSet; } private HashSet CountyLocalPages() { HashSet hashSet = new HashSet(); if (this.CountryPage == -1 || this.InCountryTop) { return hashSet; } hashSet.Add(this.CountryPage - 1); hashSet.Add(this.CountryPage); hashSet.Add(this.CountryPage + 1); return hashSet; } private HashSet GlobalLocalPages() { HashSet hashSet = new HashSet(); if (this.Page == -1 || this.InGlobalTop) { return hashSet; } hashSet.Add(this.Page - 1); hashSet.Add(this.Page); hashSet.Add(this.Page + 1); return hashSet; } public const string StorageKey = "leaderboardController"; [Obsolete] public const string RefreshTokenKey = "refreshToken2"; [Obsolete] public const string InstallUuidKey = "installUuid"; [Obsolete] public const string AccessTokenKey = "accessToken"; [Obsolete] public const string ScopeKey = "oauthScope"; public const string LeaderboardsKey = "leaderboardpages1"; public const string LeaderboardPageKey = "LeaderboardPage1"; public const string GobalRankKey = "GobalRank"; public const string CountryRankKey = "CountryRank"; public const string CountryBoardsCacheKey = "CountryBoardsCache1"; public const string CountryPageKey = "CountryPage1"; public const string VotesResetAtKey = "VotesResetAt"; public const string VotesLeftKey = "VotesLeft"; private const string GetLeaderboardsCall = "{0}/leaderboards/{1}/{2}/{3}"; private const string GetCountryLeaderboardsCall = "{0}/leaderboards/{1}/{2}/{3}/{4}"; private const string StoreLeaderboardsCall = "{0}/leaderboards/records?access_token={1}"; private const string VoteCall = "{0}/leaderboards/vote/{1}?access_token={2}&voteUp={3}"; private string _serverUrl; private string _loginUrl; private IPlayernameController _playernames; private string _sparkSocGameId = "a5a5a4f8-2d69-4cab-ab38-3d98b346e4ba"; private int _buildVersion; private int _pageSize = 20; private int _leaderboardTopSize = 100; private int _refreshCacheEvery; private string _boardName = "boards"; private string _countryCode = "xx"; private IAccessToken _accessToken = new AccessToken(); private class Leaderboards : StorableDictionary { } } }