예제 #1
0
 public GameState(GameMode mode, Level level, Difficulty difficulty) : this()
 {
     Mode          = mode;
     Level         = level;
     Difficulty    = difficulty;
     ChartChecksum = SecuredOperations.CalculateChartChecksum(Level, Difficulty);
 }
예제 #2
0
    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);
        }));
    }
예제 #3
0
    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;
        }
    }
예제 #4
0
    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);
        }));
    }
예제 #5
0
 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);
 }
예제 #6
0
    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();
    }
예제 #7
0
    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();
        });
예제 #8
0
    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();
        });
    }
예제 #9
0
    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();
        });
    }