Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/reference/kernel.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ bool set_int32_field(ark::kernel_module& kernel, const ark::guid_t& id, const st

`table*` and `row*` are borrowed pointers, with no independent ownership of entity data. Do not retain them after removing the row, clearing the table or destroying the owning entity.

### Custom unsigned values

`entity::add_custom_u_int32(name, value)` creates a named runtime `uint32_t` value separate from schema fields. After creation, `set_custom_u_int32(name, value)` updates that value across the full range from `0` to `UINT32_MAX`; read it with `get_custom_u_int32(name)`. Setting a missing name or a name containing another scalar type returns `false` without creating data, converting its type or changing the existing value, in both Debug and Release builds. Use `find_custom_data()` to check existence; a stored zero is a valid value.

`check-kernel-initialization` exercises these operations through the public `entity` interface, including other custom scalar controls. With applications, Kernel/Utility and examples enabled, `check-typed-examples` also verifies unsigned updates on entities created by the real Kernel/EntityOperations libraries.

## Register events

Class callbacks cover creation / destruction, field changes, table changes and container operations. Individual entities can also have application event handlers. Register common class callbacks with `add_common_class_event()`.
Expand Down
6 changes: 6 additions & 0 deletions docs/reference/kernel_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ bool set_int32_field(ark::kernel_module& kernel, const ark::guid_t& id, const st

`table*` 和 `row*` 是借用指针,不独立拥有实体数据。表格删除行、清空表格或销毁所属实体后,不要继续使用相关指针。

### 自定义无符号值

`entity::add_custom_u_int32(name, value)` 创建按名称访问的运行时 `uint32_t` 值,与元数据字段分开。创建后,`set_custom_u_int32(name, value)` 可在 `0` 到 `UINT32_MAX` 的完整范围内更新值,使用 `get_custom_u_int32(name)` 读取。名称不存在或对应其他标量类型时,设置操作在 Debug 和 Release 构建中均返回 `false`,不会创建数据、转换类型或修改已有值。使用 `find_custom_data()` 检查是否存在;已存储的零是有效值。

`check-kernel-initialization` 通过公开的 `entity` 接口验证这些操作,并检查其他自定义标量。启用应用、Kernel、Utility 和示例后,`check-typed-examples` 还会在真实 Kernel、EntityOperations 动态库创建的实体上验证无符号值更新。

## 注册事件

可以按类注册创建 / 销毁事件、字段变化、表格变化和容器操作,也可以为单个实体注册业务事件。公共类事件通过 `add_common_class_event()` 注册。
Expand Down
9 changes: 8 additions & 1 deletion src/plugins/kernel/entity_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,14 @@ bool entity_impl::set_custom_int32(const std::string& name, const int32_t value)
}

bool entity_impl::set_custom_u_int32(const std::string& name, const uint32_t value) {
return false;
auto* data_ptr = custom_data_list_.find_value(name);
if (data_ptr == nullptr || data_ptr->get_type() != data_type::dt_uint32)
{
return false;
}

data_ptr->set_u_int(value);
return true;
}

bool entity_impl::set_custom_int64(const std::string& name, const int64_t value) {
Expand Down
11 changes: 11 additions & 0 deletions tests/app/typed_examples.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Copyright (c) 2026 OpenArkStudio. Licensed under Apache-2.0. See LICENSE.
#include <concepts>
#include <cstdint>
#include <cstdlib>
#include <filesystem>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
#include <typeinfo>
Expand Down Expand Up @@ -111,6 +113,15 @@ int main(int argc, char* argv[]) {
require(gender == 1 || gender == 11, "Unexpected player gender");
edited += gender == 11;
require(player->get_custom_int32("test") == 111, "Custom data initialization changed");
require(player->add_custom_u_int32("unsigned_sequence", 0), "Custom uint32 creation failed");
for (const auto value : {std::numeric_limits<std::uint32_t>::max(), std::uint32_t{0}})
{
require(player->set_custom_u_int32("unsigned_sequence", value) &&
player->get_custom_u_int32("unsigned_sequence") == value,
"Public entity custom uint32 update failed through the real Kernel DSO");
}
require(!player->set_custom_u_int32("test", 7) && player->get_custom_int32("test") == 111,
"Custom uint32 update changed a signed field");
auto* items = player->find_table(ark::entity_meta_player::items());
require(items && items->first() &&
items->first()->get_int64(ark::entity_meta_item_bag::guid_index()) == 1111,
Expand Down
66 changes: 66 additions & 0 deletions tests/core/entity_config_copy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <limits>
#include <string>
#include <vector>

Expand Down Expand Up @@ -412,3 +413,68 @@ TEST_CASE("container configuration zero and rejected configurations preserve cre
CHECK(container->exist(empty->get_id()));
CHECK(world.kernel.get_static_entity(99)->get_int32("level") == 13);
}

TEST_CASE("public entity custom uint32 updates preserve the full unsigned range") {
configured_world world;
const std::shared_ptr<ark::entity> object = world.create(10, 0);
REQUIRE(object != nullptr);
REQUIRE(object->add_custom_u_int32("sequence", 0));
CHECK(object->get_custom_u_int32("sequence") == 0);
for (const auto value :
{std::numeric_limits<std::uint32_t>::max(), std::uint32_t{0x80000000}, std::uint32_t{1}, std::uint32_t{0}})
{
REQUIRE(object->set_custom_u_int32("sequence", value));
CHECK(object->get_custom_u_int32("sequence") == value);
CHECK(object->find_custom_data("sequence"));
}
}

TEST_CASE("public entity custom uint32 rejects missing names without creating data") {
configured_world world;
const std::shared_ptr<ark::entity> object = world.create(10, 0);
REQUIRE(object != nullptr);
CHECK_FALSE(object->find_custom_data("missing"));
CHECK_FALSE(object->set_custom_u_int32("missing", std::numeric_limits<std::uint32_t>::max()));
CHECK_FALSE(object->find_custom_data("missing"));
REQUIRE(object->add_custom_u_int32("missing", 7));
CHECK(object->get_custom_u_int32("missing") == 7);
REQUIRE(object->remove_custom_data("missing"));
CHECK_FALSE(object->set_custom_u_int32("missing", 0));
CHECK_FALSE(object->find_custom_data("missing"));
}

TEST_CASE("public entity custom uint32 rejects other scalar types without changing their values") {
configured_world world;
const std::shared_ptr<ark::entity> object = world.create(10, 0);
REQUIRE(object != nullptr);
REQUIRE(object->add_custom_bool("boolean", true));
REQUIRE(object->add_custom_int32("signed32", -17));
REQUIRE(object->add_custom_int64("signed64", 4567890123LL));
REQUIRE(object->add_custom_float("float", 1.25F));
REQUIRE(object->add_custom_double("double", 2.5));
REQUIRE(object->add_custom_string("string", "preserved"));
for (const auto* name : {"boolean", "signed32", "signed64", "float", "double", "string"})
{
CHECK_FALSE(object->set_custom_u_int32(name, 0));
CHECK_FALSE(object->set_custom_u_int32(name, std::numeric_limits<std::uint32_t>::max()));
}
CHECK(object->get_custom_bool("boolean"));
CHECK(object->get_custom_int32("signed32") == -17);
CHECK(object->get_custom_int64("signed64") == 4567890123LL);
CHECK(object->get_custom_float("float") == 1.25F);
CHECK(object->get_custom_double("double") == 2.5);
CHECK(std::string(object->get_custom_string("string")) == "preserved");

REQUIRE(object->set_custom_bool("boolean", false));
REQUIRE(object->set_custom_int32("signed32", 23));
REQUIRE(object->set_custom_int64("signed64", -4567890123LL));
REQUIRE(object->set_custom_float("float", 3.5F));
REQUIRE(object->set_custom_double("double", 4.25));
REQUIRE(object->set_custom_string("string", "updated"));
CHECK_FALSE(object->get_custom_bool("boolean"));
CHECK(object->get_custom_int32("signed32") == 23);
CHECK(object->get_custom_int64("signed64") == -4567890123LL);
CHECK(object->get_custom_float("float") == 3.5F);
CHECK(object->get_custom_double("double") == 4.25);
CHECK(std::string(object->get_custom_string("string")) == "updated");
}
Loading