您好!在我的博客当中,我将持续挑选一些优质的国外技术文章进行翻译,如果文章内容翻译有误,欢迎在评论区指正,感谢:)
本文是Nakama系列的第二篇,第一篇的链接如下:https://segmentfault.com/a/1190000044502161
以下是原文链接:Tutorial: Making a Multiplayer Game with Nakama and Unity: Part 2/3 – The Knights of Unity
3.1 身份验证
在之前的文章当中,我们关注于如何设置 Nakama 并让它的所有组件。在这篇文章中我们将继续关注和多人游戏开发相关技术:身份验证。它帮助我们接受并发送数据至服务端;
3.1.1 会话认证
会话认证(Session Authentication)
Nakama 提供的几乎所有和服务器通信的方法,都需要用户先启动并维护一条与服务器的会话。在我们开启一条会话之前,我们需要先创建 Nakama。客户端需要包含一系列有用的方法,从而将数据发送至服务端。
以下是一段用于建立客户端到服务端连接的代码。
// NakamaSessionManager.cs
/// <summary>
/// 用于建立客户端到服务端连接.
/// 包含与Nakama的服务器通信所需的有用方法列表
/// Contains a list of usefull methods required to communicate with Nakama server.
/// 不要直接使用它, 通过 <see cref="Client"/> 替代.
/// </summary>
private Client _client;
...
/// <summary>
/// Used to establish connection between the client and the server.
/// Contains a list of usefull methods required to communicate with Nakama server.
/// </summary>
public Client Client
{
get
{
if (_client == null)
{
// "defaultkey" should be changed when releasing the app
// see https://heroiclabs.com/docs/install-configuration/#socket
_client = new Client("defaultkey", _ipAddress, _port, false);
}
return _client;
}
}
在将 Client 准备好后,当我们想要和服务端进行验证时,我们可以使用以下几种方法来验证 Nakama 设备:
- 使用 unique ID,将其分配给每个设备;
- 使用 Email 以及密码;
- 使用你的 Facebook、Google、Game Center 或者 Steam 账号;
- 自定义你的验证系统;
异步验证与异常判断
因为这个 Jolly Rogers Demo 的游戏架构,最合适的方式是使用设备 ID 加上 Facebook 账号验证来完成验证。以下是 NakamaSessionManager.cs
的代码;
//NakamaSessionManager.cs
/// <summary>
/// 通过ID验证一个新的会话. 如果这是第一次验证.
/// 当前设备已创建新账户.
/// </summary>
/// <returns>在每次call服务器成功的时候返回true.</returns>
private async Task<AuthenticationResponse> AuthenticateDeviceIdAsync()
{
try
{
Session = await Client.AuthenticateDeviceAsync(_deviceId, null, false);
Debug.Log("Device authenticated with token:" + Session.AuthToken);
return AuthenticationResponse.Authenticated;
}
catch (ApiResponseException e)
{
if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
Debug.Log("Couldn't find DeviceId in database, creating new user; message: " + e);
return await CreateAccountAsync();
}
else
{
Debug.LogError("An error has occured reaching Nakama server; message: " + e);
return AuthenticationResponse.Error;
}
}
catch (Exception e)
{
Debug.LogError("Counldn't connect to Nakama server; message: " + e);
return AuthenticationResponse.Error;
}
}
如你所见,当前 AuthenticateDeviceIdAsync()
方法展示了以上的异步任务实现方式:不适用 events 以及 callbacks,而是使用 Tasks。当我们使用 await 标识了一个需要使用的异步函数后,我们总是同时需要在当前的函数前使用一个 async ,Unity 将会创建一个新的线程并且一直阻塞当前进程,直到 Task 返回,结束线程阻塞状态。在这种方式下,主线程不会因此而阻塞,它允许我们在发送消息至服务器、消息在服务器进行处理的同时,可以正常进行游戏。
在网络上进行通信并不总是能成功的,因此当我们 calling 这个 AuthenticateDeviceAsync
方法时可能会抛出异常。为了确定在错误发生时候的错误情况,在报错时时我们需要检查错误状态码以及错误信息——在某些情况下,当前的异常不是由一些网络信号产生的,但是我们的服务器依然收到了不合法的数据。此时我们需要检查这个错误是否是 HttpStatusCode.NotFound
, 在某些情况下,这个服务器会告诉我们当前用户尝试验证一个不存在的账号,并提示其需要在第一次使用时新建一个账号。
3.1.2 会话创建与验证设置
//NakamaSessionManager.cs
/// <summary>
/// 在Nakama服务器上创建新的账号ID:<see cref="_deviceId"/>.
/// </summary>
/// <returns>在账号成功创建时返回真.</returns>
private async Task<AuthenticationResponse> CreateAccountAsync()
{
try
{
Session = await Client.AuthenticateDeviceAsync(_deviceId, null, true);
return AuthenticationResponse.NewAccountCreated;
}
catch (Exception e)
{
Debug.LogError("Couldn't create account using DeviceId; message: " + e);
return AuthenticationResponse.Error;
}
}
在两种情况下,我们会使用 Client.AuthenticateDeviceAsync
:它和 AuthenticateDeviceIdAsync
方法只有第三个参数不同,在 CreateAccountAsync
中第三个参数被传入为真,如果没有账号被绑定到当前的设备 ID,创建一个新的账号并且绑定发送者 ID。我们可以让服务器在第一次调用时创建一个新的账号,然后这样我们就可以在用户第一次登录时,实现一些自定义的逻辑,比如显示头像选择面板之类的功能。
与其在每次打开游戏的时候都进行一次身份验证,更建议在程序第一次运行时或者会话到期之后进行身份验证。我们可以保存当前会话的身份码通过 PlayerPrefs 技术,并在每次用户登录时恢复它。
//NakamaSessionManager.cs
/// <summary>
/// Stores Nakama session authentication token in player prefs
/// </summary>
private void StoreSessionToken()
{
if (Session == null)
{
Debug.LogWarning("Session is null; cannot store in player prefs");
}
else
{
PlayerPrefs.SetString("nakama.authToken", Session.AuthToken);
}
}
默认的会话持续时间设置为60秒:这适合于重视安全性的应用程序。然而,游戏行业并不总是需要这么多的安全保障,因此有些工作室会将会话的持续时间延长至数周、数月甚至数年。我们可以使用 YAML 配置文件来扩展它,该文件应该包含在你绑定的 volumes 文件中,这个文件的配置在 Part1 中已讲解。
3.2 Facebook 验证
Nakama for Unity 插件并没有附带适用 Facebook 的 SDK 或者任何其他的外部 API,因此我们需要自行下载 Facebook for Unity 的插件。在我们使用 Facebook 账号进行身份验证之前,我们需要先引导用户完成与 Facebook 的连接。
//NakamaSessionManager.cs
/// <summary>
/// 初始化与Facebook的连接
/// </summary>
/// <param name="handler">在Facebook授权后调用.</param>
public void ConnectFacebook(Action<FacebookResponse> handler)
{
if (FB.IsInitialized == false)
{
FB.Init(() => InitializeFacebook(handler));
}
else
{
InitializeFacebook(handler);
}
}
/// <summary>
/// Invoked by <see cref="FB.Init(InitDelegate, HideUnityDelegate, string)"/> callback.
/// 尝试使用Facebook帐户登录,并使用Nakama服务器来验证用户
/// </summary>
/// <param name="handler">Invoked after Facebook authorisation.</param>
private void InitializeFacebook(Action<FacebookResponse> handler)
{
FB.ActivateApp();
List<string> permissions = new List<string>();
permissions.Add("public_profile");
FB.LogInWithReadPermissions(permissions, async result =>
{
FacebookResponse response = await ConnectFacebookAsync(result);
handler?.Invoke(response);
});
}
/// <summary>
/// 将当前的Nakama账号和Facebook连接.
/// </summary>
private async Task<FacebookResponse> ConnectFacebookAsync(ILoginResult result)
{
FacebookResponse response = await LinkFacebookAsync(result);
if (response != FacebookResponse.Linked)
{
return response;
}
. . .
}
因为 Facebook SDK 是外部插件,因此我们需要处理不同的身份验证可能的错误情况。比如,当用户取消了 Facebook 的登录时,和 Facebook 的连接将会失败,或将给定的 Facebook 令牌分配给另一个帐户。
//NakamaSessionManager.cs
/// <summary>
/// 尝试使用Facebook的帐户验证当前用户;如果在Nakama中没发现使用过的facebook账号.
/// 在数据库中创建新的Nakama用户帐户
/// 连接到提供Facebook帐户连接的帐户.
/// </summary>
private async Task<FacebookResponse> LinkFacebookAsync(ILoginResult result = null)
{
if (FB.IsLoggedIn == true)
{
string token = AccessToken.CurrentAccessToken.TokenString;
try
{
// 此行代码为Nakama插件所提供,
await Client.LinkFacebookAsync(Session, token, true);
return FacebookResponse.Linked;
}
catch (ApiResponseException e)
{
if (e.StatusCode == System.Net.HttpStatusCode.Conflict)
{
return FacebookResponse.Conflict;
}
else
{
Debug.LogWarning("An error has occured reaching Nakama server; message: " + e);
return FacebookResponse.Error;
}
}
catch (Exception e)
{
Debug.LogWarning("An error has occured while connection with Facebook; message: " + e);
return FacebookResponse.Error;
}
}
else
{
if (result == null)
{
Debug.Log("Facebook not logged in. Call ConnectFacebook first");
return FacebookResponse.NotInitialized;
}
else if (result.Cancelled == true)
{
Debug.Log("Facebook login canceled");
return FacebookResponse.Cancelled;
}
else if (string.IsNullOrWhiteSpace(result.Error) == false)
{
Debug.Log("Facebook login failed with error: " + result.Error);
return FacebookResponse.Error;
}
else
{
Debug.Log("Facebook login failed with no error message");
return FacebookResponse.Error;
}
}
}
以上代码主要用于处理不同情况下产生的对应的异常。
3.3 总结
通过与用户进行身份认证,可以保证在和服务器的会话结束之后,依然可以和服务器进行正确的连接。对于简单的访问,我们可以使用 Unity 提供的 PlayerPrefs 来存储并恢复用于身份验证的 Token。在下一小节当中,我们将会讲解 Nakama 所提供的其他服务器功能、如何实现它们,以及相关的注意事项。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。