# 脚本结构

> Understand the structure of a Cloud Code script and how the main function acts as the entry point.

Cloud Code 脚本中用作运行时入口点的主函数采用 CommonJS 包装器的形式。

以下代码片段显示了可能最简单的脚本：

*JavaScript*

```js
module.exports = async ({ params, context, logger }) => {
  // this script does nothing
};
```

虽然此脚本实际上不执行任何操作，但显示了脚本函数的上下文对象和异步性质。

> **Note:**
>
> **注意**：除非脚本导出 CommonJS 函数，否则脚本无效。

> **Note:**
>
> **注意**：脚本名称在整个项目和环境中必须具有唯一性，只能包含字母、数字、下划线和短划线，且不超过 50 个字符。

## 上下文对象##context-object

上下文对象包含以下有用的对象：

* `Params`：此对象是调用脚本时使用的输入参数的名称/值对数组。

* `Context`：此对象提供在脚本中很有用的其他上下文：

  * `projectId`：调用方通过身份验证的 Project ID。
  * `playerId`：经过身份验证的玩家 ID。
  * `accessToken`：这是可用于以经过身份验证的玩家身份调用其他 Unity 游戏服务 SDK 的 JWT。
  * `environmentName`：当前使用的 Unity [环境](/services/service-environments.md)的名称。
  * `environmentId`：当前使用的 Unity 环境的 ID。
  * `serviceToken`：这是用于以经过身份验证的 Cloud Code 用户身份调用其他 Unity 游戏服务 SDK 的 JWT。
  * `unityInstallationId`：用于标识客户端设备上的安装的唯一标识符。如果同一玩家在不同的设备上安装了游戏，则会拥有不同的 `installationId`。此信息可供与 Services SDK Core 包集成的所有 Unity 包使用。
  * `analyticsUserId`：这是一个标识玩家身份并具有唯一性的字符串，在玩家后续游戏会话中保持不变，用于分析用途。此标识符是主要用户标识符，来自 Core 包。
  * `correlationId`：用于关联请求的唯一标识符。

* `Logger`：此对象可以让脚本记录信息、警告和错误。

### JavaScript##javascript

```js
module.exports = async ({logger}) => {
  logger.info('This message confirms that the logging client is functional!');
  logger.warning('This is a serious warning that the cheese is about to run out.');
  logger.error('Out of cheese :(');
}
```

请参阅 [Logging](../../logging/overview) 文档了解更多信息。

### 令牌身份验证##token-authentication

