# 初始化新用户

**URL:** https://heroiclabs.com/docs/zh/nakama/guides/concepts/initialize-users/
**Summary:** 本教程介绍在首次注册时为新玩家创建和初始化用户记录（钱包、库存等）的不同方法。

---


# 初始化新用户

当新用户注册并为其设置了一组记录时，这通常很有用。在游戏中，用户的虚拟钱包、初始库存物品等可能需要它。本教程将介绍处理此用例的几种不同方法。

## 注册回调后

最简单的方法是在客户端注册函数的成功回调中写入记录。

这段代码通过精简示例演示了如何实现这一过程。在实际应用程序代码中，您将根据管理连接和重新连接的方式，将[身份验证](../../concepts/authentication/)和连接逻辑从存储写入中分割出来。

```csharp
var deviceId = SystemInfo.deviceUniqueIdentifier;
var session = await client.AuthenticateDeviceAsync(deviceId);

var json = "{"coins": 100, "gems": 10, "artifacts": 0}";
var object = new WriteStorageObject = {
  "collection" = "wallets",
  "key" = "mywallet",
  "value" = json
};
const storageWriteAck = await client.WriteStorageObjectsAsync(session, objects);
Debug.Log("Successfully setup new user's records.");
```

需要注意的是，代码中存在需要注意平衡的地方。在将记录写入到存储之前，可能会发生断开连接的情况。这可能会导致用户设置不完整，应用程序处于错误状态。

只有当您想要避免写入服务器端代码或已在客户端上构建重试逻辑时，才有必要选择该选项。

## 服务器端钩子

也可以通过在注册完成后运行服务器端代码为新用户写入记录。另外还可以通过[注册钩子](../../server-framework/introduction/#hooks#register-hooks)来完成。

["register_after"钩子](../../server-framework/introduction/#hooks)可以与一种`"authenticaterequest_*"`消息类型一起使用，指示服务器在处理这个消息后运行函数。需要注意的是，服务器不区分注册和登录消息，因此我们使用[有条件写入](../../concepts/storage/collections/#conditional-writes)来存储记录。

{{< code type="server" >}}
```lua
local function initialize_user(context, payload)
  if payload.created then
    -- Only run this logic if the account that has authenticated is new.
    local changeset = {
      coins = 10,   -- Add 10 coins to the user's wallet.
      gems = 5      -- Add 5 gems from the user's wallet.
      artifacts = 0 -- No artifacts to start with.
    }
    local metadata = {}
    nk.wallet_update(context.user_id, changeset, metadata, true)
  end
end

-- change to whatever message name matches your authentication type.
nk.register_req_after(initialize_user, "AuthenticateDevice")
```
{{< / code >}}

{{< code type="server" >}}
```go
func InitializeUser(ctx context.Context, logger Logger, db *sql.DB, nk NakamaModule, out *api.Session, in *api.AuthenticateDeviceRequest) error {
  if out.Created {
    // Only run this logic if the account that has authenticated is new.
    userID, ok := ctx.Value(runtime.RUNTIME_CTX_USER_ID).(string)
    if !ok {
      return "", errors.New("Invalid context")
    }
    changeset := map[string]interface{}{
      "coins": 10,    // Add 10 coins to the user's wallet.
      "gems":  5,     // Add 5 gems from the user's wallet.
      "artifacts": 0, // No artifacts to start with.
    }
    var metadata map[string]interface{}
    if err := nk.WalletUpdate(ctx, userID, changeset, metadata, true); err != nil {
      // Handle error.
    }
  }
}

// Register as after hook, this call should be in InitModule.
if err := initializer.RegisterAfterAuthenticateDevice(InitializeUser); err != nil {
  logger.Error("Unable to register: %v", err)
  return err
}
```
{{< / code >}}

{{< missing type="server" lang="typescript" />}}

这种方法避免了与客户端断开连接的交替使用情况，但需要在每次登录或注册消息后进行数据库写入。能否采用这种方法取决于您向存储引擎写入数据的频率，如果[缓存用户会话](../../concepts/authentication/#sessions)进行快速重新连接，可以减少这种需要数据库写入的情况。
