入门指引
LEVEL INFINITE PASS 资源可以帮助业务屏蔽底层账号认证的实现细节,同时也包括繁琐的 UI 开发。业务可以使用 LEVEL INFINITE PASS 统一的登录能力快速并安全地上线。
根据本文中的步骤接入 LEVEL INFINITE PASS 后,游戏只需几行代码即可支持 LEVEL INFINITE PASS 和其他第三方身份供应商(本文中称为 "渠道")的身份验证功能。
前提条件
使用客户端应用完成客户端登录流程
步骤1:导入 LI PASS 插件
- Unity
- Unreal Engine
- SDK 1.24 及之后版本
- SDK 1.24 之前版本
Unity 2021.3.33f1 ~ Unity 2022
Unity 2018 ~ Unity 2022
目录 StreamingAssets/LevelInfinite
使用于 Player Network SDK V1.20.01 及以上版本,目录 StreamingAssets/INTLGameNative
使用于 Player Network SDK V1.20.00 及以下版本。
将
LevelInfinite
、Plugins
和StreamingAssets/LevelInfinite
文件夹复制到游戏项目的Assets
目录后,再使用 Unity 导入。文件夹 说明 LevelInfinite LI PASS 引擎层代码 Plugins LI PASS 平台层库文件 StreamingAssets/LevelInfinite LI PASS 业务逻辑资源,可热更
2. [可选] 从项目中移除程序集。
LI PASS 从 V1.08 版本开始支持程序集。如果项目不支持程序集,请执行以下步骤来移除:
- 删除以下程序集定义文件。
- 修改
luaSvr.cs
,将程序集名从LevelInfinite
换成Assembly-CSharp
。 - 联系 Player Network 助手获取不带程序集的
AssetBundle
包。
在 Unity 菜单栏,选择 INTLLua > All > Clear 删除
Assets/LevelInfinite/INTLGameNative/Scripts/LuaObject
目录。
如果删除成功,则自动弹窗提示生成 Unity 的 Lua 接口。如未弹窗,在 Unity 菜单栏选择 INTL > INTLLua > All > Make 来手动触发弹窗。在弹窗中,点击 Generate 生成 C# 导出到 Lua 的接口。
在
Assets/LevelInfinite/INTLGameNative/Scripts/LuaObject
目录中查看接口导出文件是否成功生成。注意存在 Custom 和 Unity 文件夹代表接口文件生成成功。
- Custom 文件夹:自定义类接口导出到 Lua。
- Unity 文件夹:Unity 引擎类接口导出到 Lua。
6. [可选] 切换加载方式。
加载方式默认为 Editor 模式,业务可选择在开发时将加载方式切换成 StreamingAssets 模式,或在发布时切换成 CDN 模式。
在发布时使用 StreamingAssets 模式将导致后续无法加载 Player Network 控制台资源,影响控制台的热更新功能。因此,需在出包前切换成 CDN 模式。
在菜单栏 LevelInfinite > AssetLoadMode 中选择对应的加载方式。
选项 | 说明 |
---|---|
Editor | 对应 INTL_DEBUG_LUA 宏,读取本地 Lua 文件 |
StreamingAssets | 对应 INTL_USE_LOCAL_ASSETS 宏,加载 StreamingAssets 中的 AssetBundle |
CDN | 对应 Player Network 控制台 AssetBundle ,如果控制台资源加载失败则加载 StreamingAssets 中的 AssetBundle |
7. [可选] 完成 Unity 主机游戏的项目配置。
安装并激活 Input System 包:
注意Input System 需要 Unity 2019.1 或更高版本与 .NET Framework 4 运行,不适用于运行 .NET Framework 3.5 的项目。
在 XSX 真机运行时需匹配 1.6.1 版本的 Input System,同时附带安装
com.unity.inputsystem.gxdk-1.0.1
的拓展包,如果遇到引入问题请联系 Player Network 助手。更多详情,请参见 Input System 安装指南.
从 Unity 的主菜单中,进入 Window > Package Manager 以打开 Unity Package Manager。
点击 Advanced,确认 Show Preview Packages 已启用。
从左侧的列表中,选择最新的 Input System 包后,点击 Install。
从 Unity 的主菜单中,进入 Edit > Project Settings后,点击 Player。
将 Active Input Handling 的值设置为
Input System Package (New)
。
添加宏定义:
从 Unity 的主菜单中,进入 Edit > Project Settings 后,点击 Player。
打开 Other Settings 面板,浏览至 Scripting Define Symbols。
将
INTL_CONSOLE_GAME_INPUT
添加到文本框中,使用分号分隔。
UE4.21 ~ UE4.27 & UE5 ~ UE5.4
将
LevelInfinite
文件夹拷贝至目标项目的Plugins
文件夹中。如果项目没有Plugins
文件夹,请创建一个。警告在 Cook 的时候如果报错
LongPackageNameToFilename failed to convert '/LevelInfinite'. Path does not map to any roots
,需要修改 Cook 的路径/LevelInfinite -> /LevelInfinite/
,一般常出现在低于 UE4.27 版本。注意在 LI PASS V1.16 之前的版本,LI PASS 插件必须要放到
Plugins/LevelInfinite
目录中。如果业务要自定义路径,要对应修改PLUGIN_LI_CONTENT_PATH
、LI_LUA_BASE_PATH
和FRAME_LUA_BASE_PATH
变量并重新编译。
在 LI PASS V1.16 或之后的版本,LI PASS 插件自动根据业务配置的自定义路径,动态加载资源文件。说明LevelInfiniteAssetVersion.lua
的文件拓展名为 LUA 而并非 LUAC,是因为里面包含了 LI PASS 的版本号,方便业务同学了解我们的 LI PASS 版本,便于定位问题。打开游戏模块的构建文件,即
{项目名}.Build.cs
文件,并按照示例将LevelInfinite
插件添加为模块的私有依赖:public INTLSample(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay" });
// Add the following code
PrivateDependencyModuleNames.AddRange(new string[] {
"INTLCore",
"INTLFoundation",
"INTLConfig",
"LevelInfinite"
});
}将 LI PASS Lua 资源打进首包,在 Packaging/Additional Non-Asset 选项中添加:
../Plugins/LevelInfinite/Content
注意业务需要根据实际存放
LevelInfinite
文件夹的路径进行填写,例如实际存放路径是../Plugins/Template/LevelInfinite
,则在此需要添加的是../Plugins/Template/LevelInfinite/Content
。
将 LI PASS uasset 资源打进首包,在 Packaging/Additional Asset 选项中添加:
/LevelInfinite
步骤2:配置 LI PASS
业务可选择使用编辑器的可视化配置编辑工具来进行 LI PASS 的配置,或直接打开工程目录修改 INTLConfig.ini 文件。以下步骤将介绍如何使用编辑器轻松配置 LI PASS。
有关各配置项的详情,可在编辑器选中选项来自动展示选项的简述,或参见 LEVEL INFINITE PASS 配置 查看完整的配置项列表。
在编辑器运行时生效需要重启引擎,否则构建安装包时则自动生效。
- Unity
- Unreal Engine
通过编辑器工具栏 INTL > LI INIConfig Window 打开可视化配置编辑工具。
通过编辑器工具栏 LITools > Config Editor 打开可视化配置编辑工具。
将
ACCOUNT_SDK_KEY
和ACCOUNT_APP_ID
设置为 Player Network 控制台分配的值。注意在 LI PASS V1.16 之前的版本,将
ACCOUNT_URL
设置为https://test-pass.intlgame.com
来定义 LI PASS 测试环境。
在 LI PASS V1.16 或之后的版本,此配置项为选填。为所需的平台配置指定的第三方登录渠道。
例如,ACCOUNT_THIRD_CHANNEL_ANDROID = Facebook,Google
。完整的配置列表,请参加 配置。
4. [可选] 在第三方渠道登录时获取玩家邮箱。
要在第三方渠道登录时获取玩家邮箱需先设置对应权限。
- 基于合规考虑,可针对特定来源对返回的
email
做 mask 处理,如有需求请联系 Player Network 助手打开。 - 可在后台流水日志中上报 hash 后的
base64(sha256(email))
,如有需求请联系 Player Network 助手打开。 - 可用于验证玩家信息或绑定列表是否包含
email
信息,如有需求请联系 Player Network 助手打开。 - 可用于 iOS Firebase 的 Private Set Membership(PSM)功能,详见 Firebase iOS 工程配置。
以下为支持返回玩家邮箱的第三方渠道:
Apple
Apple 获取玩家邮箱需要玩家授权,如果玩家拒绝授权将无法获取到玩家邮箱,详见 Login
接口传入 email
和 fullName
。
使用编辑工具,将
email
权限添加到 [Dynamic] 下的APPLE_LOGIN_PERMISSION
。在 Player Network 控制台 开启 email 返回功能,将 return_email 设置为 YES,详细步骤请参见 第三方渠道配置。
在 Player Network 控制台 开启 email 返回功能,将 return_email 设置为 YES,详细步骤请参见 第三方渠道配置。
使用编辑工具,将
email
权限添加到 [Dynamic] 下的FACEBOOK_LOGIN_PERMISSION
。如果INTLConfig.ini
没有配置FACEBOOK_LOGIN_PERMISSION
,则会自动带上email
权限。在 Meta for Developers 配置 email 权限。email 权限需要有 高级访问级别,才能让所有应用都能获取到玩家的 email。
在 Player Network 控制台开启 email 返回功能,将 return_email 设置为 YES,详细步骤请参见 第三方渠道配置。
在 Twitter Developer Platform 的 User authentication settings 下,勾选 Request email from users。
在 Player Network 控制台 开启 email 返回功能,将 return_email 设置为 YES,详细步骤请参见 第三方渠道配置。
步骤3:初始化 LI PASS
- Unity
- Unreal Engine
步骤4:处理 LI PASS 回调
无需处理 LI PASS 内部回调事件,否则可能会拦截 LI PASS 正常的回调流程,导致登录异常。
根据以下示例,处理打开登录面板 OpenLoginPanel
和自动登录 AutoLogin
等回调事件。
- Unity
- Unreal Engine
开发人员需添加 AuthResultObserver、AuthBaseResultObserver 和 LIEventObserver 回调来处理登录组件和用户中心的事件。回调数据结构为 INTLAuthResult,其中包含登录信息和账号信息,例如 OpenID、账号绑定信息和账号注销状态。
// Add callbacks
public void AddAuthBaseObserver()
{
INTLAPI.AddAuthBaseResultObserver(OnAuthBaseResultEvent);
}
// Remove callbacks
public void RemoveAuthBaseObserver()
{
INTLAPI.RemoveAuthBaseResultObserver(OnAuthBaseResultEvent);
}
// Add callbacks
public void AddAuthObserver()
{
INTLAPI.AddAuthResultObserver(OnAuthResultEvent);
}
// Remove callbacks
public void RemoveAuthObserver()
{
INTLAPI.RemoveAuthResultObserver(OnAuthResultEvent);
}
// Handle AuthResultObserver callback result
private void OnAuthResultEvent(INTLAuthResult AuthResult)
{
switch (AuthResult.MethodId)
{
// LI PASS third channel / LI channel enter game
case (int)INTLMethodID.LI_LOGIN_ENTER_GAME:
if(AuthResult.RetCode == (int)ERROR_CODE.SUCCESS) {
Debug.Log("LI PASS Login success");
} else {
Debug.Log("LI PASS Login failed, ret_code = " + AuthResult.RetCode + ", ret_msg = " + AuthResult.RetMsg);
LevelInfinite.OpenLoginPanel();
}
break;
// LI PASS autologin enter game
case (int)INTLMethodID.LI_AUTOLOGIN_ENTER_GAME:
if(AuthResult.RetCode == (int)ERROR_CODE.SUCCESS) {
Debug.Log("LI PASS Autologin success");
} else {
Debug.Log("LI PASS Autologin failed, ret_code = " + AuthResult.RetCode + ", ret_msg = " + AuthResult.RetMsg);
LevelInfinite.OpenLoginPanel();
}
break;
default:
break;
}
}
// Process the INTLBaseResult callback
public void OnAuthBaseResultEvent(INTLBaseResult ret)
{
Debug.Log($"MethodID: {ret.MethodId}");
string methodTag = "";
if (ret.MethodId == (int)INTLMethodID.INTL_AUTH_LOGOUT)
{
methodTag = "Logout";
}
ShowLogInNewLine(methodTag + Tools.Instance.GetRetString(ret));
}
// Add callbacks
LevelInfinite.AddLIEventObserver(OnLIBaseEventResult);
// Handle LIEventObserver callback result
private void OnLIBaseEventResult(LIBaseEventResult liRet)
{
Debug.Log("<color=#ff00ff>OnLIBaseEventResult. lIEventType : " + Enum.GetName(typeof(LIEventType), liRet.lIEventType) + ", extraJson:" + liRet.extraJson + "</color>");
ShowLogInNewLine("OnLIBaseEventResult. lIEventType : " + Enum.GetName(typeof(LIEventType), liRet.lIEventType) + ", extraJson:" + liRet.extraJson);
switch (liRet.lIEventType)
{
// LI PASS Init Finish
case LIEventType.GN_READY:
// LevelInfinite.AutoLogin()
// LevelInfinite.OpenLoginPanel()
break;
// LI PASS Panel Open
case LIEventType.LIP_PANEL_OPEN:
break;
// LI PASS Panel Close
case LIEventType.LIP_PANEL_CLOSE:
break;
// LI PASS Open Login Panel
case LIEventType.OPEN_LOGIN:
break;
// LI PASS Close Login Panel
case LIEventType.CLOSE_LOGIN:
break;
// LI PASS Open Account Center
case LIEventType.OPEN_ACCOUNT_CENTER:
break;
// LI PASS Close Account Center
case LIEventType.CLOSE_ACCOUNT_CENTER:
break;
// LI PASS Close Delete Account Panel
case LIEventType.CLOSE_DELETE_ACCOUNT:
break;
// LI PASS Close Parent Certificate Panel
case LIEventType.CLOSE_PARENT_CERTIFICATE:
break;
// LI PASS Close Parent Tip Panel
case LIEventType.CLOSE_PARENT_TIP:
break;
// LI PASS Close Parent Deny Panel
case LIEventType.CLOSE_PARENT_DENY:
break;
// LI PASS Init Repeat
case LIEventType.GN_REPEAT:
break;
// LI PASS Closed
case LIEventType.GN_DISABLED:
break;
default:
break;
}
}
// Remove callbacks
LevelInfinite.RemoveLIEventObserver(OnLIBaseEventResult);
开发人员也可添加 LILuaErrorObserver 回调处理 Lua 错误。
开发人员需添加 AuthResultObserver、AuthBaseResultObserver 和 LIEventObserver 回调来处理登录组件和用户中心的事件。回调数据结构为 FINTLAuthResult,其中包含登录信息和账号信息,例如 OpenID、账号绑定信息和账号注销状态。
//configure callback
FINTLAuthBaseEvent authBaseEvent;
authBaseEvent.AddUObject(this, &OnAuthBaseResult_Implementation);
UINTLSDKAPI::SetAuthBaseResultObserver(authBaseEvent);
// Remove callbacks
UINTLSDKAPI::GetAuthBaseResultObserver().Clear();
//Handle AuthBaseResult callback result
void OnAuthBaseResult_Implementation(FINTLAuthBaseResult ret)
{
UE_LOG(LogTemp, Warning, TEXT("MethodID: %d"), ret.MethodId);
}
// Add callbacks
AuthResultObserver = UINTLSDKAPI::GetAuthResultObserver().AddUObject(this, &ULevelInfiniteWindow::OnAuthResult_Callback);
// Handle AuthResultObserver callback result
void ULevelInfiniteWindow::OnAuthResult_Callback(FINTLAuthResult AuthResult)
{
if(AuthResult.MethodId == (int32)LIEnterGameMethodId::kLILoginEnterGame)
{
FString logStr;
if(AuthResult.RetCode == INTL_NAMESPACE::ErrorCode::SUCCESS)
{
logStr = TEXT("LI PASS login success");
}else{
ULevelInfiniteAPI::OpenLoginPanel();
logStr = FString::Printf(TEXT("LI PASS login failed, ret_code = %d, ret_msg = %s"), AuthResult.RetCode, *AuthResult.RetMsg);
}
UE_LOG(LogTemp, Warning, TEXT("%s"), *logStr);
} else if (AuthResult.MethodId == (int32)LIEnterGameMethodId::kLIAutoLoginEnterGame){
if(AuthResult.RetCode == INTL_NAMESPACE::ErrorCode::SUCCESS)
{
UE_LOG(LogTemp, Warning, TEXT("LI PASS Autologin success"));
}else{
ULevelInfiniteAPI::OpenLoginPanel();
UE_LOG(LogTemp, Warning, TEXT("LI PASS Autologin failed, ret_code = %d, ret_msg = %s"), AuthResult.RetCode, *AuthResult.RetMsg);
}
}
}
// Remove callbacks
UINTLSDKAPI::GetAuthResultObserver().Remove(AuthResultObserver);
// Add callbacks
FDelegateHandle LIEventObserver;
LIEventObserver = ULevelInfiniteAPI::GetEventDelegate().AddUObject(this, &ULevelInfiniteWindow::OnLIEvent_Callback);
// Handle LIEventObserver callback result
void ULevelInfiniteWindow::OnLIEvent_Callback(FLIBaseEvent Event)
{
FString logStr;
switch(Event.EventType){
// LI PASS Closed
case ELIEventType::GN_DISABLED:
{
break;
}
// LI PASS Panel Open
case ELIEventType::LIP_PANEL_OPEN:
// LI PASS Open Login Panel
{
logStr = TEXT("LI PASS panel open");
break;
}
// LI PASS Panel Close
case ELIEventType::LIP_PANEL_CLOSE:
{
logStr = TEXT("LI PASS panel close");
break;
}
// LI PASS Open Login Panel
case ELIEventType::LOGIN_PANEL_OPEN:
{
logStr = TEXT("Login panel open");
break;
}
// LI PASS Close Login Panel
case ELIEventType::LOGIN_PANEL_CLOSE:
{
logStr = TEXT("Login panel close");
break;
}
// LI PASS Open Account Center
case ELIEventType::ACCOUNT_CENTER_OPEN:
{
logStr = TEXT("Account center open");
break;
}
// LI PASS Close Account Center
case ELIEventType::ACCOUNT_CENTER_CLOSE:
{
logStr = TEXT("Account center close");
break;
}
// LI PASS Save Protocol Version
case ELIEventType::SET_PROVISION:
{
logStr = FString::Printf(TEXT("LI set provision: %s"), *Event.ExtraJson);
break;
}
// LI PASS Real Name Compliance Success
case ELIEventType::COMPLIANCE_AGE_SUCESS:
{
logStr = TEXT("LI age and region compliance success");
break;
}
// LI PASS Minor Compliance Success
case ELIEventType::COMPLIANCE_MINOR_SUCESS:
{
logStr = TEXT("LI minor compliance success");
break;
}
// LI PASS Close Parent Tip Panel
case ELIEventType::CLOSE_PARENT_TIP_PANEL:
{
logStr = TEXT("LI close parent tip panel");
break;
}
// LI PASS Close Parent Deny Panel
case ELIEventType::CLOSE_PARENT_DENY_PANEL:
{
logStr = TEXT("LI close parent deny tip panel");
break;
}
// LI PASS Close Parent Certificate Panel
case ELIEventType::CLOSE_PARENT_CERTIFICATE_PANEL:
{
logStr = TEXT("LI close parent certificate panel");
break;
}
// LI PASS Close Delete Account Panel
case ELIEventType::CLOSE_DELETE_ACCOUNT_PANEL:
{
logStr = TEXT("LI close delete account panel");
break;
}
// LI PASS Init Repeat
case ELIEventType::INTL_REPEAT:
{
break;
}
// LI PASS Init Finish
case ELIEventType::GN_READY:
{
// ULevelInfiniteAPI::AutoLogin();
// ULevelInfiniteAPI::OpenLoginPanel();
break;
}
}
}
// Remove callbacks
ULevelInfiniteAPI::GetEventDelegate().Remove(LIEventObserver);
开发人员也可添加 LILuaErrorObserver 回调处理 Lua 错误。
步骤5:设置 LI PASS 界面和邮件的语言
开发人员需要设置所有 LI PASS 界面和邮件使用的语言,因此需要使用语言代码作为入参调用 SetLanguage
。建议此处设置的语言与游戏语言保持一致,以确保一致性。否则,LI PASS 界面和电子邮件中使用的语言可能与游戏使用的语言不同。
- Unity
- Unreal Engine
LevelInfinite.SetLanguage("zh-Hans");
ULevelInfiniteAPI::SetLanguage(TEXT("zh-Hans"));
步骤6:设置 UI 跟节点
在使用任何其他 LI PASS 功能之前,需调用 SetUIRoot
设置所有 LI PASS 界面的根节点。
设置 UI 根节点后,请不要销毁该节点,否则 LI PASS UI 界面无法挂载,会产生预期外的行为。
LI PASS 界面的释放请通过界面按钮操作,不要强制销毁。在切换场景时若由 Unity 控制 GC(垃圾回收)可能发生,会造成空指针错误出现。
- Unity
- Unreal Engine
有关分辨率的详细设置,请参见 设置 UI 根节点。
LevelInfinite.SetUIRoot(uiRoot);
Unreal Engine 需要将根节点铺满整个屏幕。
ULevelInfiniteAPI::SetUIRoot(uiRoot);
步骤7:设置发布地区
LI PASS 登录组件内置默认的合规流程,需要开发人员使用 allowList
和 blockList
数组作为入参调用 UpdateCountryList
。allowList
数组应包含 LI PASS 国家地区选择页面需要展示的国家地区的 ISO 3166-1 数字代码,而 blockList
数组应包含不应显示的国家或地区代码。
此处指定的国家和地区将用于 LI PASS 登录组件内置的合规工作流程中。
- Unity
- Unreal Engine
string[] allowArray = new string[] {"496","400"};
string[] blockArray = new string[] {"400"};
LevelInfinite.UpdateCountryList(allowArray,blockArray);
TArray<FString> allowList;
allowList.Add(TEXT("496"));
allowList.Add(TEXT("400"));
TArray<FString> blockList;
blockList.Add(TEXT("400"))
ULevelInfiniteAPI::UpdateCountryList(allowList,blockList);
步骤8:添加 LI PASS 功能
Unreal Engine 主机平台游戏内账号绑定
游戏内账号绑定功能仅用于 Unreal Engine 中的主机平台,其他平台无需接入。
此功能用于在游戏页面内拉起绑定 LI PASS 页面。
// 添加登录态监听代码
AuthResultObserver = UINTLSDKAPI::GetAuthResultObserver().AddUObject(this, &UFloatingSidebar::OnAuthResult_Callback);
// 查询当前用户的绑定关系
UINTLSDKAPI::QueryBindInfo();
// 处理查询绑定关系的回调
void UFloatingSidebar::OnAuthResult_Callback(FINTLAuthResult AuthResult)
{
UE_LOG(LogTemp, Warning, TEXT("UFloatingSidebar::OnAuthResult_Callback AuthResult.MethodId: %d"),AuthResult.MethodId);
if(AuthResult.MethodId == (int32)LIMethodId::kLIQueryBindInfo) {
if(AuthResult.RetCode == INTL_NAMESPACE::ErrorCode::SUCCESS)
{
UE_LOG(LogTemp, Warning, TEXT("LI query bind info success bindlist = %s"), *AuthResult.BindList);
TArray <TSharedPtr<FJsonValue>> bindInfoArray;
TSharedRef<TJsonReader<TCHAR>>jsonReader = TJsonReaderFactory<TCHAR>::Create(AuthResult.BindList);
bool isSe = FJsonSerializer::Deserialize(jsonReader, bindInfoArray);
if (isSe)
{
UE_LOG(LogTemp, Warning, TEXT("LI query bind info format JsonObject success"));
bool haveBindLI = false;
FString liEmail;
for(int i = 0; i < bindInfoArray.Num(); i++)
{
int32 channelId = bindInfoArray[i]->AsObject()->GetIntegerField("channelid");
FString email = bindInfoArray[i]->AsObject()->GetStringField("email");
UE_LOG(LogTemp, Warning, TEXT("LI query bind info channelId = %d, email = %s"), channelId, *email);
if(channelId == (int32)EINTLLoginChannel::kChannelLevelInfinite) {
haveBindLI = true;
liEmail = email;
break;
}
}
if(!haveBindLI) {
// 打开游戏内绑定的页面
ULevelInfiniteAPI::OpenBindAccount();
} else {
// 当前用户已绑定 LI PASS,无需进行后续的引导绑定操作
UE_LOG(LogTemp, Warning, TEXT("Account have bind LI"));
}
} else {
UE_LOG(LogTemp, Warning, TEXT("LI query bind info format JsonObject failed"));
}
}else{
UE_LOG(LogTemp, Warning, TEXT("LI query bind info failed, ret_code = %d, ret_msg = %s"), AuthResult.RetCode, *AuthResult.RetMsg);
// token失效的情况
FINTLAuthResult CurrentAuthResult;
if (UINTLSDKAPI::GetAuthResult(CurrentAuthResult)) {
EINTLLoginChannel channel = static_cast<EINTLLoginChannel>(CurrentAuthResult.ChannelID);
if (channel == EINTLLoginChannel::kChannelPS5) {
FString permissions = "psn:s2s openid id_token:psn.basic_claims";
ULevelInfiniteAPI::LoginChannelWithLIPass(channel, permissions, "{}");
} else if (channel == EINTLLoginChannel::kChannelXbox) {
ULevelInfiniteAPI::LoginChannelWithLIPass(channel, "", "{}");
} else {
ULevelInfiniteAPI::LoginChannelWithLIPass(channel, "", "{}");
}
}
}
}
}
功能示例:
使用网页登录组件完成登录流程
推荐使用 Player Network 控制台配置 LI PASS Web SDK, 详见 LI PASS 配置 中的 web 配置。
步骤1:引入 SDK
从 CDN
安装 Web SDK。
// SDK 联调版本包
<script src="https://test-common-web.intlgame.com/sdk-cdn/infinite-pass/latest/index.umd.js"></script>
<link rel="stylesheet" href="https://test-common-web.intlgame.com/sdk-cdn/infinite-pass/latest/index.css" />
// SDK 正式版本包
<script src="https://common-web.intlgame.com/sdk-cdn/infinite-pass/latest/index.umd.js"></script>
<link rel="stylesheet" href="https://common-web.intlgame.com/sdk-cdn/infinite-pass/latest/index.css" />
步骤2:在 Player Network 控制台配置 LI PASS
详细配置步骤,请参见 LI PASS 配置。
配置 web 类型的登录配置后,可在发布后获得 WEB_ID
。
您可在 Player Network 控制台创建多个 LI PASS web 配置,在不同的 web 场景使用不同的配置。
步骤3:实例化 JavaScript SDK
引入 JS、CSS 资源
<script src="https://test-common-web.intlgame.com/sdk-cdn/infinite-pass/latest/index.umd.js"></script>
<link rel="stylesheet" href="https://test-common-web.intlgame.com/sdk-cdn/infinite-pass/latest/index.css" />
<!-- 项目上线时,请使用正式版本 SDK 正式版本包 -->
<!-- 挂载节点 -->
<div id="infinite-pass-component"></div>
实例化 SDK
const pass = new PassFactory.Pass({
env: "test", // environment, 正式环境参数请联系 Player Network 助手
gameID: xxxxx, // Player Network 控制台中配置的 GAME_ID
appID: "", // Player Network 控制台中配置的 APP_ID
webID: "", // Player Network 控制台中配置的 WEB_ID
config: {}, // 如果不使用 webID,可配置 config
});
初始化参数详情,请参见 网页组件配置。
如果您已在 Player Network 控制台创建好 LI PASS web 的配置,只需要复制 WEB_ID
在代码初始化传入即可,并且能够实时修改配置。
如果您未在 Player Network 控制台完成 web 配置,则需参考 详细配置 在代码里手动配置 config
。
步骤4:使用网页登录组件实现 LI PASS 登录/注册
LEVEL INFINITE PASS 登录组件支持玩家通过 LEVEL INFINITE PASS 和第三方渠道登录游戏。
初始化 pass
组件后,调用 Start
方法,将 LI PASS web login widget 挂载到指定的 DOM 节点。 登录成功后,登录结果会在 intlSignResp
中返回。
登录/注册成功完成后,组件的 UI 不会变更,需要自行处理后续逻辑。如将登录信息存储到 cookie 或者 localStorage, 并将 UI 导航到登录成功的页面。
要自定义登录交互,请参见 Web。
<div id="infinite-pass-component"></div>
const pass = new PassFactory.Pass({
env: "test", // Environment
gameID: xxxxx, // Player Network 控制台中配置的 GAME_ID
appID: "", // Player Network 控制台中配置的 APP_ID
webID: "", // Player Network 控制台中配置的 WEB_ID
});
// 调用 `Start` 将登录启动器挂载到指定的 DOM 节点
// 登录成功后 登录结果在 `onLogin`、`onRegister` 事件返回
pass.start("#infinite-pass-component")
// 您可以监听事件列表,当用户完成登录或注册时获取用户的鉴权信息
pass.on("onLogin", (userInfo) => {
// After the user fails to log in, the 'onLoginError event' will be triggered, and the game logic after the user successfully logs in, can be processed in the event callback
// For example, redirect to a specific page
console.log(userInfo);
});
pass.on("onRegister", (userInfo) => {
// Logic added after successful registration
// For example, redirect to a specific page
console.log(userInfo);
});
返回参数
参数 | 类型 | 描述 |
---|---|---|
ret | number | 返回代码 0:请求成功 !=0:请求失败,查看 msg 详细结果说明 |
msg | string | 结果说明 |
token | string | Player Network SDK 生成的用户 token 长度:40字节,详见 Token |
openid | string | Player Network SDK 用户唯⼀标识 默认为 64-bit 无符号整数的字符串,也可以支持 32-bit |
token_expire_time | int64 | 令牌过期时间 Unix 时间 |
user_name | string | 用于登录的用户名 |
channel_info | object | 当前渠道的渠道信息。 更多信息,请参见 渠道信息。 |
del_account_status | number | 账号注销状态 -1:查询失败 0:没有记录撤销 Player Network 账号注销请求 (包括邮件账号和电话账号) 1:删除的静默期 2:账号注销完成 3:注销账号 4:账号注销失败 |
seq | string | 数据流消息序号 |
first_login | number | 是否首次登录 未知: -1 否: 0 是: 1 |
返回示例
{
channel_info:{
account:"wuqinghao@outlook.com",
account_plat_type: 131,
account_type: 1,
channelId: 131
},
del_account_status: 0,
first_login: 0,
msg: "success",
openid: "xxx",
ret: 0,
seq: "1638194026-0180225310-032531-0000292460",
token: "xxx",
token_expire_time: 1638494026,
user_name: "user",
}
步骤5:使用网页登录组件实现第三方渠道登录
LI PASS web 登录第三方渠道仅支持系统浏览器,任何游戏或应用的内置 WebView 组件均不支持第三方渠道登录:
- 基于安全考量,Google 和 Facebook 完全禁止在内置 WebView 使用网页登录
- WebView 组件不能同时加载两个页面,这意味着它无法加载第三方登录页面来完成登录过程
LI PASS 网页登录组件的第三方渠道登录目前支持 Apple、Discord、Epic、Facebook、Google、LINE、PS5、Steam、Twitch、Twitter、VK、QQ、Wechat。
方法1:通过 Player Network 控制台配置(推荐)
通过 Player Network 控制台配置第三方渠道登录,实时地变更需要展示的第三方渠道。详细配置步骤,请参见 Web 登录渠道。
方法2:通过代码配置
<div id="infinite-pass-component"></div>
const pass = new PassFactory.Pass({
env: "test", // Environment
gameID: xxxxx, // Player Network 控制台中配置的 GAME_ID
appID: "", // Player Network 控制台中配置的 APP_ID
config: {
socialList: ['facebook', 'twitter']
}
});
// 调用 `start` 方法将登录启动器挂载到指定的 DOM 节点
pass.start("#infinite-pass-component");
// 您可以监听事件列表,当用户完成登录或注册时获取用户的鉴权信息
pass.on("onLogin", (userInfo) => {
// After the user fails to log in, the 'onLoginError event' will be triggered, and the game logic after the user successfully logs in, can be processed in the event callback
// For example, redirect to a specific page
console.log(userInfo);
});
pass.on("onLoginError", (userInfo) => {
// The 'onLogin event' will be triggered after the user successfully logs in, and the game logic after the user fails to log in, can be processed in the event callback.
console.log(userInfo);
});
关键配置参数
参数 | 类型 | 描述 | 备注 |
---|---|---|---|
socialList | string[] | 渠道列表,详细配置请参见 config | 选填 |
socialParams | object | 三方渠道登录时需要的配置, 详细配置请参见 socialParams | 选填 |
返回参数
参数 | 类型 | 描述 |
---|---|---|
ret | number | 返回代码 0:请求成功 !=0:请求失败,查看 msg 详细结果说明 |
msg | string | 结果说明 |
token | string | Player Network SDK 生成的用户 token 长度:40字节,详见 Token |
openid | string | Player Network SDK 用户唯⼀标识 默认为 64-bit 无符号整数的字符串,也可以支持 32-bit |
token_expire_time | int64 | Player Network SDK token 过期时间 Unix时间 |
user_name | string | 用于登录的用户名 |
channel_info | object | 登录渠道的渠道信息 更多信息,请参见 渠道信息。 |
del_account_status | number | 账号注销状态 -1:查询失败 0:未撤回任何注销账号记录或删除号码 (邮件或手机账号) 1:账号注销前的静默期 2:已经成功注销账号 3:正在注销账号 4:注销账号失败 |
seq | string | 数据流消息序号 |
返回示例
{
channel_info: {
access_token:"xxxxx",
expire_ts: 1641527900,
}
first_login: 0,
msg: "success",
openid: "xxxx",
picture_url: "https://www.google.com/Images/profileA.png",
ret: 0,
seq: "1638935899-1006943754-018668-0000656615",
token: "xxx",
token_expire_time: 1641527900,
del_account_status: 0,
user_name: "user",
}
步骤6:实现身份验证状态保留
使用 LI PASS web login widget 时,您可以根据信息的存入方式自行开发身份验证状态如何持久保留:
- 已登录的用户在显式退出登录前,无限期地保留身份验证状态(存储 cookie 或者 localStorage)
- 在窗口关闭时清除身份验证状态(存入 sessionStorage)
- 在页面重新加载时清除身份验证状态(不做存储)
下列为表格为存储的用户凭证信息,可通过监听 onLogin
、onRegister
事件获取。
参数 | 类型 | 读取方式 |
---|---|---|
openid | string | userInfo.openid |
token | string | userInfo.token |
channelId | string | userInfo.channel_info.channelId |
禁用默认行为
使用 LI PASS web login widget 登录后,会自动将身份验证状态存储到 logined_account_cache_key
cookie 中。为避免过度依赖此 cookie,如果需要保留登录态请选择将用户凭证信息存入 localStorage 或 sessionStorage。
如果需要禁用该默认行为,可以在初始化 widget 的时候进行设置:
const pass = new PassFactory.Pass({
env: "", // Environment
gameID: "", // Game ID
appID: "", // LI PASS App ID
config: {
disableCookie: true
}
});
API 列表
方法名 | 描述 |
---|---|
start | 初始化 LI PASS web login widget |
unmount | 卸载/关闭 LI PASS web login widget |
changeLanguage | 切换语言,支持语言请参见 语言列表 |
on | 监听事件,支持的事件请参见 事件列表 |
preOrder | LI PASS 预约方法 |
unmount
调用 unmount
方法会将 LI PASS web login widget 从 DOM 上卸载。
changeLanguage
可以通过调用 changeLanguage
方法切换 login widget 语言,如果传入的语言参数暂不支持,会展示默认语言 en
。
语言列表
编码 | 语言类型 |
---|---|
ar | Arabic(阿拉伯语) |
cs | Czech(捷克语) |
de | German(德语) |
en | English(英语) |
es | Spanish (Latin America)(西班牙语-拉美) |
es-eu | Spanish (Europe)(西班牙语-欧洲) |
fi | Finnish(芬兰语) |
fil-PH | Filipino(菲律宾语) |
fr | French(法语) |
hi | Hindi(印地语) |
id | Indonesian(印尼语) |
it | Italian(意大利语) |
ja | Japanese(日语) |
ko | Korean(韩语) |
ms | Malay(马来语) |
nl | Dutch(荷兰语) |
pl | Polish(波兰语) |
pt | Portuguese (Latin America)(葡语-拉美) |
ru | Russian(俄语) |
th | Thai(泰语) |
tr | Turkish(土耳其语) |
vi | Vietnamese(越南语) |
zh-Hant | Chinese (Traditional)(繁体中文) |
zh-Hans | Chinese (Simplified)(简体中文) |
zh-TW | Chinese (Taiwan)(中文-台湾) |
示例
// login widget 切换语言到意大利语
pass.changeLanguage("it");
on
调用 pass
组件提供的 on
方法可以注册回调事件处理器。用户在 LI PASS web login widget 进行操作时,组件会在相应的时机回调事件。
需要在用户触发相应事件前注册事件。
示例
pass.on("onLogin", (userInfo) => {
// 用户登录失败后会触发 'onLoginError 事件',可以在该事件回调里处理用户成功登录后的业务逻辑
// 例如跳转到特定的某个页面
console.log(userInfo);
});
pass.on("onLoginError", (userInfo) => {
// 用户成功登录后会触发 'onLogin 事件',可以在该事件回调里处理用户登录失败后的业务逻辑
console.log(userInfo);
});
pass.on("onRegister", (userInfo) => {
// 注册成功后加入的逻辑
// 例如跳转到特定的某个页面
console.log(userInfo);
});
事件列表
方法名 | 描述 | 回调参数 |
---|---|---|
onLoad | 加载渲染的组件 | accountApi |
onClose | 模态框模式下点击关闭按钮关闭组件 | accountApi |
onLogin | 登录成功 | userInfo,accountApi |
onLoginError | 登录失败 | error |
onLoginTabChange | login widget 上的 Tab 开关 | activeTab |
onRegister | 注册成功 | userInfo,accountApi |
onRegisterError | 注册失败 | error |
onPwdReset | 密码重置成功 | accountApi |
onPwdResetError | 密码重置失败 | error,accountApi |
onPreOrder | 预约成功 | result |
onPreOrderError | 预约失败 | result |
preOrder
使用 preOrder
方法实现预注册,用户下载游戏后无需重新注册;收集用户有效邮箱,实现有效触达。
参数列表
参数 | 类型 | 描述 | 备注 |
---|---|---|---|
apiKey | string | 使用相同的 gameid 在 Smartlink 申请 apiKey | Required |
tags | string[] | 为 SmartLink 联系人添加自定义标签 | Optional |
示例
const pass = new PassFactory.Pass({
env: "", // Environment
gameID: "", // Game ID
appID: "", // LI PASS App ID
config: {
langType: 'en' // 默认为 'en', 修改此配置来更换默认语言
}
});
// 调用 `preOrder` 方法展示预订组件
pass.preOrder({
apiKey: 'xxxxxxxxx',
tags: ['tag1', 'tag2'],
})