入门指引
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 回调
- 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, "", "{}");
}
}
}
}
}
功能示例:
使用网页登录组件完成登录流程
LI PASS 网页登录组件的第三方渠道登录仅支持系统浏览器。 任何游戏或应用的内置 WebView 组件均不支持第三方渠道登录。 第一原因是安全问题,Google 和 Facebook 完全禁止在内置 WebView 使用网页登录。 第二原因是,WebView 组件不能同时加载两个页面,这意味着它无法加载第三方登录页面来完成登录过程。
步骤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:实例化 JavaScript SDK
引入 JS、CSS 资源
<!-- import JS -->
<script src="https://test-common-web.intlgame.com/sdk-cdn/infinite-pass/latest/index.umd.js"></script>
<!-- import CSS -->
<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
config: {
socialList: ["facebook", "line"], // Supports facebook, twitter, google, apple, line
},
});
初始化参数 | 类型 | 描述 | 备注 |
---|---|---|---|
env | string | SDK 环境,test 为测试环境更多信息,请参见 获取部署集群信息 | 必填 |
gameID | number | 游戏 ID | 必填 |
appID | string | App ID Player Network 控制台中的 ACCOUNT_APP_ID | 必填 |
config | object | 高级配置 | 选填 |
初始化参数详情,请参见 网页组件配置。
步骤3:使用网页登录组件实现 LI PASS 登录/注册
LEVEL INFINITE PASS 登录组件支持玩家通过 LEVEL INFINITE PASS 和第三方渠道登录游戏。
初始化 pass
组件后,调用 Start
方法,将 LI PASS web login widget 挂载到指定的 DOM 节点。 登录成功后,登录结果会在 intlSignResp
中返回。
登录/注册成功完成后,组件的 UI 不会变更,需要自行处理后续逻辑。如将登录信息存储到 cookie 或者 localStorage, 并将 UI 导航到登录成功的页面。
<div id="infinite-pass-component"></div>
const pass = new PassFactory.Pass({
env: "test", // 环境
gameID: , // 游戏 ID
appID: ""// 账号 App 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 |
pf | string | pf 值,用于 Midas 支付 |
pf_key | string | pf key,用于 Midas 支付 |
返回示例
{
birthday: "2000-01",
channel_info:{
account:"wuqinghao@outlook.com",
account_plat_type: 131,
account_type: 1,
channelId: 131
},
del_account_status: 0,
first_login: 0,
gender: 0,
msg: "success",
need_name_auth: false,
openid: "xxx",
pf: "INTLProject_INTLProject-00000000-web-00000000-INTLProject-xxxxxxxxxxxxxxxxxxxxxxxxxxx-13635546925269953152",
pf_key: "7682184xxxxxxxxxxxxxxxxxxxxxxefc8b45",
picture_url: "",
ret: 0,
seq: "1638194026-0180225310-032531-0000292460",
token: "xxx",
token_expire_time: 1638494026,
user_name: "user",
}
步骤4:使用网页登录组件实现第三方渠道登录
LI PASS 网页登录组件的第三方渠道登录目前仅支持 Apple、Discord、Epic、Facebook、Google、LINE、PS5、Steam、Twitch、Twitter、VK。
LI PASS Web 登录第三方渠道仅支持系统浏览器。 任何游戏或应用的内置 WebView 组件均不支持第三方渠道登录。 第一原因是安全问题,Google 和Facebook 完全禁止在内置 Web View 使用网页登录。 第二原因是,WebView 组件不能同时加载两个页面, 这意味着它无法加载第三方登录页面来完成登录过程。
<div id="infinite-pass-component"></div>
const pass = new PassFactory.Pass({
env: "test", // 环境
gameID: "", // 游戏 ID
appID: "", // 账号 App ID
config: {
socialList: ['facebook', 'twitter']
}
});
// 调用 `start` 方法将登录启动器挂载到指定的 DOM 节点
// 登录成功后 登录结果在 `userInfo` 中返回,也可以选择监听 `onLogin` 事件
pass.start("#infinite-pass-component").then((userInfo) => {
console.log("userInfo: ", userInfo);
});
关键配置参数
参数 | 类型 | 描述 | 备注 |
---|---|---|---|
socialList | string[] | 第三方渠道类型: apple discord epic line ps5 steam twitch vk | 选填 |
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 | 用于登录的用户名 |
birthday | string | 用户生日 |
channel_info | object | 登录渠道的渠道信息 更多信息,请参见 渠道信息。 |
del_account_status | number | 账号注销状态 -1:查询失败 0:未撤回任何注销账号记录或删除号码 (邮件或手机账号) 1:账号注销前的静默期 2:已经成功注销账号 3:正在注销账号 4:注销账号失败 |
seq | string | 数据流消息序号 |
返回示例
{
birthday: "",
channel_info: {
access_token:"xxxxx",
expire_ts: 1641527900,
}
first_login: 0,
gender: 0,
msg: "success",
need_name_auth: false,
openid: "xxxx",
pf: "facebook_fb-00000000-web-00000000-fb-xxxxxxxxxxxxxxxx-419213088809929925",
pf_key: "6cc9bxxxxxxxxxxxxxxxxxx826",
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,
uid: "xxxx",
user_name: "user",
}
步骤5:实现身份验证状态保留
使用 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'],
})