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ùng | Nó cho bạn |
|---|---|
IProjectVAuthenticationPresenter | Theo dõi trạng thái đăng nhập, đăng nhập, đăng ký tài khoản, đăng xuất |
ProjectVAuthenticationSnapshot | Những gì cần vẽ: trạng thái, người chơi đã đăng nhập, lỗi gần nhất |
ProjectVAuthenticationError | Vì sao một command (lệnh) thất bại: Category, Code, RequestId, LocalizationKey |
ProjectVAuthenticationCommandResult | Kết quả của một command |
IProjectVGuestIdentityProvider | Guest ID của thiết bị này |
ProjectVNativeAuthenticationAvailabilityView | Có được hiện nút Google hoặc Apple hay không |
ProjectVPasswordRegistrationInput | Nhữ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.
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:
| Member | Kiểu | Dùng để |
|---|---|---|
Status | ProjectVAuthenticationStatus | SignedOut, Restoring, Authenticating, Authenticated, Failed |
IsBusy | bool | Restoring hoặc Authenticating: hiện spinner và tắt các nút đăng nhập |
IsAuthenticated | bool | Đã có người chơi đăng nhập |
UserId, Username | string? | Người chơi đã đăng nhập; null trừ khi IsAuthenticated |
ExpiresAt | DateTimeOffset? | Thời điểm phiên đăng nhập hiện tại hết hạn, chỉ để hiển thị |
Error | ProjectVAuthenticationError? | 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ểu | Giá trị |
|---|---|---|
id | string | ID từ GetOrCreateAsync, để người chơi giữ nguyên tài khoản Guest |
options | ProjectVNativeAuthenticationOptions? | null. Username và Create của nó chưa được dùng |
cancellationToken | CancellationToken | Huỷ 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ểu | Giá trị |
|---|---|---|
username, password | string | Nhữ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 |
input | ProjectVPasswordRegistrationInput | new ProjectVPasswordRegistrationInput(username, password, email, displayName); email và displayName có thể là null |
Trả về
| Command | IsSuccess | Snapshot | Cần làm gì |
|---|---|---|---|
LoginPasswordAsync | true | Authenticated | Gọi StartAsync(true, token) trên Startup |
RegisterPasswordAsync | true | SignedOut | Tà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ả hai | false | Failed kèm Error | Hiệ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ĩa | Nút |
|---|---|---|
Available | Provider hoạt động ở đây | Hiện |
UnsupportedPlatform | Không có trên platform này: Google ngoài Android, iOS và Editor; Apple ngoài iOS và macOS | Ẩn |
NotConfigured | Bả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 |
ProviderUnavailable | Plugin 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ó:
IsSuccess | Snapshot | Ý nghĩa |
|---|---|---|
true | Authenticated | Phiên đăng nhập đã lưu hoạt động |
true | SignedOut | Không có phiên đăng nhập đã lưu. Hiện các lựa chọn đăng nhập |
false | SignedOut kèm Error | Phiên đăng nhập đã lưu bị từ chối. Hiện các lựa chọn đăng nhập |
false | Failed kèm Error | Khô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ống | Error.Category / Code | Người chơi thấy gì |
|---|---|---|
| Username trống hoặc mật khẩu rỗng | Validation, PROJECTV_AUTH_INVALID_CREDENTIALS | Yêu cầu người chơi điền cả hai ô; chưa gọi server |
| Form đăng ký trống | Validation, PROJECTV_AUTH_INVALID_REGISTRATION | Như trên, trên form đăng ký |
| Guest ID trống | Validation, PROJECTV_AUTH_PROVIDER_TOKEN_REQUIRED | Một lỗi; hãy dùng GetOrCreateAsync |
| Sai username hoặc mật khẩu | Authentication, mã của server | Thông báo sai username hoặc mật khẩu của bạn |
| Username đã có người dùng | mã của server | Lý do của server, trên form đăng ký |
| Người chơi đóng hộp thoại Google hoặc Apple | Cancelled, PROJECTV_AUTH_CANCELLED | Không có gì; giữ các nút ở trạng thái bật |
| Token của bạn bị huỷ | Cancelled, PROJECTV_AUTH_CANCELLED | Không có gì; scene đang đóng |
| Google hoặc Apple không có ở đây | Unavailable, PROJECTV_AUTH_PROVIDER_UNAVAILABLE | Ẩn nút (kiểm tra availability trước) |
| Hộp thoại của provider bị lỗi | Client, PROJECTV_AUTH_PROVIDER_FAILED | Mộ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 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_AUTH_BACKEND_FAILED | Mộ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 khiReady. - 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.
RegisterPasswordAsynckết thúc ởSignedOut; tiếp theo hãy gọiLoginPasswordAsync. - Coi
IsSuccesslà đã đăng nhập. Kiểm traSnapshot.IsAuthenticated. - Truyền
ProjectVNativeAuthenticationOptionsđể tạo tài khoản.UsernamevàCreatecủa nó chưa được dùng; hãy truyềnnull. - 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.