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

Authentication

Các nút đăng nhập ở scene Login: Guest, username và mật khẩu, Google và Apple. SDK giữ người chơi ở trạng thái đăng nhập giữa các lần chơi; UI Login của bạn chỉ chọn cách đăng nhập, hiện tiến trình và hiện lý do khi đăng nhập thất bại.

Bạn dùngNó cho bạn
IProjectVAuthenticationPresenterTheo dõi trạng thái đăng nhập, đăng nhập, đăng ký tài khoản, đăng xuất
ProjectVAuthenticationSnapshotNhững gì cần vẽ: trạng thái, người chơi đã đăng nhập, lỗi gần nhất
ProjectVAuthenticationErrorVì sao một command (lệnh) thất bại: Category, Code, RequestId, LocalizationKey
ProjectVAuthenticationCommandResultKết quả của một command
IProjectVGuestIdentityProviderGuest ID của thiết bị này
ProjectVNativeAuthenticationAvailabilityViewCó được hiện nút Google hoặc Apple hay không
ProjectVPasswordRegistrationInputNhững gì người chơi nhập vào form đăng ký

Authentication không có screen contract. Sau mỗi lần đăng nhập thành công, hãy chuyển sang Startup presenter; nó tải dữ liệu của người chơi và báo cho bạn khi nào mở game.

Bắt đầu nhanh​

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

public sealed class LoginButtonsView : MonoBehaviour
{
private IProjectVAuthenticationPresenter authentication = null!;
private IProjectVGuestIdentityProvider guestIdentity = null!;
private IProjectVStartupPresenter startup = null!;
private IDisposable? subscription;
private CancellationTokenSource? lifetime;

[Inject]
public void Construct(
IProjectVAuthenticationPresenter authenticationPresenter,
IProjectVGuestIdentityProvider guestIdentityProvider,
IProjectVStartupPresenter startupPresenter)
{
authentication = authenticationPresenter;
guestIdentity = guestIdentityProvider;
startup = startupPresenter;
}

private async void OnEnable()
{
lifetime = new CancellationTokenSource();
var token = lifetime.Token;
var google = authentication.GetNativeAvailability(ProjectVNativeAuthenticationProvider.Google);
var apple = authentication.GetNativeAvailability(ProjectVNativeAuthenticationProvider.Apple);
ShowProviderButtons(google.IsAvailable, apple.IsAvailable);
var authenticationSub = await authentication.SubscribeAsync(OnAuthenticationChanged);
if (token.IsCancellationRequested)
{
// The page closed while subscribing.
authenticationSub.Dispose();
return;
}
subscription = authenticationSub;
}

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

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

private async UniTaskVoid RenderAsync(ProjectVAuthenticationSnapshot snapshot)
{
await UniTask.SwitchToMainThread();
if (this == null)
{
return;
}
SetButtonsInteractable(!snapshot.IsBusy);
ShowError(snapshot.Error?.LocalizationKey);
}

public async void OnGuestClicked()
{
var token = lifetime!.Token;
var id = await guestIdentity.GetOrCreateAsync(token);
var result = await authentication.AuthenticateDeviceAsync(id, null, token);
await ContinueAsync(result, token);
}

public async void OnGoogleClicked()
{
var token = lifetime!.Token;
var result = await authentication.SignInWithGoogleAsync(null, token);
await ContinueAsync(result, token);
}

private async UniTask ContinueAsync(ProjectVAuthenticationCommandResult result, CancellationToken token)
{
if (this == null || !result.Snapshot.IsAuthenticated)
{
return; // the listener already shows the error
}
// Startup loads the player's data; the startup listener opens the game on Ready.
await startup.StartAsync(contentReady: true, token);
}

private void ShowProviderButtons(bool google, bool apple) { }
private void SetButtonsInteractable(bool interactable) { }
private void ShowError(string? localizationKey) { }
}

Listener của Startup trong phần bắt đầu nhanh của Startup mở game khi nó báo Ready. Để gọn, ví dụ này bỏ qua OperationCanceledException khi scene đóng; hãy bắt nó trong các handler nút thật.

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 ProjectVAuthenticationSnapshot:

MemberKiểuDùng để
StatusProjectVAuthenticationStatusSignedOut, Restoring, Authenticating, Authenticated, Failed
IsBusyboolRestoring hoặc Authenticating: hiện spinner và tắt các nút đăng nhập
IsAuthenticatedboolĐã có người chơi đăng nhập
UserId, Usernamestring?Người chơi đã đăng nhập; null trừ khi IsAuthenticated
ExpiresAtDateTimeOffset?Thời điểm phiên đăng nhập hiện tại hết hạn, chỉ để hiển thị
ErrorProjectVAuthenticationError?Vì sao command gần nhất thất bại, hoặc null. Có thể có giá trị khi đang SignedOut

