using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using SUISS.Cloud.Boomlagoon.JSON; using SUISS.Storage; using UnityEngine; namespace SUISS.Cloud { public class PlayernameController : IPlayernameController { public PlayernameController(string sparkSocGameId, int buildVersion, string deviceVendorId) { this._sparkSocGameId = sparkSocGameId; this._buildVersion = buildVersion; this._deviceVendorId = deviceVendorId; this._serverUrl = "https://usernames.sparklingsociety.net"; this._loginUrl = "https://usernames.sparklingsociety.net/login/{0}"; } //[DebuggerBrowsable(DebuggerBrowsableState.Never)] public event Action LogoutEvent; protected virtual void FireLogoutEvent() { if (this.LogoutEvent != null) { this.LogoutEvent(); } } public bool IsLinkedToPlayerName { get { return !string.IsNullOrEmpty(this.PlayerUuid) && !string.IsNullOrEmpty(this.PlayerName); } } public bool IsPlayerLoggedIn { get { return !string.IsNullOrEmpty(this.RefreshToken); } } public bool IsInstallRegistered { get { return !string.IsNullOrEmpty(this.InstallSecret) && !string.IsNullOrEmpty(this.InstallUuid); } } public string InstallUuid { get { if (!this.storage.ContainsKey("installUuid")) { this.storage["installUuid"] = string.Empty; } return (string)this.storage["installUuid"]; } private set { this.storage["installUuid"] = value; } } public string InstallSecret { get { if (!this.storage.ContainsKey("installSecret")) { this.storage["installSecret"] = string.Empty; } return (string)this.storage["installSecret"]; } private set { this.storage["installSecret"] = value; } } public string PlayerUuid { get { if (!this.storage.ContainsKey("playerUuid")) { this.storage["playerUuid"] = string.Empty; } return (string)this.storage["playerUuid"]; } private set { this.storage["playerUuid"] = value; } } public string PlayerName { get { if (!this.storage.ContainsKey("playerName")) { this.storage["playerName"] = string.Empty; } return (string)this.storage["playerName"]; } private set { this.storage["playerName"] = value; } } private bool HasPlayerAccessToken { get { return this._playerAccessTokens.ContainsKey("accounts") && this._playerAccessTokens["accounts"] != null && !this._playerAccessTokens["accounts"].HasExpired; } } private bool HasInstallAccessToken { get { return this._installAccessToken != null && !this._installAccessToken.HasExpired; } } private string DeviceVendorId { get { return this._deviceVendorId; } } private Dictionary storage { get { return SUISS.Storage.Storage.Get(StorageLifecycle.Forever).GetDictionary("playernameController"); } } private string RefreshToken { get { if (!this.storage.ContainsKey("refreshToken")) { this.storage["refreshToken"] = string.Empty; } return (string)this.storage["refreshToken"]; } set { this.storage["refreshToken"] = value; } } public bool IsValidPlayername(string playername) { return !string.IsNullOrEmpty(playername) && playername.Length > 2 && playername.Length <= 25 && playername.IndexOfAny("{}()[]".ToCharArray()) == -1; } public bool IsValidPassword(string password) { return !string.IsNullOrEmpty(password) && password.Length > 4; } public void LogOutPlayer() { foreach (AccessToken accessToken in this._playerAccessTokens.Values) { accessToken.Invalidate(); } this.RefreshToken = string.Empty; this.FireLogoutEvent(); } public void ClearInstall() { this.LogOutPlayer(); this.InstallUuid = string.Empty; this.InstallSecret = string.Empty; this.PlayerUuid = string.Empty; } public CloudRequest RequestPlayerAccessToken(string resourceName) { return new CloudRequest(this.CoRequestPlayerAccessToken(resourceName)); } public CloudRequest RequestInstallAccessToken(string resourceName) { return new CloudRequest(this.CoRequestInstallAccessToken(resourceName)); } public CloudRequest RegisterInstall() { if (this._registeringLock == null) { this._registeringLock = new CloudRequest(this.CoRegisterInstall()); } return this._registeringLock; } public CloudRequest CreatePlayerName(string username, string password) { return new CloudRequest(this.CoCreateAndLogin(username, password)); } public CloudRequest LinkToPlayerName(string username, string password) { return new CloudRequest(this.CoLoginAndLink(username, password)); } private CloudRequest LoginInstall(string installUuid, string installSecret) { return new CloudRequest(this.CoLoginInstall(installUuid, installSecret)); } private IEnumerator CoLoginInstall(string installUuid, string installSecret) { if (!this.HasInstallAccessToken) { string scope = "read-write"; OAuthRequest request = OAuthRequest.LoginPassword(string.Format(this._loginUrl, "accounts"), installUuid, installSecret, scope); yield return request; if (request.Result != null) { this._installAccessToken = request.Result.AccessToken; } else { this._installAccessToken = new AccessToken(); yield return new YieldError(request.Error); } } yield break; } private CloudRequest LoginPlayer(string playerRefreshToken) { return new CloudRequest(this.CoLoginPlayer(playerRefreshToken)); } private IEnumerator CoLoginPlayer(string playerRefreshToken) { string scope = "read-write"; OAuthRequest request = OAuthRequest.LoginRefreshToken(string.Format(this._loginUrl, "accounts"), playerRefreshToken, scope); yield return request; if (request.Result != null) { this.UpdateAccessTokenCache("accounts", request.Result.AccessToken); this.RefreshToken = request.Result.RefreshToken; } else { if (!request.Error.NoInternet && !request.Error.ServerError) { this.LogOutPlayer(); } yield return new YieldError(request.Error); } yield break; } private IEnumerator CoRequestPlayerAccessToken(string resourceName) { if (this.IsPlayerLoggedIn && this.IsInstallRegistered) { string scope = string.Format("read-write:{0}", this.InstallUuid); OAuthRequest request = OAuthRequest.LoginRefreshToken(string.Format(this._loginUrl, resourceName), this.RefreshToken, scope); yield return request; if (request.Result != null) { this.RefreshToken = request.Result.RefreshToken; this.UpdateAccessTokenCache(resourceName, request.Result.AccessToken); yield return new YieldResult(request.Result.AccessToken); } else if (request.Error != null) { if (request.Error.NoInternet) { yield return new YieldError(new PlayernameErrors?(PlayernameErrors.NoInternet)); } else if (request.Error.ServerError) { UnityEngine.Debug.LogError("Server error:" + request.Error.Error); yield return new YieldError(new PlayernameErrors?(PlayernameErrors.GeneralError)); } else { this.LogOutPlayer(); yield return new YieldError(new PlayernameErrors?(PlayernameErrors.LostValidRefreshToken)); } } else { this.LogOutPlayer(); yield return new YieldError(new PlayernameErrors?(PlayernameErrors.LostValidRefreshToken)); } } else { yield return new YieldError(new PlayernameErrors?(PlayernameErrors.LostValidRefreshToken)); } yield break; } private IEnumerator CoRequestInstallAccessToken(string resourceName) { if (this.IsInstallRegistered) { string scope = "read-write"; OAuthRequest request = OAuthRequest.LoginPassword(string.Format(this._loginUrl, resourceName), this.InstallUuid, this.InstallSecret, scope); yield return request; if (request.Result != null) { yield return new YieldResult(request.Result.AccessToken); } else if (request.Error.NoInternet) { yield return new YieldError(new PlayernameErrors?(PlayernameErrors.NoInternet)); } else if (request.Error.ServerError) { UnityEngine.Debug.LogError("Server error:" + request.Error.Error); yield return new YieldError(new PlayernameErrors?(PlayernameErrors.GeneralError)); } else { UnityEngine.Debug.LogWarning("Playernames lost Refresh token! Message:" + request.Error.Error); this.LogOutPlayer(); yield return new YieldError(new PlayernameErrors?(PlayernameErrors.LostValidRefreshToken)); } } else { yield return new YieldError(new PlayernameErrors?(PlayernameErrors.LostValidRefreshToken)); } yield break; } private IEnumerator CoLoginAndLink(string username, string password) { if (!this.IsInstallRegistered) { CloudRequest reg = this.RegisterInstall(); yield return reg; if (reg.Error != null) { yield return new YieldError(reg.Error); } } CloudRequest log = this.LoginPlayer(username, password); yield return log; if (log.Error != null) { yield return this.CheckErrors(log.Error); } if (!this.IsLinkedToPlayerName || username != this.PlayerName) { if (username != this.PlayerName) { UnityEngine.Debug.LogWarning("Game already linked to other player! Relinking to new player name."); } CloudRequest link = this.LinkInstallToPlayerName(this._playerAccessTokens["accounts"], this.InstallUuid, this.InstallSecret); yield return link; if (link.Error != null) { yield return this.CheckErrors(link.Error); } } yield break; } private IEnumerator CoCreateAndLogin(string username, string password) { if (!this.IsInstallRegistered) { CloudRequest reg = this.RegisterInstall(); yield return reg; if (reg.Error != null) { yield return new YieldError(reg.Error); } } if (!this.HasInstallAccessToken) { CloudRequest log = this.LoginInstall(this.InstallUuid, this.InstallSecret); yield return log; if (log.Error != null) { yield return this.CheckErrors(log.Error); } } CloudRequest create = this.CreatePlayer(this._installAccessToken, username, password); yield return create; if (create.Error != null) { yield return this.CheckErrors(create.Error); } CloudRequest loginPlayer = this.LoginPlayer(username, password); yield return loginPlayer; if (loginPlayer.Error != null) { yield return this.CheckErrors(loginPlayer.Error); } yield break; } private YieldError CheckErrors(ApiError error) { if (error.NoInternet) { return new YieldError(new PlayernameErrors?(PlayernameErrors.NoInternet)); } if (error.ServerError) { UnityEngine.Debug.LogError("Server error:" + error.Error); return new YieldError(new PlayernameErrors?(PlayernameErrors.GeneralError)); } PlayernameErrors errorCode = (PlayernameErrors)error.ErrorCode; if (errorCode != PlayernameErrors.DuplicatePlayername && errorCode != PlayernameErrors.ProfanePlayername && errorCode != PlayernameErrors.PlayernameNotFound && errorCode != PlayernameErrors.IncorrectPassword) { UnityEngine.Debug.LogError(string.Concat(new object[] { "Server returned unexpected error code (", error.ErrorCode, "):", error.Error })); return new YieldError(new PlayernameErrors?(PlayernameErrors.GeneralError)); } return new YieldError(new PlayernameErrors?((PlayernameErrors)error.ErrorCode)); } private CloudRequest LoginPlayer(string username, string password) { return new CloudRequest(this.CoLoginPlayer(username, password)); } private IEnumerator CoLoginPlayer(string username, string password) { if (this.IsPlayerLoggedIn) { this.LogOutPlayer(); } string scope = "read-write"; OAuthRequest request = OAuthRequest.LoginPassword(string.Format(this._loginUrl, "accounts"), username, password, scope); yield return request; if (request.Result != null) { this.UpdateAccessTokenCache("accounts", request.Result.AccessToken); this.RefreshToken = request.Result.RefreshToken; } else { yield return new YieldError(request.Error); } yield break; } private IEnumerator CoRegisterInstall() { if (this.IsInstallRegistered) { UnityEngine.Debug.LogWarning("This install is allready registered. Doing nothing!"); yield break; } string url = string.Format("{0}/installs", this._serverUrl); JSONValue json = this.jsonInstall(); ApiRequest request = ApiRequest.Post(url, json, this._buildVersion.ToString(), this._sparkSocGameId); yield return request; if (request.Result != null) { JSONValue response = request.Result.Data; if (response.Type == JSONValueType.Object) { JSONObject obj = response.Obj; this.InstallSecret = obj.GetString("secret"); this.InstallUuid = obj.GetString("sagId"); this._registeringLock = null; } else { UnityEngine.Debug.LogError(string.Concat(new string[] { url, " returned ", response.Type.ToString(), " but was expecting ", JSONValueType.Object.ToString() })); yield return new YieldError(new PlayernameErrors?(PlayernameErrors.GeneralError)); } } else { if (!request.Error.NoInternet) { UnityEngine.Debug.LogError("PlayernameController.CoRegisterInstall failed with: " + request.Error.Error); } else { UnityEngine.Debug.LogWarning(string.Format("PlayernameController.CoRegisterInstall failed with no internet: '{0}'", request.Error.Error)); } this._registeringLock = null; } yield break; } private CloudRequest LinkInstallToPlayerName(AccessToken playerAccessToken, string installUuid, string installSecret) { return new CloudRequest(this.CoLinkInstallToPlayerName(playerAccessToken, installUuid, installSecret)); } private IEnumerator CoLinkInstallToPlayerName(IAccessToken playerAccessToken, string installUuid, string installSecret) { string url = string.Format("{0}/installs/link/{1}?access_token={2}", this._serverUrl, installUuid, playerAccessToken.Token); JSONValue json = new JSONValue(new JSONObject { { "install_secret", new JSONValue(installSecret) } }); ApiRequest request = ApiRequest.Post(url, json, this._buildVersion.ToString(), this._sparkSocGameId); yield return request; if (request.Result != null) { JSONValue response = request.Result.Data; if (response.Type == JSONValueType.Object) { this.PlayerUuid = JSONParse.StringField(response.Obj, "accountUuid", string.Empty); this.PlayerName = JSONParse.StringField(response.Obj, "playerName", string.Empty); } else { this.PlayerUuid = string.Empty; yield return new YieldError(new ApiError { Status = 0, Error = string.Concat(new string[] { url, " returned ", response.Type.ToString(), " but was expecting ", JSONValueType.Object.ToString() }), ErrorCode = 0, ServerError = true }); } } else { this.PlayerUuid = string.Empty; yield return new YieldError(request.Error); } yield break; } private CloudRequest CreatePlayer(IAccessToken installAccessToken, string username, string password) { if (this._creatingPlayerLock == null) { this._creatingPlayerLock = new CloudRequest(this.CoCreatePlayerName(installAccessToken, username, password)); } return this._creatingPlayerLock; } private IEnumerator CoCreatePlayerName(IAccessToken installAccessToken, string username, string password) { string url = string.Format("{0}/accounts?access_token={1}", this._serverUrl, installAccessToken.Token); JSONValue json = this.jsonPlayer(username, password, string.Empty, false); ApiRequest request = ApiRequest.Post(url, json, this._buildVersion.ToString(), this._sparkSocGameId); yield return request; if (request.Result != null) { JSONValue response = request.Result.Data; PlayerNameData data = PlayerNameData.fromJson(response); if (data != null) { this.PlayerUuid = data.AccountUuid; this.PlayerName = data.Playername; this._creatingPlayerLock = null; } else { this._creatingPlayerLock = null; yield return new YieldError(new ApiError { Status = 0, Error = string.Concat(new string[] { url, " returned ", response.Type.ToString(), " but was expecting ", JSONValueType.Object.ToString() }), ErrorCode = 0, ServerError = true }); } } else { this._creatingPlayerLock = null; yield return new YieldError(request.Error); } yield break; } private JSONValue jsonInstall() { return new JSONValue(new JSONObject { { "gameId", new JSONValue(this._sparkSocGameId) }, { "deviceVendorId", new JSONValue(this.DeviceVendorId) }, { "deviceInfo", this.jsonDeviceInfo() } }); } private JSONValue jsonPlayer(string username, string password, string email, bool aweber) { return new JSONValue(new JSONObject { { "username", new JSONValue(username) }, { "email", new JSONValue(email) }, { "password", new JSONValue(password) }, { "aweberSubscription", new JSONValue(aweber) } }); } private JSONValue jsonDeviceInfo() { return new JSONValue(new JSONObject { { "devicePlatform", new JSONValue(SystemInfo.deviceModel) }, { "deviceHardware", new JSONValue(SystemInfo.deviceModel) }, { "deviceOs", new JSONValue(SystemInfo.operatingSystem) }, { "deviceType", new JSONValue(SystemInfo.deviceType.ToString()) } }); } private void UpdateAccessTokenCache(string resource, AccessToken newToken) { if (this._playerAccessTokens.ContainsKey(resource) && newToken.Token != this._playerAccessTokens[resource].Token) { this._playerAccessTokens[resource].Invalidate(); } this._playerAccessTokens[resource] = newToken; } public const string StorageKey = "playernameController"; public const string RefreshTokenKey = "refreshToken"; public const string AccessTokenKey = "accessToken"; public const string InstallUuidKey = "installUuid"; public const string PlayerUuidKey = "playerUuid"; public const string InstallSecretKey = "installSecret"; public const string PlayerNameKey = "playerName"; private const string InstallsCall = "{0}/installs"; private const string AccountsCall = "{0}/accounts?access_token={1}"; private const string LinkInstallCall = "{0}/installs/link/{1}?access_token={2}"; private const string AccountsResource = "accounts"; private string _sparkSocGameId; private int _buildVersion; private string _serverUrl; private string _loginUrl; private string _deviceVendorId; private AccessToken _installAccessToken; private Dictionary _playerAccessTokens = new Dictionary(); private CloudRequest _creatingPlayerLock; private CloudRequest _registeringLock; } }