Skip to content

Commit

Permalink
feat: system notify settings (#196)
Browse files Browse the repository at this point in the history
  • Loading branch information
zaucy authored Sep 29, 2023
1 parent 73c01df commit ef9fa96
Show file tree
Hide file tree
Showing 15 changed files with 356 additions and 9 deletions.
4 changes: 2 additions & 2 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ module(
bazel_dep(name = "rules_cc", version = "0.0.8")
bazel_dep(name = "bazel_skylib", version = "1.4.2")
bazel_dep(name = "magic_enum", version = "0.9.3")
bazel_dep(name = "ecsact_runtime", version = "0.5.1")
bazel_dep(name = "ecsact_parse", version = "0.3.0")
bazel_dep(name = "ecsact_runtime", version = "0.5.3")
bazel_dep(name = "ecsact_parse", version = "0.3.3")
2 changes: 1 addition & 1 deletion bazel/copts.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ copts = selects.with_or({
"-fexperimental-library",
],
("@rules_cc//cc/compiler:msvc-cl", "@rules_cc//cc/compiler:clang-cl"): [
"/std:c++latest",
"/std:c++20",
"/permissive-",
"/Zc:preprocessor",
],
Expand Down
195 changes: 195 additions & 0 deletions ecsact/interpret/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "ecsact/parse.h"
#include "ecsact/runtime/dynamic.h"
#include "ecsact/runtime/meta.hh"
#include "ecsact/runtime/meta.h"

#include "./detail/file_eval_error.hh"

Expand Down Expand Up @@ -1100,6 +1101,13 @@ static ecsact_eval_error eval_system_component_statement(
};
}

if(ecsact::meta::system_notify_settings_count(*sys_like_id) > 0) {
return ecsact_eval_error{
.code = ECSACT_EVAL_ERR_NOTIFY_BEFORE_SYSTEM_COMPONENT,
.relevant_content = {},
};
}

auto& data = statement.data.system_component_statement;

std::string comp_like_name(
Expand Down Expand Up @@ -1308,6 +1316,181 @@ static ecsact_eval_error eval_system_with_entity_statement(
return {};
}

static auto get_notify_setting_from_string( //
std::string_view setting_name
) -> std::optional<ecsact_system_notify_setting> {
static const auto notify_setting_name_map =
std::unordered_map<std::string_view, ecsact_system_notify_setting>{
{"always"sv, ECSACT_SYS_NOTIFY_ALWAYS},
{"oninit"sv, ECSACT_SYS_NOTIFY_ONINIT},
{"onupdate"sv, ECSACT_SYS_NOTIFY_ONUPDATE},
{"onchange"sv, ECSACT_SYS_NOTIFY_ONCHANGE},
{"onremove"sv, ECSACT_SYS_NOTIFY_ONREMOVE},
};

auto itr = notify_setting_name_map.find(setting_name);
if(itr != notify_setting_name_map.end()) {
return itr->second;
}

return std::nullopt;
}

static auto eval_system_notify_statement(
ecsact_package_id package_id,
std::span<const ecsact_statement>& context_stack,
const ecsact_statement& statement
) -> ecsact_eval_error {
auto [context, err] = expect_context(
context_stack,
{
ECSACT_STATEMENT_SYSTEM,
ECSACT_STATEMENT_ACTION,
}
);

if(err.code != ECSACT_EVAL_OK) {
return err;
}

if(auto err = disallow_statement_params(statement, context)) {
return *err;
}

auto sys_like_id = find_by_statement<ecsact_system_like_id>( //
package_id,
*context
);

if(ecsact::meta::system_notify_settings_count(*sys_like_id) > 0) {
return ecsact_eval_error{
.code = ECSACT_EVAL_ERR_MULTIPLE_NOTIFY_STATEMENTS,
.relevant_content = {},
};
}

auto data = statement.data.system_notify_statement;

auto setting_name = std::string_view( //
data.setting_name.data,
data.setting_name.length
);

if(!setting_name.empty()) {
auto notify_setting = get_notify_setting_from_string(setting_name);

if(!notify_setting) {
return ecsact_eval_error{
.code = ECSACT_EVAL_ERR_INVALID_NOTIFY_SETTING,
.relevant_content = data.setting_name,
};
}

for(auto&& [comp_id, _] : ecsact::meta::system_capabilities(*sys_like_id)) {
ecsact_set_system_notify_component_setting(
*sys_like_id,
comp_id,
*notify_setting
);
}
}

return {};
}

static auto eval_system_notify_component_statement(
ecsact_package_id package_id,
std::span<const ecsact_statement>& context_stack,
const ecsact_statement& statement
) -> ecsact_eval_error {
if(context_stack.size() < 2) {
return ecsact_eval_error{
.code = ECSACT_EVAL_ERR_INVALID_CONTEXT,
.relevant_content = {},
};
}

auto [context, err] =
expect_context(context_stack, {ECSACT_STATEMENT_SYSTEM_NOTIFY});

if(err.code != ECSACT_EVAL_OK) {
return err;
}

if(auto err = disallow_statement_params(statement, context)) {
return *err;
}

auto block_setting_name = std::string_view( //
context->data.system_notify_statement.setting_name.data,
context->data.system_notify_statement.setting_name.length
);

auto data = statement.data.system_notify_component_statement;

if(!block_setting_name.empty()) {
return ecsact_eval_error{
.code = ECSACT_EVAL_ERR_NOTIFY_BLOCK_AND_COMPONENTS,
.relevant_content = data.setting_name,
.context_type = context->type,
};
}

auto sys_like_id = find_by_statement<ecsact_system_like_id>( //
package_id,
context_stack[context_stack.size() - 2]
);

auto comp_like_name = std::string( //
data.component_name.data,
data.component_name.length
);

auto comp_like_id = find_by_name<ecsact_component_like_id>( //
package_id,
comp_like_name
);

if(!comp_like_id) {
return ecsact_eval_error{
.code = ECSACT_EVAL_ERR_UNKNOWN_COMPONENT_LIKE_TYPE,
.relevant_content = data.component_name,
};
}

auto setting_name = std::string_view( //
data.setting_name.data,
data.setting_name.length
);

auto notify_setting = get_notify_setting_from_string(setting_name);

if(!notify_setting) {
return ecsact_eval_error{
.code = ECSACT_EVAL_ERR_INVALID_NOTIFY_SETTING,
.relevant_content = data.setting_name,
};
}

for(auto&& [existing_comp_id, _] :
ecsact::meta::system_notify_settings(*sys_like_id)) {
if(existing_comp_id == *comp_like_id) {
return ecsact_eval_error{
.code = ECSACT_EVAL_ERR_DUPLICATE_NOTIFY_COMPONENT,
.relevant_content = {},
};
}
}

ecsact_set_system_notify_component_setting(
*sys_like_id,
*comp_like_id,
*notify_setting
);

return {};
}

static ecsact_eval_error eval_entity_constraint_statement(
ecsact_package_id package_id,
std::span<const ecsact_statement>& context_stack,
Expand Down Expand Up @@ -1484,6 +1667,18 @@ ecsact_eval_error ecsact_eval_statement(
context_statements,
statement
);
case ECSACT_STATEMENT_SYSTEM_NOTIFY:
return eval_system_notify_statement(
package_id,
context_statements,
statement
);
case ECSACT_STATEMENT_SYSTEM_NOTIFY_COMPONENT:
return eval_system_notify_component_statement(
package_id,
context_statements,
statement
);
}

return std::nullopt;
Expand Down
2 changes: 1 addition & 1 deletion ecsact/interpret/eval.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ ecsact_eval_error ecsact_eval_statement(
);

/**
* Clears memory of all previous successfully evaluated statements.
* @deprecated
*/
void ecsact_eval_reset();

Expand Down
18 changes: 17 additions & 1 deletion ecsact/interpret/eval_error.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,24 @@ typedef enum ecsact_eval_error_code {
/// Statement does not support any parameters yet parameters were given
ECSACT_EVAL_ERR_PARAMETERS_NOT_ALLOWED,

/// Notify setting provided is invalid
ECSACT_EVAL_ERR_INVALID_NOTIFY_SETTING,

/// More than 1 notify statements were found. Only 1 is permitted per system.
ECSACT_EVAL_ERR_MULTIPLE_NOTIFY_STATEMENTS,

/// Notify block setting and component settings were found. Only one or the
/// other is permitted.
ECSACT_EVAL_ERR_NOTIFY_BLOCK_AND_COMPONENTS,

/// The notify statement must be after all capability statements.
ECSACT_EVAL_ERR_NOTIFY_BEFORE_SYSTEM_COMPONENT,

// More than one system notify component statement for the same component.
ECSACT_EVAL_ERR_DUPLICATE_NOTIFY_COMPONENT,

/// Internal error. Should not happen and is an indiciation of a bug.
ECSACT_EVAL_ERR_INTERNAL,
ECSACT_EVAL_ERR_INTERNAL = 999,

/// Not an error code. Start of file only errors.
/// File error codes only applies when parsing complete Ecsact files or
Expand Down
60 changes: 58 additions & 2 deletions parse-resolver-runtime/parse-resolver-runtime.cc
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,21 @@ struct system_like {
ecsact_system_generate flag;
};

std::unordered_map<ecsact_component_like_id, cap_entry> caps;
using caps_t = std::unordered_map< //
ecsact_component_like_id,
cap_entry>;
caps_t caps;

using generates_t = std::unordered_map<
ecsact_system_generates_id,
std::unordered_map<ecsact_component_id, ecsact_system_generate>>;
generates_t generates;
generates_t generates;

using notify_settings_t = std::unordered_map< //
ecsact_component_like_id,
ecsact_system_notify_setting>;
notify_settings_t notify_settings;

ecsact_system_like_id parent_system_id = (ecsact_system_like_id)-1;

/** in execution order */
Expand Down Expand Up @@ -1173,3 +1183,49 @@ bool ecsact_meta_get_system_parallel_execution( //
auto& def = get_system_like(system_like_id);
return def.parallel_execution;
}

auto ecsact_meta_system_notify_settings_count(
ecsact_system_like_id system_like_id
) -> int32_t {
auto& def = get_system_like(system_like_id);
return static_cast<int32_t>(def.notify_settings.size());
}

auto ecsact_meta_system_notify_settings(
ecsact_system_like_id system_like_id,
int32_t max_notifies_count,
ecsact_component_like_id* out_notify_component_ids,
ecsact_system_notify_setting* out_notify_settings,
int32_t* out_notifies_count
) -> void {
auto& def = get_system_like(system_like_id);

auto itr = def.notify_settings.begin();
for(int i = 0; max_notifies_count > i; ++i) {
if(i >= def.notify_settings.size() || itr == def.notify_settings.end()) {
break;
}

out_notify_component_ids[i] = itr->first;
out_notify_settings[i] = itr->second;

++itr;
}

if(out_notifies_count != nullptr) {
*out_notifies_count = static_cast<int32_t>(def.notify_settings.size());
}
}

auto ecsact_set_system_notify_component_setting(
ecsact_system_like_id system_like_id,
ecsact_component_like_id component_like_id,
ecsact_system_notify_setting setting
) -> void {
auto& def = get_system_like(system_like_id);
if(setting == ECSACT_SYS_NOTIFY_NONE) {
def.notify_settings.erase(component_like_id);
} else {
def.notify_settings[component_like_id] = setting;
}
}
3 changes: 3 additions & 0 deletions test/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ module(name = "ecsact_interpret_test")
bazel_dep(name = "rules_cc", version = "0.0.8")
bazel_dep(name = "bazel_skylib", version = "1.4.2")
bazel_dep(name = "googletest", version = "1.14.0")
bazel_dep(name = "ecsact_parse", version = "0.3.3")
bazel_dep(name = "ecsact_runtime", version = "0.5.3")

bazel_dep(name = "bazel_sundry")
bazel_dep(name = "ecsact_interpret")

Expand Down
4 changes: 4 additions & 0 deletions test/errors/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
load("@rules_cc//cc:defs.bzl", "cc_test")
load("@ecsact_interpret//bazel:copts.bzl", "copts")

# buildifier: keep sorted
_TESTS = [
"duplicate_notice_components",
"invalid_notify_settings",
"no_capabilities",
"no_package_statement_first",
"unknown_association_field",
"unknown_component_notify_settings",
]

[cc_test(
Expand Down
11 changes: 11 additions & 0 deletions test/errors/duplicate_notice_components.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include "gtest/gtest.h"
#include "ecsact/interpret/eval.h"

#include "test_lib.hh"

TEST(DuplicateNoticeComponents, DuplicateNoticeComponents) {
auto errs =
ecsact_interpret_test_files({"errors/duplicate_notice_components.ecsact"});
ASSERT_EQ(errs.size(), 1);
ASSERT_EQ(errs[0].eval_error, ECSACT_EVAL_ERR_DUPLICATE_NOTIFY_COMPONENT);
}
Loading

0 comments on commit ef9fa96

Please sign in to comment.