Chuyển tới nội dung chính

Account

Các hành động của popup tài khoản: đăng xuất, và xoá tài khoản qua hai bước (gửi yêu cầu, rồi xác nhận). Sau bất kỳ hành động nào trong hai hành động đó, SDK đã đăng xuất người chơi và xoá dữ liệu của tài khoản; trang của bạn chỉ cần mở scene Login.

Bạn dùngNó cho bạn
IProjectVAccountPresenterTheo dõi trạng thái tài khoản, đăng xuất, yêu cầu và xác nhận xoá tài khoản
ProjectVAccountSnapshotNhững gì cần vẽ: trạng thái, yêu cầu xoá đang chờ, lỗi gần nhất
ProjectVAccountDeletionChallengeMột yêu cầu xoá đang chờ và thời điểm nó hết hạn
ProjectVAccountErrorVì sao một command (lệnh) thất bại: Category, Code, RequestId, LocalizationKey
ProjectVAccountCommandResultKết quả của một command

Account không có screen contract; popup dùng trực tiếp presenter.

Bắt đầu nhanh​

using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using SilverTiger.ProjectV.Unity.Presentation.Account;
using UnityEngine;
using VContainer;

public sealed class AccountPopup : MonoBehaviour
{
private IProjectVAccountPresenter account = null!;
private IDisposable? subscription;
private CancellationTokenSource? lifetime;

[Inject]
public void Construct(IProjectVAccountPresenter presenter) => account = presenter;

private async void OnEnable()
{
lifetime = new CancellationTokenSource();
var token = lifetime.Token;
var accountSub = await account.SubscribeAsync(OnAccountChanged);
if (token.IsCancellationRequested)
{
// The popup closed while subscribing.
accountSub.Dispose();
return;
}
subscription = accountSub;
}

private void OnDisable()
{
subscription?.Dispose();
subscription = null;
lifetime?.Cancel();
lifetime?.Dispose();
lifetime = null;
}

// May be called off the Unity main thread.
private void OnAccountChanged(ProjectVAccountSnapshot snapshot) => RenderAsync(snapshot).Forget();

private async UniTaskVoid RenderAsync(ProjectVAccountSnapshot snapshot)
{
await UniTask.SwitchToMainThread();
if (this == null)
{
return;
}

// Sent once, right after logout or deletion.
if (snapshot.RequiresLoginNavigation)
{
if (snapshot.RemoteDeletionSucceeded)
{
ShowAccountDeletedNotice();
}
OpenLogin();
return;
}

SetButtonsInteractable(!IsBusy(snapshot.Status));
ShowConfirmStep(snapshot.CanConfirmDeletion, snapshot.PendingDeletion?.ExpiresAt);
ShowError(snapshot.Error?.LocalizationKey);
}

public async void OnLogoutClicked()
{
await account.LogoutAsync(lifetime!.Token);
}

private static bool IsBusy(ProjectVAccountStatus status) =>
status == ProjectVAccountStatus.RequestingDeletion ||
status == ProjectVAccountStatus.ConfirmingDeletion ||
status == ProjectVAccountStatus.ShuttingDown;

private void OpenLogin() { }
private void ShowAccountDeletedNotice() { }
private void SetButtonsInteractable(bool interactable) { }
private void ShowConfirmStep(bool visible, DateTimeOffset? expiresAt) { }
private void ShowError(string? localizationKey) { }
}

Đó là toàn bộ popup: đăng ký nhận (subscribe), vẽ, gọi command. Listener mở Login; các command không cần xử lý gì riêng ngoài luồng xoá tài khoản.

Main thread

Listener của SubscribeAsync có thể chạy ngoài main thread của Unity. Hãy chuyển về main thread trước khi đụng tới object Unity, như RenderAsync làm.

Cần vẽ gì​

Listener nhận một ProjectVAccountSnapshot:

MemberKiểuDùng để
StatusProjectVAccountStatusCái gì đang chạy (bảng bên dưới)
RequiresLoginNavigationboolStatus là SignedOut: mở scene Login. Chỉ gửi một lần
CanConfirmDeletionboolCó một yêu cầu xoá đang chờ: bật nút xác nhận cuối cùng
PendingDeletionProjectVAccountDeletionChallenge?Yêu cầu xoá đang chờ; ExpiresAt là lúc nó hết hiệu lực
RemoteDeletionSucceededboolServer đã xoá tài khoản. Báo cho người chơi biết tài khoản không còn
ErrorProjectVAccountError?Vì sao command gần nhất thất bại, hoặc null

Với mỗi ProjectVAccountStatus:

StatusHiện gì
ReadyCác lựa chọn tài khoản
RequestingDeletion, ConfirmingDeletion, ShuttingDownMột spinner; tắt các nút
AwaitingDeletionConfirmationCảnh báo cuối cùng cùng nút xác nhận, cho tới PendingDeletion.ExpiresAt
SignedOutKhông gì cả; mở Login
FailedLỗi; hiện lại các lựa chọn tài khoản

SignedOut chỉ được phát một lần, sau đó account.Current quay về Ready. Hãy phản ứng với nó trong listener hoặc trong kết quả command; đừng bao giờ poll Current để chờ nó.

Đăng xuất​

