public GameState(GameMode mode, Level level, Difficulty difficulty) : this() { Mode = mode; Level = level; Difficulty = difficulty; ChartChecksum = SecuredOperations.CalculateChartChecksum(Level, Difficulty); }
public Promise <Profile> AuthenticateWithJwtToken() { IsAuthenticating = true; var jwtToken = Context.Player.Settings.LoginToken; if (jwtToken == null) { throw new ArgumentException(); } Debug.Log($"JWT token: {jwtToken}"); return(new Promise <Profile>((resolve, reject) => { RestClient.Get <Session>(new RequestHelper { Uri = Context.ApiUrl + $"/session?captcha={SecuredOperations.GetCaptcha()}", Headers = new Dictionary <string, string> { { "Authorization", "JWT " + jwtToken } }, EnableDebug = true }).Then(session => { Context.Player.Settings.PlayerId = session.user.Uid; Context.Player.Settings.LoginToken = session.token; Context.Player.SaveSettings(); return FetchProfile(); } ).Then(profile => { if (profile == null) { reject(new RequestException("Profile not found", true, false, 404, null)); return; } IsAuthenticated = true; OnAuthenticated.Invoke(); resolve(profile); }).CatchRequestError(result => { IsAuthenticated = false; Debug.LogWarning("Sign in failed."); Debug.LogWarning(result); if (!result.IsNetworkError) { Context.Player.Settings.LoginToken = null; Context.Player.SaveSettings(); } reject(result); }).Finally(() => IsAuthenticating = false); })); }
public GameState(Game game, GameMode mode, HashSet <Mod> mods) { Level = game.Level; Difficulty = game.Difficulty; DifficultyLevel = Level.Meta.GetDifficultyLevel(Difficulty.Id); Mode = mode; Mods = new HashSet <Mod>(mods); ChartChecksum = SecuredOperations.CalculateChartChecksum(Level, Difficulty); NoteCount = game.Chart.Model.note_list.Count; game.Chart.Model.note_list.ForEach(it => Judgements[it.id] = new NoteJudgement()); noteScoreMultiplierFactor = Math.Sqrt(NoteCount) / 3.0; UseHealthSystem = Mods.Contains(Mod.Hard) || Mods.Contains(Mod.ExHard) || mode == GameMode.Tier; MaxHealth = DifficultyLevel * 75; if (MaxHealth <= 0) { MaxHealth = 1000; } Health = MaxHealth; switch (mode) { case GameMode.Tier: { Context.TierState.Stages[Context.TierState.CurrentStageIndex] = this; // Keep allowed mods only Mods.IntersectWith(AllowedTierMods); if (Application.isEditor && game.EditorForceAutoMod) { Mods.Add(Mod.Auto); } // Use max health from meta MaxHealth = Context.TierState.Tier.Meta.maxHealth; Health = MaxHealth; break; } case GameMode.Calibration: // Remove auto mods Mods.ExceptWith(DisallowedCalibrationMods); if (Application.isEditor && game.EditorForceAutoMod) { Mods.Add(Mod.Auto); } break; } }
public Promise <Profile> Authenticate(string password) { IsAuthenticating = true; return(new Promise <Profile>((resolve, reject) => { RestClient.Post <Session>(new RequestHelper { Uri = Context.ApiUrl + "/session", EnableDebug = true, BodyString = SecuredOperations.WithCaptcha(new { username = Context.Player.Id, password, }).ToString() }).Then(session => { Context.Player.Settings.PlayerId = session.user.Uid; Context.Player.Settings.LoginToken = session.token; Context.Player.SaveSettings(); return FetchProfile(); } ).Then(profile => { if (profile == null) { reject(new RequestException("Profile not found", true, false, 404, null)); return; } IsAuthenticated = true; OnAuthenticated.Invoke(); resolve(profile); }).CatchRequestError(result => { IsAuthenticated = false; Debug.LogWarning("Sign in failed."); Debug.LogWarning(result); if (result.IsHttpError) { Context.Player.Settings.LoginToken = null; Context.Player.SaveSettings(); } reject(result); }).Finally(() => IsAuthenticating = false); })); }
public GameState() { IsCompleted = true; Mods = new HashSet <Mod>(); Score = 1000000; Accuracy = 1.000000; MaxCombo = 1; gradeCounts = new Dictionary <NoteGrade, int> { { NoteGrade.Perfect, 1 }, { NoteGrade.Great, 0 }, { NoteGrade.Good, 0 }, { NoteGrade.Bad, 0 }, { NoteGrade.Miss, 0 } }; Level = MockData.CommunityLevel; Difficulty = Difficulty.Parse(Level.Meta.charts[0].type); ChartChecksum = SecuredOperations.CalculateChartChecksum(Level, Difficulty); }
public async UniTask SignUp() { if (signUpIdInput.text.IsNullOrEmptyTrimmed()) { Toast.Next(Toast.Status.Failure, "TOAST_ENTER_ID".Get()); return; } if (signUpPasswordInput.text.IsNullOrEmptyTrimmed()) { Toast.Next(Toast.Status.Failure, "TOAST_ENTER_PASSWORD".Get()); return; } if (signUpEmailInput.text.IsNullOrEmptyTrimmed()) { Toast.Next(Toast.Status.Failure, "TOAST_ENTER_EMAIL_ADDRESS".Get()); return; } var id = signUpIdInput.text = signUpIdInput.text.ToLower(CultureInfo.InvariantCulture).Trim(); var password = signUpPasswordInput.text; var email = signUpEmailInput.text; if (!Regex.IsMatch(id, "^[a-z0-9-_]{3,16}$")) { Toast.Next(Toast.Status.Failure, "TOAST_INCORRECT_ID_FORMAT".Get()); return; } if (password.Length < 9) { Toast.Next(Toast.Status.Failure, "TOAST_INCORRECT_PASSWORD_FORMAT".Get()); return; } if (!IsValidEmail(email)) { Toast.Next(Toast.Status.Failure, "TOAST_INVALID_EMAIL_ADDRESS".Get()); return; } if (!await TermsOverlay.Show("TERMS_OF_SERVICE".Get())) { return; } var registered = false; var failed = false; RestClient.Put(new RequestHelper { Uri = Context.ApiUrl + $"/session?captcha={SecuredOperations.GetCaptcha()}", Headers = Context.OnlinePlayer.GetRequestHeaders(), EnableDebug = true, Body = new SignUpBody { uid = id, password = password, email = email } }) .Then(_ => { registered = true; }) .CatchRequestError(error => { failed = true; Debug.LogWarning("Sign up failed."); Debug.LogWarning(error); var errorResponse = JsonConvert.DeserializeObject <SignUpErrorResponse>(error.Response); Toast.Next(Toast.Status.Failure, errorResponse.message); }); closeButton.Leave(); signInButtonParent.Leave(); await UniTask.WaitUntil(() => registered || failed); if (failed) { closeButton.Enter(); signInButtonParent.Enter(); return; } var completed = false; Authenticate(id, password, true).Finally(() => { completed = true; signUpIdInput.text = ""; signUpPasswordInput.text = ""; signUpEmailInput.text = ""; signInIdInput.text = id; if (!lastAuthenticateSucceeded) { signInPasswordInput.text = password; SwitchToSignIn(); } }); await UniTask.WaitUntil(() => completed); if (State != ScreenState.Active) { return; } closeButton.Enter(); signInButtonParent.Enter(); }
public void DownloadAndUnpackLevelDialog( Level level, bool allowAbort = true, Action onDownloadSucceeded = default, Action onDownloadAborted = default, Action onDownloadFailed = default, Action <Level> onUnpackSucceeded = default, Action onUnpackFailed = default, bool forceInternational = false ) { if (!Context.OnlinePlayer.IsAuthenticated) { Toast.Next(Toast.Status.Failure, "TOAST_SIGN_IN_REQUIRED".Get()); return; } if (onDownloadSucceeded == default) { onDownloadSucceeded = () => { } } ; if (onDownloadAborted == default) { onDownloadAborted = () => { } } ; if (onDownloadFailed == default) { onDownloadFailed = () => { } } ; if (onUnpackSucceeded == default) { onUnpackSucceeded = _ => { } } ; if (onUnpackFailed == default) { onUnpackFailed = () => { } } ; var dialog = Dialog.Instantiate(); dialog.Message = "DIALOG_DOWNLOADING".Get(); dialog.UseProgress = true; dialog.UsePositiveButton = false; dialog.UseNegativeButton = allowAbort; ulong downloadedSize; var totalSize = 0UL; var downloading = false; var aborted = false; var targetFile = $"{Application.temporaryCachePath}/Downloads/{level.Id}-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}.cytoidlevel"; var destFolder = $"{level.Type.GetDataPath()}/{level.Id}"; try { Directory.CreateDirectory(destFolder); } catch (Exception error) { Debug.LogError("Failed to create level folder."); Debug.LogError(error); onDownloadFailed(); return; } if (level.IsLocal) { // Write to the local folder instead destFolder = level.Path; } // Download detail first, then package RequestHelper req; var downloadHandler = new DownloadHandlerFile(targetFile) { removeFileOnAbort = true }; RestClient.Get <OnlineLevel>(req = new RequestHelper { Uri = $"{(forceInternational ? CdnRegion.International.GetApiUrl() : Context.ApiUrl)}/levels/{level.Id}", Headers = Context.OnlinePlayer.GetRequestHeaders(), EnableDebug = true }).Then(it => { if (aborted) { throw new OperationCanceledException(); } totalSize = (ulong)it.Size; downloading = true; var packagePath = (forceInternational ? CdnRegion.International : Context.CdnRegion).GetPackageUrl(level.Id); Debug.Log($"Package path: {packagePath}"); // Get resources return(RestClient.Post <OnlineLevelResources>(req = new RequestHelper { Uri = packagePath, Headers = Context.OnlinePlayer.GetRequestHeaders(), BodyString = SecuredOperations.WithCaptcha(new { }).ToString(), EnableDebug = true })); }).Then(res => { if (aborted) { throw new OperationCanceledException(); } Debug.Log($"Asset path: {res.package}"); // Start download // TODO: Change to HttpClient return(RestClient.Get(req = new RequestHelper { Uri = res.package, DownloadHandler = downloadHandler, WillParseBody = false })); }).Then(async res => { downloading = false; try { onDownloadSucceeded(); } catch (Exception e) { Debug.LogError(e); } dialog.OnNegativeButtonClicked = it => { }; dialog.UseNegativeButton = false; dialog.Progress = 0; dialog.Message = "DIALOG_UNPACKING".Get(); DOTween.To(() => dialog.Progress, value => dialog.Progress = value, 1f, 1f).SetEase(Ease.OutCubic); var success = await Context.LevelManager.UnpackLevelPackage(targetFile, destFolder); if (success) { try { level = (await Context.LevelManager.LoadFromMetadataFiles(level.Type, new List <string> { destFolder + "/level.json" }, true)) .First(); onUnpackSucceeded(level); } catch (Exception e) { Debug.LogError(e); onUnpackFailed(); } } else { try { onUnpackFailed(); } catch (Exception e) { Debug.LogError(e); } } dialog.Close(); File.Delete(targetFile); }).Catch(error => { if (aborted || error is OperationCanceledException || (req != null && req.IsAborted)) { try { onDownloadAborted(); } catch (Exception e) { Debug.LogError(e); } } else { if (!forceInternational && error is RequestException requestException && requestException.StatusCode < 400 && requestException.StatusCode >= 500) { DownloadAndUnpackLevelDialog( level, allowAbort, onDownloadSucceeded, onDownloadAborted, onDownloadFailed, onUnpackSucceeded, onUnpackFailed, true ); } else { Debug.LogError(error); try { onDownloadFailed(); } catch (Exception e) { Debug.LogError(e); } } } dialog.Close(); });
public void UploadRecord() { rankingsTab.spinner.IsSpinning = true; var uploadTierRecord = SecuredOperations.MakeTierRecord(tierState); SecuredOperations.UploadTierRecord(tierState, uploadTierRecord) .Then(stateChange => { Toast.Next(Toast.Status.Success, "TOAST_TIER_CLEARED".Get()); EnterControls(); rankingsTab.UpdateTierRankings(tierState.Tier.Id); Context.OnlinePlayer.FetchProfile(); if (stateChange.rewards != null && stateChange.rewards.Any(it => it.Type == OnlinePlayerStateChange.Reward.RewardType.Level || it.Type == OnlinePlayerStateChange.Reward.RewardType.Character)) { RewardOverlay.Show(stateChange.rewards); if (stateChange.rewards.Any( it => it.Type == OnlinePlayerStateChange.Reward.RewardType.Level)) { Context.Library.Fetch(); } } } ).CatchRequestError(error => { Debug.LogWarning(error.Response); if (error.IsNetworkError) { Toast.Next(Toast.Status.Failure, "TOAST_CHECK_NETWORK_CONNECTION".Get()); } else if (error.IsHttpError) { if (error.StatusCode == 404) { Toast.Next(Toast.Status.Failure, "TOAST_TIER_NOT_FOUND".Get()); } else if (error.StatusCode == 400) { Toast.Next(Toast.Status.Failure, "TOAST_TIER_VERIFICATION_FAILED".Get()); } else if (error.StatusCode == 500) { Toast.Next(Toast.Status.Failure, "TOAST_SERVER_INTERNAL_ERROR".Get()); } else { Toast.Next(Toast.Status.Failure, $"Status code: {error.StatusCode}".Get()); } } Context.Haptic(HapticTypes.Failure, true); var dialog = Dialog.Instantiate(); dialog.Message = "DIALOG_RETRY_SYNCHRONIZE_TIER_PERFORMANCE".Get(); dialog.UseProgress = false; dialog.UsePositiveButton = true; dialog.UseNegativeButton = true; dialog.OnPositiveButtonClicked = _ => { dialog.Close(); UploadRecord(); }; dialog.OnNegativeButtonClicked = _ => { dialog.Close(); EnterControls(); Context.OnlinePlayer.FetchProfile(); }; dialog.Open(); }); }
public void UploadRecord() { var usedAuto = gameState.Mods.Contains(Mod.Auto) || gameState.Mods.Contains(Mod.AutoDrag) || gameState.Mods.Contains(Mod.AutoHold) || gameState.Mods.Contains(Mod.AutoFlick); if (!Application.isEditor && usedAuto) { throw new Exception(); } rankingsTab.spinner.IsSpinning = true; var uploadRecord = SecuredOperations.MakeRecord(gameState); SecuredOperations.UploadRecord(gameState, uploadRecord) .Then(stateChange => { uploadRecordSuccess = true; Toast.Next(Toast.Status.Success, "TOAST_PERFORMANCE_SYNCHRONIZED".Get()); EnterControls(); rankingsTab.UpdateRankings(gameState.Level.Id, gameState.Difficulty.Id); Context.OnlinePlayer.FetchProfile(); if (stateChange.rewards != null && stateChange.rewards.Any(it => it.Type == OnlinePlayerStateChange.Reward.RewardType.Level || it.Type == OnlinePlayerStateChange.Reward.RewardType.Character)) { RewardOverlay.Show(stateChange.rewards); if (stateChange.rewards.Any( it => it.Type == OnlinePlayerStateChange.Reward.RewardType.Level)) { Context.Library.Fetch(); } } } ).CatchRequestError(error => { Debug.LogWarning(error.Response); if (error.IsNetworkError) { Toast.Next(Toast.Status.Failure, "TOAST_CHECK_NETWORK_CONNECTION".Get()); } else if (error.IsHttpError) { if (error.StatusCode == 404) { Toast.Next(Toast.Status.Failure, "TOAST_LEVEL_NOT_RANKED".Get()); } else if (error.StatusCode == 400) { Toast.Next(Toast.Status.Failure, "TOAST_LEVEL_VERIFICATION_FAILED".Get()); } else if (error.StatusCode == 500) { Toast.Next(Toast.Status.Failure, "TOAST_SERVER_INTERNAL_ERROR".Get()); } else { Toast.Next(Toast.Status.Failure, $"Status code: {error.StatusCode}".Get()); } } Context.Haptic(HapticTypes.Failure, true); var dialog = Dialog.Instantiate(); dialog.Message = "DIALOG_RETRY_SYNCHRONIZE_PERFORMANCE".Get(); dialog.UseProgress = false; dialog.UsePositiveButton = true; dialog.UseNegativeButton = true; dialog.OnPositiveButtonClicked = _ => { dialog.Close(); UploadRecord(); }; dialog.OnNegativeButtonClicked = _ => { dialog.Close(); EnterControls(); rankingsTab.UpdateRankings(gameState.Level.Id, gameState.Difficulty.Id); Context.OnlinePlayer.FetchProfile(); }; dialog.Open(); }); }