authentication.Current giữ cùng snapshot đó; ban đầu nó là SignedOut.

Mọi command đều trả về một ProjectVAuthenticationCommandResult kèm Snapshot lúc nó kết thúc. IsSuccess nghĩa là "không có lỗi"; nó không phải lúc nào cũng có nghĩa là đã đăng nhập, nên hãy kiểm tra Snapshot.IsAuthenticated.

Đăng nhập Guest​

Task<string> IProjectVGuestIdentityProvider.GetOrCreateAsync(CancellationToken cancellationToken)
Task<ProjectVAuthenticationCommandResult> AuthenticateDeviceAsync(string id, ProjectVNativeAuthenticationOptions? options, CancellationToken cancellationToken)
Tham sốKiểuGiá trị
idstringID từ GetOrCreateAsync, để người chơi giữ nguyên tài khoản Guest
optionsProjectVNativeAuthenticationOptions?null. Username và Create của nó chưa được dùng
cancellationTokenCancellationTokenHuỷ lần thử, ví dụ khi scene đóng

GetOrCreateAsync tạo một ID ngẫu nhiên một lần và lưu nó trên thiết bị này; hãy gọi nó trên main thread của Unity. Gỡ cài đặt game hoặc xoá dữ liệu của game sẽ mất ID, và mất luôn tài khoản Guest.

Trả về Snapshot.IsAuthenticated là true khi người chơi đã đăng nhập; nếu không, Snapshot.Error cho biết lý do (xem Khi thất bại).

Username và mật khẩu​

Task<ProjectVAuthenticationCommandResult> LoginPasswordAsync(string username, string password, CancellationToken cancellationToken)
Task<ProjectVAuthenticationCommandResult> RegisterPasswordAsync(ProjectVPasswordRegistrationInput input, CancellationToken cancellationToken)
Tham sốKiểuGiá trị
username, passwordstringNhững gì người chơi đã nhập. Username trống hoặc mật khẩu rỗng sẽ thất bại mà không liên lạc với server
inputProjectVPasswordRegistrationInputnew ProjectVPasswordRegistrationInput(username, password, email, displayName); email và displayName có thể là null

Trả về

CommandIsSuccessSnapshotCần làm gì
LoginPasswordAsynctrueAuthenticatedGọi StartAsync(true, token) trên Startup
RegisterPasswordAsynctrueSignedOutTài khoản đã được tạo, nhưng người chơi chưa đăng nhập. Gọi LoginPasswordAsync với cùng username và mật khẩu
cả haifalseFailed kèm ErrorHiện lỗi
var created = await authentication.RegisterPasswordAsync(
new ProjectVPasswordRegistrationInput(username, password, email: null, displayName: null),
token);
if (!created.IsSuccess)
{
ShowError(created.Snapshot.Error?.LocalizationKey);
return;
}

var signedIn = await authentication.LoginPasswordAsync(username, password, token);

Không bao giờ hiện, lưu hoặc ghi log mật khẩu.

Google và Apple​

ProjectVNativeAuthenticationAvailabilityView GetNativeAvailability(ProjectVNativeAuthenticationProvider provider)
Task<ProjectVAuthenticationCommandResult> SignInWithGoogleAsync(ProjectVNativeAuthenticationOptions? options, CancellationToken cancellationToken)
Task<ProjectVAuthenticationCommandResult> SignInWithAppleAsync(ProjectVNativeAuthenticationOptions? options, CancellationToken cancellationToken)

Chỉ hiện nút của một provider khi GetNativeAvailability(provider).IsAvailable là true. Quyết định theo giá trị này, đừng bao giờ chỉ dựa vào platform của Unity:

AvailabilityÝ nghĩaNút
AvailableProvider hoạt động ở đâyHiện
UnsupportedPlatformKhông có trên platform này: Google ngoài Android, iOS và Editor; Apple ngoài iOS và macOSẨn
NotConfiguredBản build thiếu cấu hình (Google: không có asset cấu hình hoặc Web client ID; trong Editor, còn có thể do đăng nhập trong Editor đang tắt)Ẩn, và sửa cấu hình project
ProviderUnavailablePlugin của provider bị thiếu hoặc hiện không dùng đượcẨn

Truyền null cho options. Mỗi lời gọi mở hộp thoại riêng của provider, rồi đăng nhập người chơi bằng tài khoản đã chọn.

Cấu hình Google. Đăng nhập Google là một phần của com.silvertiger.projectv. Nó cần một asset ProjectVGoogleOAuthConfiguration tại Assets/Resources/GoogleOAuthConfig.asset (tạo nó từ Assets › Create › SilverTiger › ProjectV SDK › Google OAuth). Đăng nhập trong Editor mặc định tắt; để bật cần một Web OAuth client với redirect URI http://localhost:49153/projectv-google/callback. Kiểm tra cấu hình bằng Tools › SilverTiger › ProjectV SDK › Google › Validate Configuration. Bản build Android phải được ký bằng keystore đã đăng ký cho Android OAuth client.