Task<ProjectVAccountCommandResult> LogoutAsync(CancellationToken cancellationToken)
Tham sốKiểuGiá trị
cancellationTokenCancellationTokenHuỷ request, ví dụ khi popup đóng

Đăng xuất người chơi trên thiết bị này. Tài khoản và tiến trình của nó được giữ lại.

Trả về một ProjectVAccountCommandResult:

IsSuccessSnapshot.StatusChuyện gì đã xảy ra
trueSignedOutNgười chơi đã đăng xuất. Mở Login (listener đã nhận được rồi)
falseFailedĐăng xuất thất bại; hiện Error và ở lại trang

Xoá tài khoản​

Xoá tài khoản cần hai lời gọi. Lời gọi đầu tiên không xoá gì cả.

Task<ProjectVAccountCommandResult> RequestDeletionAsync(CancellationToken cancellationToken)
Task<ProjectVAccountCommandResult> ConfirmDeletionAsync(CancellationToken cancellationToken)
CommandKhi thành côngTiếp theo
RequestDeletionAsyncAwaitingDeletionConfirmation; PendingDeletion.ExpiresAt có giá trịHiện cảnh báo cuối cùng
ConfirmDeletionAsyncSignedOut với RemoteDeletionSucceeded là trueBáo người chơi tài khoản đã bị xoá và mở Login

Chỉ gọi ConfirmDeletionAsync sau khi RequestDeletionAsync đã xong và trong khi CanConfirmDeletion là true.

private async UniTask DeleteAsync(string typedConfirmation, CancellationToken token)
{
// The typed phrase is game UI; the SDK does not require one.
if (typedConfirmation != "DELETE")
{
ShowInline("Type DELETE in capital letters to confirm.");
return;
}

var request = await account.RequestDeletionAsync(token);
if (!request.Snapshot.CanConfirmDeletion)
{
ShowError(request.Snapshot.Error?.LocalizationKey);
return;
}

// Normally the player confirms a final warning here.
var confirm = await account.ConfirmDeletionAsync(token);
if (confirm.Snapshot.RemoteDeletionSucceeded)
{
// The account is gone, even if IsSuccess is false.
OpenLogin();
return;
}

ShowError(confirm.Snapshot.Error?.LocalizationKey);
}

Trả về của ConfirmDeletionAsync:

IsSuccessRemoteDeletionSucceededStatusChuyện gì đã xảy ra
truetrueSignedOutĐã xoá và đã đăng xuất. Mở Login
falsetrueSignedOutĐã xoá, nhưng sau đó đăng xuất trên thiết bị này thất bại (Error có giá trị). Vẫn mở Login
falsefalseFailedChưa xoá. Yêu cầu xoá đang chờ đã bị huỷ; hãy gửi yêu cầu lại

Khi thất bại​

Mọi command thất bại đều huỷ yêu cầu xoá đang chờ, nên người chơi phải gửi yêu cầu xoá lại. Hiện thông báo ứng với Error.LocalizationKey (projectv.account.<category>):

Tình huốngError.Category / CodeNgười chơi thấy gì
Xác nhận khi không có yêu cầu xoá đang chờ, hoặc trước khi yêu cầu xongValidation, PROJECTV_ACCOUNT_DELETE_CONFIRMATION_REQUIREDYêu cầu người chơi bắt đầu xoá lại
Yêu cầu xoá đã hết hạn hoặc bị từ chốimã của serverLỗi; bắt đầu xoá lại
Token của bạn bị huỷCancelled, PROJECTV_ACCOUNT_CANCELLEDKhông có gì; popup đang đóng
Mất kết nối hoặc hết thời gian chờNetwork, Timeout hoặc UnavailableThông báo mất kết nối; cho người chơi thử lại
Bất cứ lỗi bất ngờ nàoClient, PROJECTV_ACCOUNT_BACKEND_FAILEDMột lỗi chung

Đưa Error.RequestId, khi có, vào panel hỗ trợ.

Quy tắc​

  • Mỗi lúc chỉ chạy một command. Bấm lần hai trong khi một command đang chạy sẽ chờ nó xong rồi cũng chạy; không được gộp lại. Tắt các nút khi đang bận.
  • Mở Login khi có RequiresLoginNavigation, sau khi đăng xuất hoặc xoá tài khoản. SDK đã xoá phiên và toàn bộ dữ liệu tài khoản.
  • Một Guest đăng nhập lại sau khi xoá tài khoản sẽ nhận một tài khoản mới.

Lỗi thường gặp​

  • Poll Current để chờ SignedOut. Nó quay về Ready ngay lập tức; hãy dùng listener hoặc kết quả command.
  • Coi IsSuccess là false sau khi xác nhận nghĩa là "chưa xoá". Kiểm tra RemoteDeletionSucceeded trước.
  • Xác nhận bằng một yêu cầu cũ. Sau mọi lỗi, hãy gọi lại RequestDeletionAsync.
  • Tự xoá PlayerPrefs hoặc dữ liệu đăng nhập đã lưu. SDK sẽ làm việc đó.
  • Đụng tới object Unity trong listener mà không chuyển thread.
  • Không dispose (giải phóng) subscription. Dispose nó trong OnDisable hoặc OnDestroy.