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ùng | Nó cho bạn |
|---|---|
IProjectVAccountPresenter | Theo 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 |
ProjectVAccountSnapshot | Những gì cần vẽ: trạng thái, yêu cầu xoá đang chờ, lỗi gần nhất |
ProjectVAccountDeletionChallenge | Một yêu cầu xoá đang chờ và thời điểm nó hết hạn |
ProjectVAccountError | Vì sao một command (lệnh) thất bại: Category, Code, RequestId, LocalizationKey |
ProjectVAccountCommandResult | Kế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.
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:
| Member | Kiểu | Dùng để |
|---|---|---|
Status | ProjectVAccountStatus | Cái gì đang chạy (bảng bên dưới) |
RequiresLoginNavigation | bool | Status là SignedOut: mở scene Login. Chỉ gửi một lần |
CanConfirmDeletion | bool | Có một yêu cầu xoá đang chờ: bật nút xác nhận cuối cùng |
PendingDeletion | ProjectVAccountDeletionChallenge? | Yêu cầu xoá đang chờ; ExpiresAt là lúc nó hết hiệu lực |
RemoteDeletionSucceeded | bool | Server đã xoá tài khoản. Báo cho người chơi biết tài khoản không còn |
Error | ProjectVAccountError? | Vì sao command gần nhất thất bại, hoặc null |
Với mỗi ProjectVAccountStatus:
| Status | Hiện gì |
|---|---|
Ready | Các lựa chọn tài khoản |
RequestingDeletion, ConfirmingDeletion, ShuttingDown | Một spinner; tắt các nút |
AwaitingDeletionConfirmation | Cảnh báo cuối cùng cùng nút xác nhận, cho tới PendingDeletion.ExpiresAt |
SignedOut | Không gì cả; mở Login |
Failed | Lỗ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ểu | Giá trị |
|---|---|---|
cancellationToken | CancellationToken | Huỷ 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:
IsSuccess | Snapshot.Status | Chuyện gì đã xảy ra |
|---|---|---|
true | SignedOut | Người chơi đã đăng xuất. Mở Login (listener đã nhận được rồi) |
false | Failed | Đă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)
| Command | Khi thành công | Tiếp theo |
|---|---|---|
RequestDeletionAsync | AwaitingDeletionConfirmation; PendingDeletion.ExpiresAt có giá trị | Hiện cảnh báo cuối cùng |
ConfirmDeletionAsync | SignedOut với RemoteDeletionSucceeded là true | Bá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:
IsSuccess | RemoteDeletionSucceeded | Status | Chuyện gì đã xảy ra |
|---|---|---|---|
true | true | SignedOut | Đã xoá và đã đăng xuất. Mở Login |
false | true | SignedOut | Đã 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 |
false | false | Failed | Chư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ống | Error.Category / Code | Ngườ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 xong | Validation, PROJECTV_ACCOUNT_DELETE_CONFIRMATION_REQUIRED | Yê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ối | mã của server | Lỗi; bắt đầu xoá lại |
| Token của bạn bị huỷ | Cancelled, PROJECTV_ACCOUNT_CANCELLED | Không có gì; popup đang đóng |
| Mất kết nối hoặc hết thời gian chờ | Network, Timeout hoặc Unavailable | Thô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ào | Client, PROJECTV_ACCOUNT_BACKEND_FAILED | Mộ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ềReadyngay lập tức; hãy dùng listener hoặc kết quả command. - Coi
IsSuccesslà false sau khi xác nhận nghĩa là "chưa xoá". Kiểm traRemoteDeletionSucceededtrướ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
OnDisablehoặcOnDestroy.