diff --git a/cpp/src/component.cpp b/cpp/src/component.cpp index 14d8d86..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,14 @@ 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, @@ -314,11 +325,11 @@ void GFComponent::build_data_from_members( ) { ecs_world_t* w_raw = world->raw(); - const EcsStruct* struct_data = ecs_get(w_raw, component_id, EcsStruct); + const EcsStruct* struct_data = GFComponent::get_struct_ptr(world, component_id); if (struct_data == nullptr) { ERR(/**/, - "Could not build data from Variant\n", - "Component is not a struct." + "Could not build data from Array\n", + " Entity ", world->id_to_text(component_id), " is not a struct." ); } diff --git a/cpp/src/component.h b/cpp/src/component.h index 909d2c4..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 @@ -51,6 +52,8 @@ namespace godot { 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/doc_classes/GFEntity.xml b/cpp/src/doc_classes/GFEntity.xml index 8ad4119..358f239 100644 --- a/cpp/src/doc_classes/GFEntity.xml +++ b/cpp/src/doc_classes/GFEntity.xml @@ -24,6 +24,22 @@ 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. + + @@ -83,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] + + @@ -176,6 +207,24 @@ 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 7f1f936..bb9fb36 100644 --- a/cpp/src/entity.cpp +++ b/cpp/src/entity.cpp @@ -73,7 +73,31 @@ Ref GFEntity::_add_component(Variant component, Array members) { return this; } -Ref GFEntity::add_pair(Variant first, Variant second, Array members) { +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); @@ -132,6 +156,12 @@ Ref GFEntity::get_component(Variant component) { return c; } +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)); +} + Ref GFEntity::set_component( const Variant** args, GDExtensionInt arg_count, GDExtensionCallError &error ) { @@ -204,6 +234,41 @@ Ref GFEntity::_set_component( 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); +} + void GFEntity::delete_() { ecs_delete(get_world()->raw(), get_id()); } @@ -327,9 +392,24 @@ void GFEntity::_bind_methods() { godot::ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, StringName("set_component"), &GFEntity::set_component, mi); } - godot::ClassDB::bind_method(D_METHOD("add_pair", "first", "second", "data"), &GFEntity::add_pair, Array()); + { + 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("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 2cc02ae..d929ef4 100644 --- a/cpp/src/entity.h +++ b/cpp/src/entity.h @@ -37,15 +37,18 @@ namespace godot { 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_pair(Variant, Variant, Array data); Ref add_tag(Variant); Ref get_component(Variant); - - Ref set_component(const Variant**, GDExtensionInt, GDExtensionCallError&); - Ref _set_component(Variant, Array); - + 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/world.cpp b/cpp/src/world.cpp index 7a9c948..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() { @@ -757,6 +747,34 @@ Ref GFWorld::register_script_id_no_user_call(Ref