

终止支持通知：2026 年 5 月 20 日， AWS 将终止对的支持。 AWS SimSpace Weaver 2026 年 5 月 20 日之后，您将无法再访问 SimSpace Weaver 控制台或 SimSpace Weaver 资源。有关更多信息，请参阅[AWS SimSpace Weaver 终止支持](https://docs.aws.amazon.com/simspaceweaver/latest/userguide/simspaceweaver-end-of-support.html)。

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# AWS SimSpace Weaver 应用程序 SDK
<a name="working-with_app-sdk"></a>

A SimSpace Weaver pp SDK 提供了 APIs 可用于控制模拟中的实体和响应 SimSpace Weaver 事件的功能。其中包括以下命名空间：
+ **API** – API 的核心定义及其用途

链接到以下库：
+ `libweaver_app_sdk_cxx_v1_full.so`

**重要**  
当您在 AWS 云中运行应用程序时，该库可用于动态链接。您无需将其与应用程序一起上传。

**注意**  
 SimSpace Weaver 应用程序 SDK APIs 控制仿真中的数据。 APIs它们与 SimSpace Weaver 服务是分开的 APIs，后者控制您的 SimSpace Weaver 服务资源（例如模拟、应用程序和时钟）。 AWS有关更多信息，请参阅 [SimSpace Weaver API 参考资料](api-reference.md)。

**Topics**
+ [API 方法返回一个 Result](working-with_app-sdk_return-result.md)
+ [在顶层与应用程序 SDK 交互](working-with_app-sdk_top-level.md)
+ [模拟管理](working-with_app-sdk_sim.md)
+ [订阅](working-with_app-sdk_sub.md)
+ [实体](working-with_app-sdk_ent.md)
+ [实体事件](working-with_app-sdk_events.md)
+ [Result 和错误处理](working-with_app-sdk_result.md)
+ [泛型和域类型](working-with_app-sdk_generics.md)
+ [其他应用程序 SDK 操作](working-with_app-sdk_misc.md)

# API 方法返回一个 Result
<a name="working-with_app-sdk_return-result"></a>

大多数 SimSpace Weaver API 函数都有返回类型`Aws::WeaverRuntime::Result<T>`。如果函数执行成功，则 `Result` 会包含 `T`。否则，将`Result``Aws::WeaverRuntime::ErrorCode`包含表示错误代码的 Rust App SDK.

**Example 示例**  

```
Result<Transaction> BeginUpdate(Application& app)
```

此方法：
+ 如果 `BeginUpdate()` 执行成功，则返回 `Transaction`。
+ 如果 `BeginUpdate()` 失败，则返回 `Aws::WeaverRuntime::ErrorCode`。

# 在顶层与应用程序 SDK 交互
<a name="working-with_app-sdk_top-level"></a>

**生命周期**
+  SimSpace Weaver 应用程序 SDK 管理应用程序的生命周期。您无需读取或写入应用程序的生命周期状态。

**分区**
+ 使用 `Result <PartitionSet> AssignedPartitions(Transaction& txn);` 可获取拥有的分区。
+ 使用 `Result <PartitionSet> AllPartitions(Transaction& txn);` 可获取模拟中的所有分区。

# 模拟管理
<a name="working-with_app-sdk_sim"></a>

本节介绍适用于常见模拟管理任务的解决方案。

**Topics**
+ [启动模拟。](working-with_app-sdk_sim_start.md)
+ [更新模拟](working-with_app-sdk_sim_update.md)
+ [终止模拟](working-with_app-sdk_sim_terminate.md)

# 启动模拟。
<a name="working-with_app-sdk_sim_start"></a>

使用 `CreateApplication()` 可创建应用程序。

**Example 示例**  

```
Result<Application> applicationResult = Api::CreateApplication();

if (!applicationResult)
{
    ErrorCode errorCode = WEAVERRUNTIME_EXPECT_ERROR(applicationResult);

    std::cout << "Failed to create application. Error code " <<
        static_cast<std::underlying_type_t<ErrorCode>>(errorCode) <<
        " Last error message "<< Api::LastErrorMessage() << ".";

    return 1;
}

/**
* Run simulation
*/
RunSimulation(std::move(applicationResult.assume_value()));
```

# 更新模拟
<a name="working-with_app-sdk_sim_update"></a>

使用以下 `BeginUpdate` 函数可更新应用程序：
+ `Result<Transaction> BeginUpdate(Application& app)`
+ `Result<bool> BeginUpdateWillBlock(Application& app)` – 告诉您会不会阻止 `BeginUpdate()`。

使用 `Result<void> Commit(Transaction& txn)` 可提交更改：

**Example 示例**  

```
Result<void> AppDriver::RunSimulation(Api::Application app) noexcept
{
    while (true)
    {
        {
            bool willBlock;

            do
            {
                WEAVERRUNTIME_TRY(willBlock, Api::BeginUpdateWillBlock(m_app));
            } while (willBlock);
        }

        WEAVERRUNTIME_TRY(Transaction transaction, Api::BeginUpdate(app));

        /**
         * Simulate app.
         */
        WEAVERRUNTIME_TRY(Simulate(transaction));
        WEAVERRUNTIME_TRY(Api::Commit(std::move(transaction)));
    }

    return Success();
}
```

# 终止模拟
<a name="working-with_app-sdk_sim_terminate"></a>

使用 `Result<void> DestroyApplication(Application&& app)` 可终止应用程序和模拟。

从对 `BeginUpdateWillBlock()` 或 `BeginUpdate()` 的调用收到 `ErrorCode::ShuttingDown` 时，其他应用程序发现模拟正在关闭。当应用程序收到时 `ErrorCode::ShuttingDown` 时，可以调用 `Result<void> DestroyApplication(Application&& app)` 来自行终止。

**Example 示例**  

```
Result<void> AppDriver::EncounteredAppError(Application&& application) noexcept
{
    const ErrorCode errorCode = WEAVERRUNTIME_EXPECT_ERROR(runAppResult);

    switch (errorCode)
    {
    case ErrorCode::ShuttingDown:
        {
            // insert custom shutdown process here.

            WEAVERRUNTIME_TRY(Api::DestroyApplication(std::move(application)));
            return Success();
        }
    default:
        {
            OnAppError(errorCode);
            return errorCode;
        }
    }
}
```

**重要**  
只能在 `Result<void> DestroyApplication(Application&& app)` 之后调用 `Api::Commit()`。在更新过程中销毁应用程序可能会导致未定义的行为。

**重要**  
您必须在程序退出之前调用 `DestroyApplication()`，以确保应用程序报告为成功终止。  
程序退出时未能调用 `DestroyApplication()` 将导致状态报告为 `FATAL`。

# 订阅
<a name="working-with_app-sdk_sub"></a>

您可以创建具有订阅区域和域 ID 的订阅。域 ID 表示拥有该订阅区域的域。`BoundingBox2F32` 描述订阅区域。使用以下函数可创建订阅：

```
Result<SubscriptionHandle> CreateSubscriptionBoundingBox2F32(Transaction& txn, DomainId id, const BoundingBox2F32& boundingBox)
```

**Example 示例**  

```
Result<void> CreateSubscriptionInSpatialDomain(Transaction& transaction)
{
    WEAVERRUNTIME_TRY(Api::PartitionSet partitionSet, Api::AllPartitions(transaction)); 
    
    Api::DomainId spatialDomainId;

    for (const Api::Partition& partition : partitionSet.partitions)
    {
        if (partition.domain_type == Api::DomainType::Spatial)
        {
            /**
            * Get the spatial domain ID.
            */
            spatialDomainId = partition.domain_id;
            break;
        }
    }
    
    constexpr Api::BoundingBox2F32 subscriptionBounds { 
        /* min */ { /* x */ 0, /* y */ 0 }, 
        /* max */ { /* x */ 1000, /* y */ 1000 } }

    WEAVERRUNTIME_TRY(
        Api::SubscriptionHandle subscriptionHandle,
        Api::CreateSubscriptionBoundingBox2F32(
        transaction,
        spatialDomainId,
        subscriptionBounds));
        
    return Success();
}
```

您可以使用 `CreateSubscriptionBoundingBox2F32()` 返回的 `Api::SubscriptionHandle` 来修改订阅。您可以将其作为参数传递给以下函数：

```
Result<void> ModifySubscriptionBoundingBox2F32(Transaction& txn, SubscriptionHandle handle, const BoundingBox2F32& boundingBox)
```

```
Result<void> DeleteSubscription(Transaction& txn, SubscriptionHandle handle)
```

# 实体
<a name="working-with_app-sdk_ent"></a>

当实体进入应用程序`Api:Entity`的订阅区域时`CreateEntity()`，您可以使用从或从所有权变更事件`Result<Api::Entity>`返回的 and（有关更多信息，请参阅[实体事件](working-with_app-sdk_events.md)）。`Store` `Load` APIs 我们建议您跟踪您的`Api::Entity`对象，以便可以将它们与这些对象一起使用 APIs。

**Topics**
+ [创建实体](working-with_app-sdk_ent_create.md)
+ [将实体转移到空间域](working-with_app-sdk_ent_transfer.md)
+ [写入和读实体字段数据](working-with_app-sdk_ent_readwrite.md)
+ [存储实体的位置](working-with_app-sdk_ent_store-position.md)
+ [存储实体的位置](working-with_app-sdk_ent_load-position.md)

# 创建实体
<a name="working-with_app-sdk_ent_create"></a>

使用 `CreateEntity()` 可创建实体。您可以定义传递给此函数的 `Api::TypeId` 的含义。

```
Namespace
{
    constexpr Api::TypeId k_entityTypeId { /* value */ 512 };
}

Result<void> CreateEntity(Transaction& transaction)
{
    WEAVERRUNTIME_TRY(
        Api::Entity entity,
        Api::CreateEntity(
            transaction, Api::BuiltinTypeIdToTypeId(k_entityTypeId )));
}
```

**注意**  
`Api::BuiltinTypeId` 的值 0-511 是保留值。你的实体 TypeID （在本例`k_entityTypeId`中）的值必须等于 512 或更高。

# 将实体转移到空间域
<a name="working-with_app-sdk_ent_transfer"></a>

自定义应用程序或服务应用程序创建实体后，必须将实体转移到空间域中，实体才能在模拟中以空间形式存在。空间域中的实体可以由其他应用程序读取，并且可以由空间应用程序更新。使用 `ModifyEntityDomain()` API 可将实体转移到空间域中。

```
AWS_WEAVERRUNTIME_API Result<void> ModifyEntityDomain(Transaction& txn, const Entity& entity, DomainId domainId) noexcept;
```

如果 `DomainId` 与调用的应用程序分配的 `Partition` 不匹配，则 `DomainId` 必须为 `DomainType::Spatial` `Domain`。在 `Commit(Transaction&&)` 过程中，所有权会转移到新的 `Domain`。参数

`txn`  
当前 `Transaction`。

`entity`  
更改 `Entity` 的目标 `Domain`。

`domainId`  
`Entity` 的目标 `Domain` 的 `DomainId`。

如果已成功更改实体域，此 API 会返回 `Success`。

# 写入和读实体字段数据
<a name="working-with_app-sdk_ent_readwrite"></a>

所有实体数据字段都是 BLOB 类型。您最多可以向实体写入 1,024 字节数据。我们建议您尽量减小 BLOB，因为 BLOB 越大性能越低。当你写入 blob 时，你会传递 SimSpace Weaver 一个指向数据及其长度的指针。从 BLOB 中读取时， SimSpace Weaver 会向您提供一个指针和一个读取长度。在应用程序调用 `Commit()` 之前，必须完成所有读取操作。当应用程序调用 `Commit()` 时，读取调用返回的指针会失效。

**重要**  
不支持在 `Commit()` 之后从缓存的 BLOB 指针中读取，这可能会导致模拟失败。
不支持写入读取调用返回的 BLOB 指针，这可能会导致模拟失败。

**Topics**
+ [存储实体的字段数据](working-with_app-sdk_ent_readwrite_store.md)
+ [加载实体的字段数据](working-with_app-sdk_ent_readwrite_load.md)
+ [加载已移除实体的字段数据](working-with_app-sdk_ent_readwrite_load-removed.md)

# 存储实体的字段数据
<a name="working-with_app-sdk_ent_readwrite_store"></a>

以下示例演示如何存储（写入 State Fabric）应用程序拥有的实体的字段数据。这些示例使用以下函数：

```
AWS_WEAVERRUNTIME_API Result<void> StoreEntityField(
    Transaction& txn,
    const Entity& entity,
    TypeId keyTypeId,
    FieldIndex index,
    std::int8_t* src,
    std::size_t length) noexcept;
```

`Api::TypeId keyTypeId` 参数表示传入数据的数据类型。

`Api::TypeId keyTypeId` 参数应从 `Api::BuiltinTypeId` 接收相应的 `Api::TypeId`。如果没有适当的转换，则可以使用 `Api::BuiltinTypeId::Dynamic`。

对于复杂的数据类型，请使用 `Api::BuiltInTypeId::Dynamic`。

**注意**  
`FieldIndex index` 的值必须大于 0。0 是为索引键保留的值（请参阅 `StoreEntityIndexKey()`）。

**Example 使用基元数据类型的示例**  

```
namespace
{
    constexpr Api::FieldIndex k_isTrueFieldId { /* value */ 1 };
}

Result<void> SetEntityFields(
    Api::Entity& entity, 
    Transaction& transaction)
{
    bool value = true;
    
    auto* src = reinterpret_cast<std::int8_t*>(value);
    size_t length = sizeof(*value);
    
    WEAVERRUNTIME_TRY(Api::StoreEntityField(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(
            Aws::WeaverRuntime::Api::BuiltinTypeId::Bool),
        k_isTrueFieldId,
        src,
        length));
}
```

**Example 使用 a 的示例 struct 来保存数据**  

```
namespace
{
    constexpr Api::FieldIndex k_dataFieldId { /* value */ 1 };
}

struct Data
{
    bool boolData;
    float floatData;
};

Result<void> SetEntityFields(
    Api::Entity& entity, 
    Transaction& transaction)
{
    Data data = { /* boolData */ false, /* floatData */ -25.93 };
    
    auto* src = reinterpret_cast<std::int8_t*>(data);
    size_t length = sizeof(*data);
    
    WEAVERRUNTIME_TRY(Api::StoreEntityField(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(
            Aws::WeaverRuntime::Api::BuiltinTypeId::Dynamic),
        k_dataFieldId,
        src,
        length));
}
```

# 加载实体的字段数据
<a name="working-with_app-sdk_ent_readwrite_load"></a>

以下示例演示如何加载（从 State Fabric 读取）实体的字段数据。这些示例使用以下函数：

```
Result<std::size_t> LoadEntityField(
    Transaction& txn,
    const Entity& entity,
    TypeId keyTypeId,
    FieldIndex index,
    std::int8_t** dest) noexcept;
```

`Api::TypeId keyTypeId` 参数应从 `Api::BuiltinTypeId` 接收相应的 `Api::TypeId`。如果没有适当的转换，则可以使用 `Api::BuiltinTypeId::Dynamic`。

**注意**  
`FieldIndex` 索引的值必须大于 0。0 是为索引键保留的值（请参阅 `StoreEntityIndexKey()`）。

**Example 使用基元数据类型的示例**  

```
namespace
{
    constexpr Api::FieldIndex k_isTrueFieldId { /* value */ 1 };
}

Result<void> LoadEntityFields(
    Api::Entity& entity, 
    Transaction& transaction)
{
    std::int8_t* dest = nullptr;
    
    WEAVERRUNTIME_TRY(Api::LoadEntityField(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(
            Aws::WeaverRuntime::Api::BuiltinTypeId::Bool),
        k_isTrueFieldId,
        &dest));
    
    bool isTrueValue = *reinterpret_cast<bool*>(dest);
}
```

**Example 使用 a 的示例 struct 来保存数据**  

```
namespace
{
    constexpr Api::FieldIndex k_dataFieldId { /* value */ 1 };
}

struct Data
{
    bool boolData;
    float floatData;
};

Result<void> LoadEntityFields(
    Api::Entity& entity, 
    Transaction& transaction)
{
    std::int8_t* dest = nullptr;
    
    WEAVERRUNTIME_TRY(Api::LoadEntityField(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(
            Aws::WeaverRuntime::Api::BuiltinTypeId::Dynamic),
        k_dataFieldId,
        &dest));
    
    Data dataValue = *reinterpret_cast<Data*>(dest);
}
```

# 加载已移除实体的字段数据
<a name="working-with_app-sdk_ent_readwrite_load-removed"></a>

对于已经从应用程序所有权和订阅区域中移除的实体，您无法加载（从 State Fabric 读取）实体字段数据。以下示例会导致错误，因为 `Api::ChangeListAction::Remove` 会导致它调用实体的 `Api::LoadIndexKey()`。第二个示例显示了直接在应用程序中存储和加载实体数据的正确方法。

**Example 错误代码示例**  

```
Result<void> ProcessSubscriptionChanges(Transaction& transaction)
{
    /* ... */
    
    WEAVERRUNTIME_TRY(Api::SubscriptionChangeList subscriptionChangeList, 
        Api::AllSubscriptionEvents(transaction));
    
    for (const Api::SubscriptionEvent& event : 
        subscriptionChangeList.changes)
    {
        switch (event.action)
        {
        case Api::ChangeListAction::Remove:
            {
                std::int8_t* dest = nullptr;
    
                /**
                 * Error!
                 * This calls LoadEntityIndexKey on an entity that
                 * has been removed from the subscription area.
                 */
                WEAVERRUNTIME_TRY(Api::LoadEntityIndexKey(
                    transaction,
                    event.entity,
                    Api::BuiltinTypeIdToTypeId(
                        Api::BuiltinTypeId::Vector3F32),
                    &dest));
    
                AZ::Vector3 position = 
                    *reinterpret_cast<AZ::Vector3*>(dest);
                break;
            }
        }
 
    }

    /* ... */
}
```

**Example 在应用程序中存储和加载实体数据的正确方法示例**  

```
Result<void> ReadAndSaveSubscribedEntityPositions(Transaction& transaction)
{
    static std::unordered_map<Api::EntityId, AZ::Vector3> 
        positionsBySubscribedEntity;

    WEAVERRUNTIME_TRY(Api::SubscriptionChangeList subscriptionChangeList, 
        Api::AllSubscriptionEvents(transaction));

    for (const Api::SubscriptionEvent& event : 
        subscriptionChangeList.changes)
    {
        switch (event.action)
        {
        case Api::ChangeListAction::Add:
            {
                std::int8_t* dest = nullptr;

                /**
                 * Add the position when the entity is added.
                 */
                WEAVERRUNTIME_TRY(Api::LoadEntityIndexKey(
                    transaction,
                    event.entity,
                    Api::BuiltinTypeIdToTypeId(
                        Api::BuiltinTypeId::Vector3F32),
                    &dest));

                AZ::Vector3 position = 
                    *reinterpret_cast<AZ::Vector3*>(dest);
                positionsBySubscribedEntity.emplace(
                    event.entity.descriptor->id, position);

                break;
            }
        case Api::ChangeListAction::Update:
            {
                std::int8_t* dest = nullptr;

                /**
                 * Update the position when the entity is updated.
                 */
                WEAVERRUNTIME_TRY(Api::LoadEntityIndexKey(
                    transaction,
                    event.entity,
                    Api::BuiltinTypeIdToTypeId(
                        Api::BuiltinTypeId::Vector3F32),
                    &dest));

                AZ::Vector3 position = 
                    *reinterpret_cast<AZ::Vector3*>(dest);
                positionsBySubscribedEntity[event.entity.descriptor->id] = 
                    position;

                break;
            }
        case Api::ChangeListAction::Remove:
            {
                /**
                 * Load the position when the entity is removed.
                 */
                AZ::Vector3 position = positionsBySubscribedEntity[
                    event.entity.descriptor->id];

                /**
                 * Do something with position...
                 */
                break;
            }
        }
    }
    
    /* ... */
}
```

# 存储实体的位置
<a name="working-with_app-sdk_ent_store-position"></a>

您可以使用整数数据结构存储（写入 State Fabric）实体的位置。这些示例使用以下函数：

```
Result<void> StoreEntityIndexKey(
    Transaction& txn, 
    const Entity& entity, 
    TypeId keyTypeId, 
    std::int8_t* src, 
    std::size_t length)
```

**注意**  
您必须将 `Api::BuiltinTypeId::Vector3F32` 提供给 `Api::StoreEntityIndexKey()`，如以下示例所示。

**Example 使用数组表示位置的示例**  

```
Result<void> SetEntityPositionByFloatArray(
    Api::Entity& entity, 
    Transaction& transaction)
{
    std::array<float, 3> position = { /* x */ 25, /* y */ 21, /* z */ 0 };
    
    auto* src = reinterpret_cast<std::int8_t*>(position.data());
    std::size_t length = sizeof(position);
    
    WEAVERRUNTIME_TRY(Api::StoreEntityIndexKey(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(Api::BuiltinTypeId::Vector3F32),
        src,
        length));
}
```

**Example 使用 a 的示例 struct 来代表立场**  

```
struct Position 
{
   float x;
   float y;
   float z;
};

Result<void> SetEntityPositionByStruct(
    Api::Entity& entity, 
    Transaction& transaction)
{
    Position position = { /* x */ 25, /* y */ 21, /* z */ 0 };
    
    auto* src = reinterpret_cast<std::int8_t*>(&position);
    std::size_t length = sizeof(position);
    
    WEAVERRUNTIME_TRY(Api::StoreEntityIndexKey(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(Api::BuiltinTypeId::Vector3F32),
        src,
        length));
}
```

# 存储实体的位置
<a name="working-with_app-sdk_ent_load-position"></a>

您可以使用整数数据结构加载（从 State Fabric 读取）实体的位置。这些示例使用以下函数：

**注意**  
您必须将 `Api::BuiltinTypeId::Vector3F32` 提供给 `Api::LoadEntityIndexKey()`，如以下示例所示。

**Example 使用数组表示位置的示例**  

```
Result<void> GetEntityPosition(Api::Entity& entity, 
    Transaction& transaction)
{
    std::int8_t* dest = nullptr;
    
    WEAVERRUNTIME_TRY(Aws::WeaverRuntime::Api::LoadEntityIndexKey(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(
            Aws::WeaverRuntime::Api::BuiltinTypeId::Vector3F32),
        &dest));
        
    std::array<float, 3> position = 
        *reinterpret_cast<std::array<float, 3>*>(dest);
}
```

**Example 使用 a 的示例 struct 来代表立场**  

```
struct Position 
{struct
   float x;
   float y;
   float z;
};

Result<void> GetEntityPosition(Api::Entity& entity, Transaction& transaction)
{
    std::int8_t* dest = nullptr;
    
    WEAVERRUNTIME_TRY(Aws::WeaverRuntime::Api::LoadEntityIndexKey(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(
            Aws::WeaverRuntime::Api::BuiltinTypeId::Vector3F32),
        &dest));
        
    Position position = *reinterpret_cast<Position*>(dest);
}
```

# 实体事件
<a name="working-with_app-sdk_events"></a>

您可以使用 SimSpace Weaver 应用程序 SDK 中的以下函数来获取所有权和订阅事件：
+ `Result<OwnershipChangeList> OwnershipChanges(Transaction& txn) `
+ `Result<SubscriptionChangeList> AllSubscriptionEvents(Transaction& txn) `

如果您需要回调驱动的实体事件处理，则可以使用 SimSpace Weaver 演示框架。有关更多信息，请参阅以下网站头文件：
+ `sdk-folder/packaging-tools/samples/ext/DemoFramework/include/DemoFramework/EntityEventProcessor.h`

您还可以创建自己的实体事件处理。

**Topics**
+ [遍历所拥有实体的事件](working-with_app-sdk_events_own.md)
+ [遍历所订阅实体的事件](working-with_app-sdk_events_sub.md)
+ [遍历实体的所有权更改事件](working-with_app-sdk_events_change.md)

# 遍历所拥有实体的事件
<a name="working-with_app-sdk_events_own"></a>

使用 `OwnershipChanges()` 可获取所拥有实体（应用程序所有权区域中的实体）的事件列表。该函数具有以下签名：

```
Result<OwnershipChangeList> OwnershipChanges(Transaction& txn)
```

然后使用循环遍历各个实体，如以下示例所示。

**Example 示例**  

```
WEAVERRUNTIME_TRY(Result<Api::OwnershipChangeList> ownershipChangesResult, Api::OwnershipChanges(transaction));

for (const Api::OwnershipChange& event : ownershipChangeList.changes)
{
    Api::Entity entity = event.entity;
    Api::ChangeListAction action = event.action;

    switch (action)
    {
    case Api::ChangeListAction::None:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Remove:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Add:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Update:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Reject:
        // insert code to handle the event
        break;
    }
}
```

**事件类型**
+ `None` – 实体位于该区域内，未修改其位置和字段数据。
+ `Remove` – 实体已从该区域内移除。
+ `Add` – 实体已添加到该区域内。
+ `Update` – 实体位于该区域内且已被修改。
+ `Reject` – 应用程序未能将实体从该区域内移除。

**注意**  
如果发生 `Reject` 事件，应用程序将在下一个刻度再次尝试转移。

# 遍历所订阅实体的事件
<a name="working-with_app-sdk_events_sub"></a>

使用 `AllSubscriptionEvents()` 可获取所订阅实体（应用程序订阅区域中的实体）的事件列表。该函数具有以下签名：

```
Result<SubscriptionChangeList> AllSubscriptionEvents(Transaction& txn)
```

然后使用循环遍历各个实体，如以下示例所示。

**Example 示例**  

```
WEAVERRUNTIME_TRY(Api::SubscriptionChangeList subscriptionChangeList, Api::AllSubscriptionEvents(transaction));

for (const Api::SubscriptionEvent& event : subscriptionChangeList.changes)
{
    Api::Entity entity = event.entity;
    Api::ChangeListAction action = event.action;

    switch (action)
    {
    case Api::ChangeListAction::None:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Remove:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Add:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Update:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Reject:
        // insert code to handle the event
        break;
    }
}
```

**事件类型**
+ `None` – 实体位于该区域内，未修改其位置和字段数据。
+ `Remove` – 实体已从该区域内移除。
+ `Add` – 实体已添加到该区域内。
+ `Update` – 实体位于该区域内且已被修改。
+ `Reject` – 应用程序未能将实体从该区域内移除。

**注意**  
如果发生 `Reject` 事件，应用程序将在下一个刻度再次尝试转移。

# 遍历实体的所有权更改事件
<a name="working-with_app-sdk_events_change"></a>

要获取实体在所有权区域和订阅区域之间移动的事件，请比较实体所有权和订阅事件当前与之前的变化。

您可以通过阅读以下 API 来处理这些事件：
+ `Api::SubscriptionChangeList`
+ `Api::OwnershipEvents`

然后，您可以将更改与之前存储的数据进行比较。

以下示例说明了如何处理实体所有权更改事件。此示例假设，对于在已订阅实体和拥有的实体（任一方向）之间过渡的实体，所有权remove/add event occurs first followed by the subscription remove/add事件将在下一个勾选中显示。

**Example 示例**  

```
Result<void> ProcessOwnershipEvents(Transaction& transaction)
{
    using EntityIdsByAction =
        std::unordered_map<Api::ChangeListAction, 
        std::vector<Api::EntityId>>;
    using EntityIdSetByAction =
        std::unordered_map<Api::ChangeListAction, 
        std::unordered_set<Api::EntityId>>;
   
    static EntityIdsByAction m_entityIdsByPreviousOwnershipAction;
    
    EntityIdSetByAction entityIdSetByAction;
   
    /**
     * Enumerate Api::SubscriptionChangeList items 
     * and store Add and Remove events.
     */ 
    WEAVERRUNTIME_TRY(Api::SubscriptionChangeList subscriptionEvents, 
        Api::AllSubscriptionEvents(transaction));
   
    for (const Api::SubscriptionEvent& event : subscriptionEvents.changes)
    {
        const Api::ChangeListAction action = event.action;
    
        switch (action)
        {
        case Api::ChangeListAction::Add:
        case Api::ChangeListAction::Remove:
    
            {
                entityIdSetByAction[action].insert(
                    event.entity.descriptor->id);
                break;
            }
        case Api::ChangeListAction::None:
        case Api::ChangeListAction::Update:
        case Api::ChangeListAction::Reject:
            {
                break;
            }
        }
    }
    
    EntityIdsByAction entityIdsByAction;
    
    /**
     * Enumerate Api::OwnershipChangeList items 
     * and store Add and Remove events.
     */
    
    WEAVERRUNTIME_TRY(Api::OwnershipChangeList ownershipChangeList, 
        Api::OwnershipChanges(transaction));
   
    for (const Api::OwnershipChange& event : ownershipChangeList.changes)
    {
        const Api::ChangeListAction action = event.action;
    
        switch (action)
        {
        case Api::ChangeListAction::Add:
        case Api::ChangeListAction::Remove:
            {
                entityIdsByAction[action].push_back(
                    event.entity.descriptor->id);
                break;
            }
        case Api::ChangeListAction::None:
        case Api::ChangeListAction::Update:
        case Api::ChangeListAction::Reject:
            {
                break;
            }
        }
    
    }
      
    std::vector<Api::EntityId> fromSubscribedToOwnedEntities;
    std::vector<Api::EntityId> fromOwnedToSubscribedEntities;
   
    /**
     * Enumerate the *previous* Api::OwnershipChangeList Remove items
     * and check if they are now in 
     * the *current* Api::SubscriptionChangeList Add items.
     *
     * If true, then that means 
     * OnEntityOwnershipChanged(bool isOwned = false)
     */ 
    for (const Api::EntityId& id : m_entityIdsByPreviousOwnershipAction[
        Api::ChangeListAction::Remove])
    {
        if (entityIdSetBySubscriptionAction[
            Api::ChangeListAction::Add].find(id) !=
                entityIdSetBySubscriptionAction[
                Api::ChangeListAction::Add].end())
        {
            fromOwnedToSubscribedEntities.push_back(id);
        }
    }
    
   
    /**
     * Enumerate the *previous* Api::OwnershipChangeList Add items
     * and check if they are now in 
     * the *current* Api::SubscriptionChangeList Remove items.
     *
     * If true, then that means 
     * OnEntityOwnershipChanged(bool isOwned = true)
     */ 
    for (const Api::EntityId& id : m_entityIdsByPreviousOwnershipAction[
        Api::ChangeListAction::Add])
    {
        if (entityIdSetBySubscriptionAction[
            Api::ChangeListAction::Remove].find(id) !=
            
                entityIdSetBySubscriptionAction[
                Api::ChangeListAction::Remove].end())
        {
            fromSubscribedToOwnedEntities.push_back(id);
        }
    }
    
    m_entityIdsByPreviousOwnershipAction = entityIdsByOwnershipAction;
    
    return Success();
}
```

# Result 和错误处理
<a name="working-with_app-sdk_result"></a>

`Aws::WeaverRuntime::Result<T>` 类使用第三方 `Outcome` 库。您可以使用以下模式来检查 API 调用返回的 `Result` 和捕获错误。

```
void DoBeginUpdate(Application& app)
{
    Result<Transaction> transactionResult = Api::BeginUpdate(app);
    
    if (transactionResult)
    {
        Transaction transaction = 
            std::move(transactionResult).assume_value();
        
        /**
         * Do things with transaction ...
         */
    }
    else
    {     
        ErrorCode errorCode = WEAVERRUNTIME_EXPECT_ERROR(transactionResult);
        /**
         * Macro compiles to:
         * ErrorCode errorCode = transactionResult.assume_error();
         */
    }
}
```

## Result 控制语句宏
<a name="working-with_app-sdk_result_macro"></a>

在具有返回类型 `Aws::WeaverRuntime::Result<T>` 的函数中，您可以使用 `WEAVERRUNTIME_TRY` 宏来代替之前的代码模式。该宏将执行传递给它的函数。如果传递的函数失败，该宏将使用封闭函数返回错误。如果传递的函数执行成功，则会执行到下一行。以下示例显示对之前 `DoBeginUpdate()` 函数的重写。此版本使用`WEAVERRUNTIME_TRY`宏而不是 if-else 控制结构。请注意，此函数的返回类型为 `Aws::WeaverRuntime::Result<void>`。

```
Aws::WeaverRuntime::Result<void> DoBeginUpdate(Application& app)
{
    /**
     * Execute Api::BeginUpdate() 
     * and return from DoBeginUpdate() if BeginUpdate() fails.
     * The error is available as part of the Result.
     */
    WEAVERRUNTIME_TRY(Transaction transaction, Api::BeginUpdate(m_app));
    
    /**
     * Api::BeginUpdate executed successfully.
     *
     * Do things here.
     */
    
    return Aws::Success();
}
```

如果 `BeginUpdate()` 失败，该宏会在失败时提前返回 `DoBeginUpdate()`。您可以使用 `WEAVERRUNTIME_EXPECT_ERROR` 宏从 `BeginUpdate()` 获取 `Aws::WeaverRuntime::ErrorCode`。以下示例显示了 `Update()` 函数在失败时如何调用 `DoBeginUpdate()` 和获取错误代码。

```
void Update(Application& app)
{
    Result<void> doBeginUpdateResult = DoBeginUpdate(app);
    
    if (doBeginUpdateResult)
    {
        /**
         * Successful.
         */
    }
    else
    {    
        /**
         * Get the error from Api::BeginUpdate().
         */ 
        ErrorCode errorCode = WEAVERRUNTIME_EXPECT_ERROR(doBeginUpdateResult);

    }
}
```

通过将 `Update()` 的返回类型更改为 `Aws::WeaverRuntime::Result<void>`，您可以将错误代码从 `BeginUpdate()` 提供给调用 `Update()` 的函数。您可以重复此过程，继续将错误代码发送到调用堆栈以下。

# 泛型和域类型
<a name="working-with_app-sdk_generics"></a>

 SimSpace Weaver 应用程序 SDK 提供单精度数据类型`Api::Vector2F32`和`Api::BoundingBox2F32`，以及双精`Api::Vector2F64`度和。`Api::BoundingBox2F64`这些数据类型是被动数据结构，没有便捷的方法。请注意，API 仅使用 `Api::Vector2F32` 和 `Api::BoundingBox2F32`。您可以使用这些数据类型来创建和修改订阅。

演 SimSpace Weaver 示框架提供了最小版本的 AzCore 数学库，其中包含`Vector3`和`Aabb`。有关更多信息，请参阅以下路径下的头文件：
+ `sdk-folder/packaging-tools/samples/ext/DemoFramework/include/AzCore/Math`

# 其他应用程序 SDK 操作
<a name="working-with_app-sdk_misc"></a>

**Topics**
+ [AllSubscriptionEvents 以及 OwnershipChanges 包含上次通话中的事件](working-with_app-sdk_misc_events-from-last-call.md)
+ [处理后解除读取锁 SubscriptionChangeList](working-with_app-sdk_misc_release-locks.md)
+ [创建用于测试的独立应用程序实例](working-with_app-sdk_misc_testing-app.md)

# AllSubscriptionEvents 以及 OwnershipChanges 包含上次通话中的事件
<a name="working-with_app-sdk_misc_events-from-last-call"></a>

对 `Api::AllSubscriptionEvents()` 和 `Api::OwnershipChanges()` 的调用的返回值包含上一个调用（**不是上一个刻度**）的事件。在以下示例中，`secondSubscriptionEvents` 和 `secondOwnershipChangeList` 为空，因为它们的函数会在第一个调用后立即调用。

如果您等待 10 个刻度，然后调用 `Api::AllSubscriptionEvents()` 和 `Api::OwnershipChanges()`，则它们的结果将既包含事件又包含最近 10 个刻度（不是最后一个刻度）内的变化。

**Example 示例**  

```
Result<void> ProcessOwnershipChanges(Transaction& transaction)
{
    WEAVERRUNTIME_TRY(
        Api::SubscriptionChangeList firstSubscriptionEvents,
        Api::AllSubscriptionEvents(transaction));
    WEAVERRUNTIME_TRY(
        Api::OwnershipChangeList firstOwnershipChangeList,
        Api::OwnershipChanges(transaction));
    
    WEAVERRUNTIME_TRY(
        Api::SubscriptionChangeList secondSubscriptionEvents,
        Api::AllSubscriptionEvents(transaction));
    WEAVERRUNTIME_TRY(
        Api::OwnershipChangeList secondOwnershipChangeList,
        Api::OwnershipChanges(transaction));
    
    /**
     * secondSubscriptionEvents and secondOwnershipChangeList are 
     * both empty because there are no changes since the last call.
     */
}
```

**注意**  
函数 `AllSubscriptionEvents()` 已实施，但函数 `SubscriptionEvents()` **未实施**。

# 处理后解除读取锁 SubscriptionChangeList
<a name="working-with_app-sdk_misc_release-locks"></a>

当您开始更新时，在其他分区中会有在前一个刻度提交的数据的共享内存段。这些共享内存段可能会被读取器锁定。在所有读取器都释放锁定之前，应用程序将无法完全提交。作为一项优化措施，应用程序应在处理 `Api::SubscriptionChangelist` 项目后调用 `Api::ReleaseReadLeases()` 来释放锁定。这样可以减少提交时的争用情况。在默认情况下，`Api::Commit()` 会释放读取租约，但最佳实践是在处理订阅更新后手动将其释放。

**Example 示例**  

```
Result<void> ProcessSubscriptionChanges(Transaction& transaction)
{
    WEAVERRUNTIME_TRY(ProcessSubscriptionChanges(transaction));
    
    /**
     * Done processing Api::SubscriptionChangeList items.
     * Release read locks. 
     */
        
    WEAVERRUNTIME_EXPECT(Api::ReleaseReadLeases(transaction));
    
    ...
}
```

# 创建用于测试的独立应用程序实例
<a name="working-with_app-sdk_misc_testing-app"></a>

在实际模拟中运行代码之前，您可以使用 `Api::CreateStandaloneApplication()` 来创建独立应用程序，以便测试应用程序逻辑。

**Example 示例**  

```
int main(int argc, char* argv[])
{
    Api::StandaloneRuntimeConfig config = { 
        /* run_for_seconds (the lifetime of the app) */ 3,
        /* tick_hertz (the app clock rate) */ 10 };
    
    Result<Application> applicationResult =
        Api::CreateStandaloneApplication(config);

    ...
}
```