diff --git a/.vscode/launch.json b/.vscode/launch.json index 3746b7f..dd969b9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,10 +9,10 @@ "program": "${config:godotTools.editorPath.godot4}", "args": [ "--path", - "../../${workspaceFolder}", - "--headless" + "./", + "--unittest=all" ], - "cwd": "../../${workspaceFolder}" + "cwd": "${workspaceFolder}/../../" } ] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c56f5bb..53b48fc 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -27,7 +27,7 @@ "label": "godot-extension: Doctool Dump Documentation Files", "detail": "Dumps GDExtension documentation xml files", "type": "shell", - "command": "${config:godotTools.editorPath.godot4} --doctool ./cpp/src/ --gdextension-docs --editor --headless --quit", + "command": "cd ../../ && ${config:godotTools.editorPath.godot4} --doctool ./addons/glecs/cpp/src --gdextension-docs --editor --headless --quit", "problemMatcher": [] } ] diff --git a/cpp/src/component.cpp b/cpp/src/component.cpp index 12baf44..c8a1e8f 100644 --- a/cpp/src/component.cpp +++ b/cpp/src/component.cpp @@ -35,10 +35,11 @@ Ref GFComponent::from(Variant comp, Variant entity, GFWorld* world) return from_id(world->coerce_id(comp), world->coerce_id(entity), world); } Ref GFComponent::from_id(ecs_entity_t comp, ecs_entity_t entity, GFWorld* world) { - if (!ecs_has_id(world->raw(), comp, ecs_id(EcsComponent))) { + const EcsComponent* comp_ptr = GFComponent::get_component_ptr(world, comp); + if (comp_ptr == nullptr) { ERR(nullptr, "Could not instantiate ", get_class_static(), "\n", - "ID is not a component" + " Entity ", world->id_to_text(comp), " is not a component" ); } Ref component = from_id_template(comp, world); @@ -117,15 +118,17 @@ const EcsMember* GFComponent::get_member_data(String member) { ecs_world_t* raw = get_world()->raw(); const char* c_str = member.utf8().get_data(); + ecs_entity_t main_id = get_world()->get_main_id(get_id()); + // Get member ID - ecs_entity_t member_id = ecs_lookup_child(raw, get_id(), c_str); + ecs_entity_t member_id = ecs_lookup_child(raw, main_id, c_str); if (member_id == 0) { ERR(nullptr, "No member named \"", member, "\" found in component \"", - ecs_get_name(raw, get_id()), + get_world()->id_to_text(get_id()), "\"" ); } @@ -181,7 +184,7 @@ Variant GFComponent::member_value_as_type( } ERR(nullptr, - "Can't convert type ", ecs_get_name(raw, type), " to Variant" + "Can't convert type ", get_world()->id_to_text(type), " to Variant" ); } case(Variant::Type::BOOL): return Variant( *static_cast(ptr) ); @@ -306,6 +309,47 @@ void GFComponent::build_data_from_variant( } } +const EcsComponent* GFComponent::get_component_ptr(GFWorld* world, ecs_entity_t id) { + return ecs_get(world->raw(), world->get_main_id(id), EcsComponent); +} + +const EcsStruct* GFComponent::get_struct_ptr(GFWorld* world, ecs_entity_t id) { + return ecs_get(world->raw(), world->get_main_id(id), EcsStruct); +} + +void GFComponent::build_data_from_members( + Array members, + void* output, + ecs_entity_t component_id, + GFWorld* world +) { + ecs_world_t* w_raw = world->raw(); + + const EcsStruct* struct_data = GFComponent::get_struct_ptr(world, component_id); + if (struct_data == nullptr) { + ERR(/**/, + "Could not build data from Array\n", + " Entity ", world->id_to_text(component_id), " is not a struct." + ); + } + + for (int i=0; i != members.size() && i != ecs_vec_size(&struct_data->members); i++) { + // Iterate the combined sizes of the passed array and the members vector + Variant value = members[i]; + + ecs_member_t* member_data = /* Get member metadata */ ecs_vec_get_t( + &struct_data->members, + ecs_member_t, + i + ); + void* member_ptr = /* Get member pointer */ static_cast( + static_cast(output) + member_data->offset + ); + // Set member value + Utils::set_type_from_variant(value, member_data->type, w_raw, member_ptr); + } +} + void GFComponent::set_source_id(ecs_entity_t id) { source_entity_id = id; } diff --git a/cpp/src/component.h b/cpp/src/component.h index 893f60a..4926047 100644 --- a/cpp/src/component.h +++ b/cpp/src/component.h @@ -6,6 +6,7 @@ #include "entity.h" #include "godot_cpp/core/gdvirtual.gen.inc" #include "registerable_entity.h" +#include "world.h" #include #include @@ -50,6 +51,9 @@ namespace godot { void _register_internal(); void build_data_from_variant(Variant, void* output); + static void build_data_from_members(Array, void*, ecs_entity_t, GFWorld*); + static const EcsComponent* get_component_ptr(GFWorld*, ecs_entity_t); + static const EcsStruct* get_struct_ptr(GFWorld*, ecs_entity_t); void set_source_id(ecs_entity_t id); static Ref new_internal(); diff --git a/cpp/src/component_builder.cpp b/cpp/src/component_builder.cpp index 6b873a5..57a80db 100644 --- a/cpp/src/component_builder.cpp +++ b/cpp/src/component_builder.cpp @@ -74,11 +74,16 @@ Ref GFComponentBuilder::set_name( } void GFComponentBuilder::build() { - const char* FAILED_TO_BUILD = "Failed to build component\n"; if (built) { ERR(/**/, - FAILED_TO_BUILD, - "Component builder was already built" + "Failed to build component \"" + name + "\".\n", + " Component is already built." + ); + } + if (get_member_count() == 0) { + ERR(/**/, + "Failed to build component \"" + name + "\".\n", + " No members were defined. Specify at least one member." ); } built = true; diff --git a/cpp/src/doc_classes/GFComponent.xml b/cpp/src/doc_classes/GFComponent.xml index 40a62a6..dd45159 100644 --- a/cpp/src/doc_classes/GFComponent.xml +++ b/cpp/src/doc_classes/GFComponent.xml @@ -1,5 +1,6 @@ - + A reference to a Glecs component from a [GFWorld] that is attatached to an entity. @@ -55,7 +56,7 @@ - Returns the value of a component's member + Returns the value of a component's member. [codeblock] var entity = GFEntity.spawn() entity.add_component("glecs/meta/Vector2") @@ -70,7 +71,7 @@ - Sets the value of a component's member + Sets the value of a component's member. [codeblock] var entity = GFEntity.spawn() entity.add_component("glecs/meta/Vector2") diff --git a/cpp/src/doc_classes/GFEntity.xml b/cpp/src/doc_classes/GFEntity.xml index 0942b6f..358f239 100644 --- a/cpp/src/doc_classes/GFEntity.xml +++ b/cpp/src/doc_classes/GFEntity.xml @@ -1,5 +1,6 @@ - + A reference to an entity from a [GFWorld]. @@ -9,7 +10,7 @@ https://www.flecs.dev/flecs/md_docs_2EntitiesComponents.html - + @@ -17,9 +18,26 @@ The component's type ID is coerced from a [Variant]. To learn more about [Variant] coercion see [method GFWorld.coerce_id]. [codeblock] var entity:= GFEntity.spawn() - entity.add_component("glecs/meta/Array") - entity.get_component("glecs/meta/Array") != null # true + entity.add_component(GFPosition2D, Vector2(10, 10)) + entity.get_component(GFPosition2D).get_vec() == Vector2(10, 10) # true + [/codeblock] + This method returns [code]self[/code] for chaining. + + + + + + + + Adds component data to the pair of [param first] and [param second] to + this entity. + The component's type ID is coerced from a [Variant]. To learn more about [Variant] coercion see [method GFWorld.coerce_id]. + [codeblock] + var entity:= GFEntity.spawn() + entity.add_pair(GFPosition2D, Vector2(10, 10)) + entity.get_pair(GFPosition2D).get_vec() == Vector2(10, 10) # true [/codeblock] + This method returns [code]self[/code] for chaining. @@ -81,6 +99,21 @@ [/codeblock] + + + + + + Returns a reference to this entity's component's data in the pair + of [param first] and [param second]. + Returns [code]null[/code] if the entity does not have the [param component] attached or if [param component] is not a component. The component is identified by an ID coerced from a [Variant]. To learn more about [Variant] coercion see [method GFWorld.coerce_id]. + [codeblock] + var entity:= GFEntity.spawn() + entity.add_component("glecs/meta/Array") + entity.get_component("glecs/meta/Array") != null # true + [/codeblock] + + @@ -135,10 +168,32 @@ [codeblock] var Eats:= GFEntity.spawn() var Grass:= GFEntity.spawn() - var EatsGrass_id:int = Eats.pair(Grass.get_id()) + var EatsGrass_id:int = Eats.pair_id(Grass.get_id()) [/codeblock] + + + + + Sets component data of type [param component] in this entity. + The component's type ID is coerced from a [Variant]. To learn more about [Variant] coercion see [method GFWorld.coerce_id]. + [codeblock] + var entity:= GFEntity.spawn() + entity.add_component(GFPosition2D) + entity.set_component(GFPosition2D, Vector2(10, 10)) + entity.get_component(GFPosition2D).get_vec() == Vector2(10, 10) # true + [/codeblock] + If the component is not added yet, then this method will add + it automaticly. + [codeblock] + var entity:= GFEntity.spawn() + entity.set_component(GFPosition2D, Vector2(10, 10)) + entity.get_component(GFPosition2D).get_vec() == Vector2(10, 10) # true + [/codeblock] + This method returns [code]self[/code] for chaining. + + @@ -149,6 +204,25 @@ entity.set_name("Entity") entity.get_name() == "Entity" # true [/codeblock] + This method returns [code]self[/code] for chaining. + + + + + + + + Sets component data is this entity from the pair of [param first] and + [param second]. + The component's type ID is coerced from a [Variant]. To learn more about [Variant] coercion see [method GFWorld.coerce_id]. + [codeblock] + var start:= GFEntity.spawn().set_name("Start") + var entity:= GFEntity.spawn() + # Pair will be added if it is not already. + entity.set_pair(start, GFPosition2D, Vector2(10, 10)) + entity.get_pair(start, GFPosition2D).get_vec() == Vector2(10, 10) # true + [/codeblock] + This method returns [code]self[/code] for chaining. diff --git a/cpp/src/entity.cpp b/cpp/src/entity.cpp index e38007e..bb9fb36 100644 --- a/cpp/src/entity.cpp +++ b/cpp/src/entity.cpp @@ -1,6 +1,7 @@ #include "entity.h" #include "godot_cpp/classes/script.hpp" +#include "godot_cpp/variant/array.hpp" #include "utils.h" // needed here because entity.h does not include // component.h, but uses forward declaration instead @@ -31,54 +32,87 @@ Ref GFEntity::from_id(ecs_entity_t id, GFWorld* world) { return from_id_template(id, world); } -Ref GFEntity::add_component(Variant component, Variant data) { - GFWorld* w = get_world(); - - ecs_entity_t c_id = w->coerce_id(component); - - if (ECS_IS_PAIR(c_id)) { - add_pair( - ECS_PAIR_FIRST(c_id), - ECS_PAIR_SECOND(c_id), - data - ); - return Ref(this); +Ref GFEntity::add_component( + const Variant** args, GDExtensionInt arg_count, GDExtensionCallError &error +) { + if (arg_count < 1) { + // Too few arguments, return with error. + error.error = GDExtensionCallErrorType::GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS; + error.argument = arg_count; + error.expected = 1; + return this; } - if (!ecs_has_id(w->raw(), c_id, ecs_id(EcsComponent))) { - ERR(Ref(this), - "Failed to add component to entity\n", - "ID coerced from ", component, " is not a component" - ); + // Parse arguments + Array members = Array(); + members.resize(arg_count); + for (int i=0; i != arg_count; i++) { + members[i] = *args[i]; } + Variant comopnent = members.pop_front(); - ecs_add_id(w->raw(), get_id(), c_id); + _add_component(comopnent, members); - if (data != Variant()) { - set_component(c_id, data); - } - - return Ref(this); + return this; } -Ref GFEntity::add_entity(Variant entity, Variant data) { +Ref GFEntity::_add_component(Variant component, Array members) { GFWorld* w = get_world(); - ecs_add_id(w->raw(), get_id(), w->coerce_id(entity)); - if (data != Variant()) { - set_component(entity, data); + ecs_entity_t c_id = w->coerce_id(component); + + if (ecs_has_id(w->raw(), get_id(), c_id)) { + ERR(this, + "Can't add component to entity\n", + "ID coerced from ", component, " is already added to ", get_id() + ) } - return Ref(this); + _set_component(c_id, members); + + return this; } -Ref GFEntity::add_pair(Variant first, Variant second, Variant data) { +Ref GFEntity::add_pair( + const Variant** args, GDExtensionInt arg_count, GDExtensionCallError &error +) { + if (arg_count < 2) { + // Too few arguments, return with error. + error.error = GDExtensionCallErrorType::GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS; + error.argument = arg_count; + error.expected = 2; + return this; + } + + // Parse arguments + Array members = Array(); + members.resize(arg_count); + for (int i=0; i != arg_count; i++) { + members[i] = *args[i]; + } + Variant first = members.pop_front(); + Variant sec = members.pop_front(); + + _add_pair(first, sec, members); + + return this; +} +Ref GFEntity::_add_pair(Variant first, Variant second, Array members) { GFWorld* w = get_world(); ecs_entity_t first_id = w->coerce_id(first); ecs_entity_t second_id = w->coerce_id(second); ecs_entity_t pair_id = w->pair_ids(first_id, second_id); - add_entity(pair_id, data); + if ( + members.size() != 0 + || ecs_has_id(w->raw(), pair_id, FLECS_IDEcsComponentID_) + ) { + // Add pair as a component + _add_component(pair_id, members); + } else { + // Add pair as a dataless tag + add_tag(pair_id); + } return Ref(this); } @@ -87,11 +121,12 @@ Ref GFEntity::add_tag(Variant tag) { GFWorld* w = get_world(); ecs_entity_t tag_id = w->coerce_id(tag); + if (ecs_has(w->raw(), tag_id, EcsComponent)) { ERR(Ref(this), "Failed to add tag to entity\n", - "ID, ", tag_id, "is a component, not a tag\n" - "(Tags are any non-component entity)" + " ID, ", tag_id, " is a component, not a tag\n" + " (Tags are any entity with no data)" ); } @@ -121,41 +156,115 @@ Ref GFEntity::get_component(Variant component) { return c; } -Ref GFEntity::set_component(Variant component, Variant data) { - GFWorld* world = get_world(); +Ref GFEntity::get_pair(Variant first, Variant second) { + ecs_entity_t first_id = get_world()->coerce_id(first); + ecs_entity_t second_id = get_world()->coerce_id(second); + return get_component(ecs_pair(first_id, second_id)); +} - ecs_entity_t id = world->coerce_id(component); +Ref GFEntity::set_component( + const Variant** args, GDExtensionInt arg_count, GDExtensionCallError &error +) { + if (arg_count < 1) { + // Too few arguments, return with error. + error.error = GDExtensionCallErrorType::GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS; + error.argument = arg_count; + error.expected = 1; + return this; + } - if (!ECS_IS_PAIR(id)) { - if (!ecs_has(world->raw(), id, EcsComponent)) { - ERR(Ref(this), - "Failed to set data in component\n", - "ID, ", id, "is not a component" - ); - } - } else { - ecs_entity_t first_id = ECS_PAIR_FIRST(id); - ecs_entity_t second_id = ECS_PAIR_SECOND(id); - if ( - !ecs_has_id(world->raw(), first_id, ecs_id(EcsComponent)) - && !ecs_has_id(world->raw(), second_id, ecs_id(EcsComponent)) - ) { - // ID is not a component, err + // Parse arguments + Array members = Array(); + members.resize(arg_count); + for (int i=0; i != arg_count; i++) { + members[i] = *args[i]; + } + Variant comopnent = members.pop_front(); + + return _set_component(comopnent, members); +} + +Ref GFEntity::_set_component( + Variant component, + Array members +) { + GFWorld* w = get_world(); + + ecs_entity_t c_id = w->coerce_id(component); + + if (!ecs_has_id(w->raw(), get_id(), c_id)) { + // Component is not present in entity, add it + + if (ECS_IS_PAIR(c_id)) { + // ID is a pair, perform pair specific checks + ecs_entity_t first_id = ECS_PAIR_FIRST(c_id); + ecs_entity_t second_id = ECS_PAIR_SECOND(c_id); + if ( + !ecs_has_id(w->raw(), first_id, ecs_id(EcsComponent)) + && !ecs_has_id(w->raw(), second_id, ecs_id(EcsComponent)) + ) { + // ID is not a component, error + ERR(Ref(this), + "Failed to set data in pair\n", + "Neither ID ", first_id, + " nor ", second_id, "are components" + ); + } + + } else if (!ecs_has_id(w->raw(), c_id, ecs_id(EcsComponent))) { + // Error, passed variant is not a real component ERR(Ref(this), - "Failed to set data in pair\n", - "Neither ID ", first_id, - " nor ", second_id, "are components" + "Failed to add component to entity\n", + "ID coerced from ", component, " is not a component" ); + } + + ecs_add_id(w->raw(), get_id(), c_id); } - // TODO: change build_data_from_variant to a static method - Ref(memnew(GFComponent(get_id(), id, world))) - ->build_data_from_variant( - data, - ecs_get_mut_id(world->raw(), get_id(), id) - ); - ecs_modified_id(world->raw(), get_id(), id); + GFComponent::build_data_from_members( + members, + ecs_get_mut_id(w->raw(), get_id(), c_id), + c_id, + get_world() + ); + ecs_modified_id(w->raw(), get_id(), c_id); + + return Ref(this); +} + +Ref GFEntity::set_pair( + const Variant** args, GDExtensionInt arg_count, GDExtensionCallError &error +) { + if (arg_count < 1) { + // Too few arguments, return with error. + error.error = GDExtensionCallErrorType::GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS; + error.argument = arg_count; + error.expected = 1; + return this; + } + + // Parse arguments + Array members = Array(); + members.resize(arg_count); + for (int i=0; i != arg_count; i++) { + members[i] = *args[i]; + } + Variant first = members.pop_front(); + Variant sec = members.pop_front(); + + return _set_pair(first, sec, members); +} + +Ref GFEntity::_set_pair( + Variant first, + Variant second, + Array members +) { + ecs_entity_t first_id = get_world()->coerce_id(first); + ecs_entity_t second_id = get_world()->coerce_id(second); + _set_component(ecs_pair(first_id, second_id), members); return Ref(this); } @@ -270,12 +379,37 @@ void GFEntity::_bind_methods() { godot::ClassDB::bind_static_method(GFEntity::get_class_static(), D_METHOD("from", "entity", "world"), &GFEntity::from, nullptr); godot::ClassDB::bind_static_method(GFEntity::get_class_static(), D_METHOD("from_id", "id", "world"), &GFEntity::from_id, nullptr); - godot::ClassDB::bind_method(D_METHOD("add_component", "component", "data"), &GFEntity::add_component, nullptr); - godot::ClassDB::bind_method(D_METHOD("add_entity", "entity", "data"), &GFEntity::add_entity, nullptr); - godot::ClassDB::bind_method(D_METHOD("add_pair", "first", "second", "data"), &GFEntity::add_pair, nullptr); + { + MethodInfo mi; + mi.arguments.push_back(PropertyInfo(Variant::NIL, "component")); + mi.name = "add_component"; + godot::ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, StringName("add_component"), &GFEntity::add_component, mi); + } + { + MethodInfo mi; + mi.arguments.push_back(PropertyInfo(Variant::NIL, "component")); + mi.name = "set_component"; + godot::ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, StringName("set_component"), &GFEntity::set_component, mi); + } + + { + MethodInfo mi; + mi.arguments.push_back(PropertyInfo(Variant::NIL, "first")); + mi.arguments.push_back(PropertyInfo(Variant::NIL, "second")); + mi.name = "add_pair"; + godot::ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, StringName("add_pair"), &GFEntity::add_pair, mi); + } + { + MethodInfo mi; + mi.arguments.push_back(PropertyInfo(Variant::NIL, "first")); + mi.arguments.push_back(PropertyInfo(Variant::NIL, "second")); + mi.name = "set_pair"; + godot::ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, StringName("set_pair"), &GFEntity::set_pair, mi); + } + godot::ClassDB::bind_method(D_METHOD("add_tag", "tag"), &GFEntity::add_tag); godot::ClassDB::bind_method(D_METHOD("get_component", "component"), &GFEntity::get_component); - godot::ClassDB::bind_method(D_METHOD("set_component", "component", "value"), &GFEntity::set_component); + godot::ClassDB::bind_method(D_METHOD("get_pair", "first", "second"), &GFEntity::get_pair); godot::ClassDB::bind_method(D_METHOD("delete"), &GFEntity::delete_); diff --git a/cpp/src/entity.h b/cpp/src/entity.h index f80b35b..d929ef4 100644 --- a/cpp/src/entity.h +++ b/cpp/src/entity.h @@ -35,13 +35,20 @@ namespace godot { static Ref from(Variant, GFWorld*); static Ref from_id(ecs_entity_t, GFWorld*); - Ref add_component(Variant, Variant data); - Ref add_entity(Variant, Variant data); - Ref add_pair(Variant, Variant, Variant data); + Ref add_component(const Variant**, GDExtensionInt, GDExtensionCallError&); + Ref _add_component(Variant, Array); + Ref set_component(const Variant**, GDExtensionInt, GDExtensionCallError&); + Ref _set_component(Variant, Array); + + Ref add_pair(const Variant**, GDExtensionInt, GDExtensionCallError&); + Ref _add_pair(Variant, Variant, Array); + Ref set_pair(const Variant**, GDExtensionInt, GDExtensionCallError&); + Ref _set_pair(Variant, Variant, Array); + Ref add_tag(Variant); Ref get_component(Variant); - Ref set_component(Variant, Variant data); + Ref get_pair(Variant, Variant); void delete_(); diff --git a/cpp/src/register_types.cpp b/cpp/src/register_types.cpp index 32c401a..b7f8f91 100644 --- a/cpp/src/register_types.cpp +++ b/cpp/src/register_types.cpp @@ -5,6 +5,8 @@ #include "entity.h" #include "gdextension_interface.h" #include "godot_cpp/classes/engine.hpp" +#include "godot_cpp/classes/resource_loader.hpp" +#include "godot_cpp/variant/utility_functions.hpp" #include "module.h" #include "observer_builder.h" #include "pair.h" @@ -20,40 +22,53 @@ using namespace godot; void initialize_module(ModuleInitializationLevel p_level) { - if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { - GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen editor_help_load_xml_from_utf8_chars_and_len = (GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen)internal::gdextension_interface_get_proc_address("editor_help_load_xml_from_utf8_chars_and_len"); - editor_help_load_xml_from_utf8_chars_and_len(_doc_data, _doc_data_size); - } - if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { - return; - } - - godot::ClassDB::register_class(); + switch (p_level) { + case MODULE_INITIALIZATION_LEVEL_CORE: + case MODULE_INITIALIZATION_LEVEL_SERVERS: + case MODULE_INITIALIZATION_LEVEL_MAX: + break; + case MODULE_INITIALIZATION_LEVEL_SCENE: + godot::ClassDB::register_class(); - godot::ClassDB::register_abstract_class(); - godot::ClassDB::register_abstract_class(); - godot::ClassDB::register_class(); - godot::ClassDB::register_class(); - godot::ClassDB::register_class(); + godot::ClassDB::register_abstract_class(); + godot::ClassDB::register_abstract_class(); + godot::ClassDB::register_class(); + godot::ClassDB::register_class(); + godot::ClassDB::register_class(); - godot::ClassDB::register_abstract_class(); - godot::ClassDB::register_abstract_class(); - godot::ClassDB::register_abstract_class(); + godot::ClassDB::register_abstract_class(); + godot::ClassDB::register_abstract_class(); + godot::ClassDB::register_abstract_class(); - godot::ClassDB::register_abstract_class(); - godot::ClassDB::register_abstract_class(); - godot::ClassDB::register_abstract_class(); - godot::ClassDB::register_abstract_class(); - - Engine::get_singleton()->register_singleton(GFWorld::SINGLETON_NAME, memnew(GFWorld)); + godot::ClassDB::register_abstract_class(); + godot::ClassDB::register_abstract_class(); + godot::ClassDB::register_abstract_class(); + godot::ClassDB::register_abstract_class(); + Engine::get_singleton()->register_singleton(GFWorld::SINGLETON_NAME, memnew(GFWorld)); + break; + case MODULE_INITIALIZATION_LEVEL_EDITOR: + GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen + editor_help_load_xml_from_utf8_chars_and_len + = (GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen) + internal::gdextension_interface_get_proc_address( + "editor_help_load_xml_from_utf8_chars_and_len" + ); + editor_help_load_xml_from_utf8_chars_and_len(_doc_data, _doc_data_size); + break; + } } void uninitialize_module(ModuleInitializationLevel p_level) { - if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { - return; + switch (p_level) { + case MODULE_INITIALIZATION_LEVEL_CORE: + case MODULE_INITIALIZATION_LEVEL_SERVERS: + case MODULE_INITIALIZATION_LEVEL_EDITOR: + case MODULE_INITIALIZATION_LEVEL_MAX: + break; + case MODULE_INITIALIZATION_LEVEL_SCENE: + Engine::get_singleton()->unregister_singleton(GFWorld::SINGLETON_NAME); + break; } - - Engine::get_singleton()->unregister_singleton(GFWorld::SINGLETON_NAME); } extern "C" { diff --git a/cpp/src/utils.cpp b/cpp/src/utils.cpp index 183b97c..2c6dcfe 100644 --- a/cpp/src/utils.cpp +++ b/cpp/src/utils.cpp @@ -1,6 +1,7 @@ #include "utils.h" #include "godot_cpp/core/defs.hpp" +#include "godot_cpp/variant/utility_functions.hpp" #include "godot_cpp/variant/variant.hpp" #include "world.h" @@ -159,6 +160,70 @@ void Utils::set_gd_struct_from_variant( } } +bool Utils::can_convert_type_to_primitive(Variant::Type type, ecs_primitive_kind_t primi) { + switch (type) { + case Variant::NIL: + return false; + case Variant::BOOL: + return primi == ecs_primitive_kind_t::EcsBool; + case Variant::INT: { + switch (primi) { + case EcsU8: case EcsU16: case EcsU32: case EcsU64: + case EcsI8: case EcsI16: case EcsI32: case EcsI64: + return true; + default: + return false; + } + } + case Variant::FLOAT: { + switch (primi) { + case EcsF32: case EcsF64: + return true; + default: + return false; + } + } + case Variant::STRING: + case Variant::VECTOR2: + case Variant::VECTOR2I: + case Variant::RECT2: + case Variant::RECT2I: + case Variant::VECTOR3: + case Variant::VECTOR3I: + case Variant::TRANSFORM2D: + case Variant::VECTOR4: + case Variant::VECTOR4I: + case Variant::PLANE: + case Variant::QUATERNION: + case Variant::AABB: + case Variant::BASIS: + case Variant::TRANSFORM3D: + case Variant::PROJECTION: + case Variant::COLOR: + case Variant::STRING_NAME: + case Variant::NODE_PATH: + case Variant::RID: + case Variant::OBJECT: + case Variant::CALLABLE: + case Variant::SIGNAL: + case Variant::DICTIONARY: + case Variant::ARRAY: + case Variant::PACKED_BYTE_ARRAY: + case Variant::PACKED_INT32_ARRAY: + case Variant::PACKED_INT64_ARRAY: + case Variant::PACKED_FLOAT32_ARRAY: + case Variant::PACKED_FLOAT64_ARRAY: + case Variant::PACKED_STRING_ARRAY: + case Variant::PACKED_VECTOR2_ARRAY: + case Variant::PACKED_VECTOR3_ARRAY: + case Variant::PACKED_COLOR_ARRAY: + case Variant::PACKED_VECTOR4_ARRAY: + case Variant::VARIANT_MAX: + return false; + } + return false; +} + void Utils::set_primitive_from_variant( const Variant value, const ecs_primitive_kind_t primi_kind, @@ -194,6 +259,13 @@ void Utils::set_type_from_variant( ) { const EcsPrimitive* primi = ecs_get(world, type, EcsPrimitive); if (primi != nullptr) { + if (!Utils::can_convert_type_to_primitive(value.get_type(), primi->kind)) { + // TODO: Display names of primitive and variant type, instead of numbers + ERR(/**/, "Set value failed.\n", + "Setting Flecs primitive type, ", primi->kind, ", to value of Godot type, ", + value.get_type(), ", is invalid." + ); + } set_primitive_from_variant(value, primi->kind, out); return; } diff --git a/cpp/src/utils.h b/cpp/src/utils.h index fa060a1..73623d9 100644 --- a/cpp/src/utils.h +++ b/cpp/src/utils.h @@ -94,6 +94,7 @@ namespace godot { static Variant primitive_value_to_variant(const void*, ecs_primitive_kind_t); static Variant::Type primitive_type_to_variant(ecs_primitive_kind_t); static EntityResult variant_type_to_id(Variant::Type type); + static bool can_convert_type_to_primitive(Variant::Type type, ecs_primitive_kind_t primi); static String into_pascal_case(String str); diff --git a/cpp/src/world.cpp b/cpp/src/world.cpp index daa1ce9..4563370 100644 --- a/cpp/src/world.cpp +++ b/cpp/src/world.cpp @@ -490,17 +490,7 @@ GFWorld::GFWorld() { #undef DEFINE_GD_COMPONENT #undef DEFINE_GD_COMPONENT_WITH_HOOKS - // Register modules from scripts - Engine* engine = Engine::get_singleton(); - if (engine->has_singleton("_glecs_modules")) { - ecs_entity_t prev_scope = ecs_get_scope(raw()); - ecs_set_scope(raw(), glecs); - - Object* glecs_modules = engine->get_singleton("_glecs_modules"); - glecs_modules->call("register_modules", this); - - ecs_set_scope(raw(), prev_scope); - } + _register_modules_from_scripts(0); } GFWorld::~GFWorld() { @@ -690,6 +680,12 @@ ecs_entity_t GFWorld::register_script_id(Ref