`accessToken` 和 `serviceToken` 是作为 `context` 对象的属性提供的 [JWT](https://jwt.io/)。

这些令牌对 Cloud Code 的其他 Unity Gaming Services（Unity 游戏服务）调用进行身份验证。

| 令牌类型           | 来源                                          | 数据访问        | 用途                                                                          |
| -------------- | ------------------------------------------- | ----------- | --------------------------------------------------------------------------- |
| `accessToken`  | 由 [Authentication 服务](/authentication.md)生成 | 仅限经过身份验证的玩家 | `accessToken` 是用于对 Cloud Code 调用进行验证的 JWT。此令牌可以传递给其他 UGS 服务来访问经过身份验证的玩家的数据。 |
| `serviceToken` | 由 Cloud Code 生成                             | 允许访问跨玩家数据   | `serviceToken` 是 Cloud Code 生成的令牌，可用于调用其他 UGS 服务并处理跨玩家数据。                   |

配置的[访问控制](./access-control.md)规则会影响 `accessToken`。

以下示例说明了如何使用 `accessToken` 调用 Economy SDK 来获取玩家背包。

#### JavaScript##javascript

```js
// Player inventory
const { InventoryApi } = require("@unity-services/economy-2.4");

module.exports = async ({params, context, logger}) => {
 const { projectId, playerId, accessToken } = context;
 const inventory = new InventoryApi({accessToken});
 const result = await inventory.getPlayerInventory({projectId, playerId});
 return result.data;
}
```

如果要使用 `serviceToken`，同样的脚本将如下所示：

#### JavaScript##javascript

```js
// Player inventory
const { InventoryApi } = require("@unity-services/economy-2.4");

module.exports = async ({params, context, logger}) => {
const { projectId, playerId } = context;
const inventory = new InventoryApi(context);
const result = await inventory.getPlayerInventory({projectId, playerId});
return result.data;
}
```

如需了解这两个令牌之间差异的深入说明，请参阅[服务和访问令牌](./token-support.md)。

## 脚本参数##script-parameters

脚本参数可以在脚本正文外部或内部定义。

> **Note:**
>
> **注意**：脚本内参数提供了一种比声明参数更简单的替代方法，但如果您尝试在不使用 Deployment 包的情况下更新脚本内参数，Unity Dashboard 目前不会解析这些参数。

### 脚本内参数##in-script-parameters

添加脚本内参数的方法是导出 `params` 对象，此对象中包含每个参数名称作为键，参数类型作为值。

> **Important:**
>
> **重要**：在设置参数之前，必须分配 `module.exports` 属性。

例如：

#### JavaScript##javascript

```js
module.exports.params = { "echo" : "Boolean" }
```

或者，如果要指定某个参数为必需参数，可以指定一个同时包含 `type` 和 `required` 属性的对象。

#### JavaScript##javascript

```js
module.exports = async ({ params, context, logger }) => {
    return {
        "value": params["aParam"]
    };
};

module.exports.params = { "aParam" : { "type": "String", "required": true } }
```

默认情况下，参数不是必需参数。

两种格式可以根据需要组合使用：

#### JavaScript##javascript

```js
module.exports = async ({ params, context, logger }) => {
    var value = params["echo"] ? params["aParam"] : "default";
    return {
        "value": value
    };
};

module.exports.params = {
  "echo" : "Boolean",
  "aParam" : { "type": "String", "required": true }
 }
```

## async/await##asyncawait

主脚本函数可以是异步函数。这意味着函数可以等待承诺，从而允许在脚本中使用其他 Unity 游戏服务 SDK。请查看 Mozilla 文档中对[承诺](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Promises)的描述。

一个简单的示例如下所示：

### JavaScript##javascript

```js
// Player inventory
const { InventoryApi } = require("@unity-services/economy-2.4");

module.exports = async ({params, context, logger}) => {
 const { projectId, playerId } = context;
 const inventory = new InventoryApi(context);
 const result = await inventory.getPlayerInventory({projectId, playerId});
 return result.data;
}
```

> **Note:**
>
> **注意**：Cloud Code 客户端调用始终为异步调用，包括常规（同步）脚本和异步脚本。

## 其他包##additional-packages

脚本可以导入带有 `import` 或 `require` 关键字的本地脚本或外部包。

如需查看可导入内容的完整列表，请参阅[可用包](../reference/available-libraries)。

### JavaScript##javascript

```js
const _ = require("lodash-4.17");

module.exports = async () => {
  return _.random(1, 6);
};
```

> **Note:**
>
> **注意**：必需的包必须在导出的函数外部声明。

## 捆绑##bundling

> **Note:**
>
> **注意**：Unity Dashboard 和 CLI 目前不完全支持捆绑脚本。

脚本捆绑是在 Unity 编辑器中使用 Deployment 包启用的一项功能，可帮助在本地管理脚本、解锁其他工作流程，以及允许在多个脚本之间共享通用功能。如需详细了解如何生成捆绑的脚本，请参阅 [JS 捆绑包](./write-scripts/unity-editor.md#js-bundles)文档。

### JavaScript##javascript

```js

const lib = require("./lib");

module.exports = async () => {
  return lib.helloWorld();
};

module.exports.bundled = true;
```

## 输出##output

脚本输出可以包含以下类型：

### JavaScript##javascript

* `string`
  ```js
  module.exports = async () => {
    return "hello world";
  };
  ```
* `boolean`
  ```js
  module.exports = async () => {
    return true;
  };
  ```
* `number`
  ```js
  module.exports = async () => {
    return 3.14;
  };
  ```
* `object`
  ```js
  module.exports = async () => {
    return {
      message: "hello world",
      success: true
    };
  };
  ```

成功响应以 JSON 格式从 API 返回，结构如下：

```text
{
  "output": <SCRIPT OUTPUT>
}
```

### 错误输出##error-output

当某些原因（例如，服务请求失败）导致调用失败时，脚本可能会抛出错误。

在脚本中捕获错误可以确定应如何处理失败。

#### JavaScript##javascript

```js
module.exports = async ({logger}) => {
  try {
    let result = service.call("example");
    return result;
  } catch (err) {
    logger.error("Something went wrong!", {"error.message": err.message});
    throw err;
  }
};
```

错误响应以 JSON 格式从 API 返回，并提供错误详细信息，包括错误类型、错误消息以及旨在帮助确定错误原因的堆栈跟踪。

```text
{
  "type": "problems/invocation",
  "title": "Unprocessable Entity",
  "status": 422,
  "detail": "Invocation Error",
  "instance": null,
  "code": 9009,
  "details": [
      {
          "name": "ReferenceError",
          "message": "service is not defined",
          "stackTrace": [
              "at module.exports (example-test.js:1:26)"
          ]
      }
  ]
}
```