Cấu hình Apple. Đăng nhập Apple cần plugin Unity Sign in with Apple (AppleAuth) trong project; nếu không có, availability là ProviderUnavailable.

Khôi phục và đăng xuất​

Task<ProjectVAuthenticationCommandResult> RestoreAsync(CancellationToken cancellationToken)
Task<ProjectVAuthenticationCommandResult> LogoutAsync(CancellationToken cancellationToken)

Startup đã tự đăng nhập lại cho người chơi, nên scene Login thường không gọi RestoreAsync. Khi bạn có gọi nó:

IsSuccessSnapshotÝ nghĩa
trueAuthenticatedPhiên đăng nhập đã lưu hoạt động
trueSignedOutKhông có phiên đăng nhập đã lưu. Hiện các lựa chọn đăng nhập
falseSignedOut kèm ErrorPhiên đăng nhập đã lưu bị từ chối. Hiện các lựa chọn đăng nhập
falseFailed kèm ErrorKhông kiểm tra được, ví dụ do mất kết nối

LogoutAsync đăng xuất người chơi trên thiết bị này và kết thúc ở SignedOut. Trong các scene game, hãy dùng LogoutAsync của Account presenter; nó còn cho bạn biết khi nào mở Login.

Khi thất bại​

Hiện thông báo ứng với Error.LocalizationKey (projectv.authentication.<category>). Chỉ dùng Error.Code để rẽ nhánh cho một trường hợp cụ thể:

Tình huốngError.Category / CodeNgười chơi thấy gì
Username trống hoặc mật khẩu rỗngValidation, PROJECTV_AUTH_INVALID_CREDENTIALSYêu cầu người chơi điền cả hai ô; chưa gọi server
Form đăng ký trốngValidation, PROJECTV_AUTH_INVALID_REGISTRATIONNhư trên, trên form đăng ký
Guest ID trốngValidation, PROJECTV_AUTH_PROVIDER_TOKEN_REQUIREDMột lỗi; hãy dùng GetOrCreateAsync
Sai username hoặc mật khẩuAuthentication, mã của serverThông báo sai username hoặc mật khẩu của bạn
Username đã có người dùngmã của serverLý do của server, trên form đăng ký
Người chơi đóng hộp thoại Google hoặc AppleCancelled, PROJECTV_AUTH_CANCELLEDKhông có gì; giữ các nút ở trạng thái bật
Token của bạn bị huỷCancelled, PROJECTV_AUTH_CANCELLEDKhông có gì; scene đang đóng
Google hoặc Apple không có ở đâyUnavailable, PROJECTV_AUTH_PROVIDER_UNAVAILABLEẨn nút (kiểm tra availability trước)
Hộp thoại của provider bị lỗiClient, PROJECTV_AUTH_PROVIDER_FAILEDMột lỗi kèm lựa chọn thử lại
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_AUTH_BACKEND_FAILEDMột lỗi chung kèm lựa chọn thử lại

Đư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 IsBusy.
  • Sau mỗi lần đăng nhập thành công, gọi StartAsync(true, token) trên Startup và chỉ mở game khi Ready.
  • Không bao giờ lưu, ghi log hoặc hiện mật khẩu, token của provider hay OAuth secret.

Lỗi thường gặp​

  • Nghĩ rằng đăng ký sẽ đăng nhập luôn cho người chơi. RegisterPasswordAsync kết thúc ở SignedOut; tiếp theo hãy gọi LoginPasswordAsync.
  • Coi IsSuccess là đã đăng nhập. Kiểm tra Snapshot.IsAuthenticated.
  • Truyền ProjectVNativeAuthenticationOptions để tạo tài khoản. Username và Create của nó chưa được dùng; hãy truyền null.
  • Hiện nút Google hoặc Apple theo platform. Dùng GetNativeAvailability.
  • Tự tạo Guest ID. Dùng IProjectVGuestIdentityProvider, nếu không người chơi sẽ nhận một tài khoản Guest mới.
  • Đụng tới object Unity trong listener mà không chuyển thread.

Scene gate và UI binding​

IProjectVAuthenticatedSceneGate nhớ nơi cần quay lại sau khi đăng nhập: một scene cần người chơi đã đăng nhập sẽ kiểm tra IsReady, gọi RequestReturnTo(sceneName) và mở Login; sau khi đăng nhập, Login mở ConsumeReturnScene("Lobby"). Bạn tự load các scene; gate chỉ nhớ tên.

authentication.SubscribeUiAsync(ProjectVUiBindingId.LoginAuthentication, apply, token) hoạt động như SubscribeAsync và còn đánh dấu trong log của SDK khi apply đã trả về (xem UI subscriptions). Nó không chuyển thread; hãy tự chuyển về main thread.