Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GFTag class and related tests #60

Merged
merged 4 commits into from
Jan 10, 2025
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
3 changes: 1 addition & 2 deletions cpp/src/doc_classes/GFComponent.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GFComponent" inherits="GFRegisterableEntity"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd">
<class name="GFComponent" inherits="GFRegisterableEntity" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd">
<brief_description>
A reference to a Glecs component from a [GFWorld] that is attatached to an entity.
</brief_description>
Expand Down
3 changes: 2 additions & 1 deletion cpp/src/doc_classes/GFComponentBuilder.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GFComponentBuilder" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd">
<class name="GFComponentBuilder" inherits="RefCounted"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd">
<brief_description>
Builder for a new component type.
</brief_description>
Expand Down
31 changes: 15 additions & 16 deletions cpp/src/doc_classes/GFEntity.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GFEntity" inherits="RefCounted"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd">
<class name="GFEntity" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd">
<brief_description>
A reference to an entity from a [GFWorld].
</brief_description>
Expand All @@ -10,6 +9,20 @@
<link title="More on Flecs Entities">https://www.flecs.dev/flecs/md_docs_2EntitiesComponents.html</link>
</tutorials>
<methods>
<method name="add" qualifiers="const vararg">
<return type="GFEntity" />
<param index="0" name="component" type="Variant" />
<description>
Adds component data of type [param component] 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.new()
entity.add(GFPosition2D, Vector2(10, 10))
entity.get(GFPosition2D).get_vec() == Vector2(10, 10) # true
[/codeblock]
This method returns [code]self[/code] for chaining.
</description>
</method>
<method name="add_child">
<return type="GFEntity" />
<param index="0" name="entity" type="Variant" />
Expand All @@ -32,20 +45,6 @@
This codeblock returns [code]self[/code] for chaining.
</description>
</method>
<method name="add" qualifiers="const vararg">
<return type="GFEntity" />
<param index="0" name="component" type="Variant" />
<description>
Adds component data of type [param component] 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.new()
entity.add(GFPosition2D, Vector2(10, 10))
entity.get(GFPosition2D).get_vec() == Vector2(10, 10) # true
[/codeblock]
This method returns [code]self[/code] for chaining.
</description>
</method>
<method name="add_pair" qualifiers="const vararg">
<return type="GFEntity" />
<param index="0" name="first" type="Variant" />
Expand Down
51 changes: 51 additions & 0 deletions cpp/src/doc_classes/GFTag.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GFTag" inherits="GFRegisterableEntity"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd">
<brief_description>
A reference to an entity with no data.
</brief_description>
<description>
Tags are a kind of entity that can be attached to other entities to mark them without adding data, unlike components which will add data when attatched.
</description>
<tutorials>
<link title="More on Flecs Tags">https://www.flecs.dev/flecs/md_docs_2Quickstart.html#tag</link>
</tutorials>
<methods>
<method name="from" qualifiers="static">
<return type="GFTag" />
<param index="0" name="tag" type="Variant" />
<param index="1" name="world" type="GFWorld" default="null" />
<description>
Returns an entity from an ID coerced from a [Variant].
If no world is specified, a default world is used.
To learn more about [Variant] coercion see [method GFWorld.coerce_id].
[codeblock]
var entity:= GFTag.from("flecs/core/Trait")
[/codeblock]
</description>
</method>
<method name="from_id" qualifiers="static">
<return type="GFTag" />
<param index="0" name="tag_id" type="int" />
<param index="1" name="world" type="GFWorld" default="null" />
<description>
Returns an tag from an ID.
If no world is specified, a default world is used.
[codeblock]
var entity:= GFEntity.from_id(255)
[/codeblock]
</description>
</method>
<method name="new_in_world" qualifiers="static">
<return type="GFTag" />
<param index="0" name="world" type="GFWorld" />
<description>
Returns a reference to a new entity created within [param world].
[codeblock]
var world:= GFWorld.new()
var entity:= GFEntity.new_in_world(world)
[/codeblock]
</description>
</method>
</methods>
</class>
9 changes: 9 additions & 0 deletions cpp/src/register_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "entity_builder.h"
#include "gdextension_interface.h"
#include "godot_cpp/classes/engine.hpp"
#include "godot_cpp/core/memory.hpp"
#include "module.h"
#include "observer_builder.h"
#include "pair.h"
Expand All @@ -16,6 +17,7 @@
#include "register_types.h"
#include "registerable_entity.h"
#include "system_builder.h"
#include "tag.h"
#include "world.h"

using namespace godot;
Expand All @@ -34,6 +36,7 @@ void initialize_module(ModuleInitializationLevel p_level) {
godot::ClassDB::register_class<GFRegisterableEntity>();
godot::ClassDB::register_class<GFComponent>();
godot::ClassDB::register_class<GFModule>();
godot::ClassDB::register_class<GFTag>();

godot::ClassDB::register_class<GFEntityBuilder>();
godot::ClassDB::register_class<GFComponentBuilder>();
Expand All @@ -44,6 +47,12 @@ void initialize_module(ModuleInitializationLevel p_level) {
godot::ClassDB::register_class<GFObserverBuilder>();
godot::ClassDB::register_class<GFQueryBuilder>();
godot::ClassDB::register_class<GFSystemBuilder>();

if (Engine::get_singleton()->has_singleton(GFWorld::SINGLETON_NAME)) {
Object* world = Engine::get_singleton()->get_singleton(GFWorld::SINGLETON_NAME);
Engine::get_singleton()->unregister_singleton(GFWorld::SINGLETON_NAME);
memdelete(world);
}
Engine::get_singleton()->register_singleton(GFWorld::SINGLETON_NAME, memnew(GFWorld));
break;
case MODULE_INITIALIZATION_LEVEL_EDITOR:
Expand Down
46 changes: 46 additions & 0 deletions cpp/src/tag.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

#include "tag.h"
#include "utils.h"
#include <stdint.h>
#include <flecs.h>
#include "godot_cpp/classes/wrapped.hpp"
#include "godot_cpp/variant/array.hpp"
#include "world.h"
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/variant/utility_functions.hpp>

using namespace godot;

GFTag::~GFTag() {
}

Ref<GFTag> GFTag::new_in_world(GFWorld* world) {
return memnew(GFTag(world));
}

Ref<GFTag> GFTag::from(Variant tag, GFWorld* world) {
ecs_entity_t tag_id = world->coerce_id(tag);
return from_id(tag_id, world);
}

Ref<GFTag> GFTag::from_id(ecs_entity_t tag_id, GFWorld* world_) {
GFWorld* world = GFWorld::world_or_singleton(world_);
Ref<GFTag> ett = memnew(GFTag(
tag_id,
world
));
if (!ett->is_alive()) {
ERR(nullptr,
"Could not instantiate tag from ID\n",
"World/ID is not valid/alive"
);
}
return ett;
}


void GFTag::_bind_methods() {
godot::ClassDB::bind_static_method(get_class_static(), D_METHOD("new_in_world", "world"), &GFTag::new_in_world);
godot::ClassDB::bind_static_method(get_class_static(), D_METHOD("from", "tag", "world"), &GFTag::from, nullptr);
godot::ClassDB::bind_static_method(get_class_static(), D_METHOD("from_id", "tag_id", "world"), &GFTag::from_id, nullptr);
}
63 changes: 63 additions & 0 deletions cpp/src/tag.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

#ifndef GF_TAG_H
#define GF_TAG_H

#include "registerable_entity.h"
#include "world.h"

#include <flecs.h>
#include <godot_cpp/classes/ref_counted.hpp>
#include <godot_cpp/variant/string.hpp>

namespace godot {

class GFTag : public GFRegisterableEntity {
GDCLASS(GFTag, GFRegisterableEntity)

public:
/// Create a new named module
GFTag(GFWorld* world):
GFRegisterableEntity(
ecs_new(
world->raw()
),
world
)
{}
/// Create from existing ID
GFTag(ecs_entity_t id, GFWorld* world):
GFRegisterableEntity(
id,
world
)
{}
GFTag():
GFRegisterableEntity(GFWorld::singleton())
{}

~GFTag();

// --------------------------------------
// --- Exposed
// --------------------------------------

static Ref<GFTag> new_in_world(GFWorld* world);
static Ref<GFTag> new_named_in_world(String name, GFWorld*);
static Ref<GFTag> from(Variant module, GFWorld*);
static Ref<GFTag> from_id(ecs_entity_t, GFWorld*);

// --------------------------------------
// --- Unexposed
// --------------------------------------

protected:
static void _bind_methods();


private:

};

}

#endif
4 changes: 2 additions & 2 deletions unittests/test_entities.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ extends GutTest

var world:GFWorld = null

func before_all():
func before_each():
world = GFWorld.new()

func after_all():
func after_each():
world.free()

#region Tests
Expand Down
29 changes: 29 additions & 0 deletions unittests/test_tags.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

extends GutTest

var world:GFWorld = null

func before_each():
world = GFWorld.new()

func after_each():
world.free()

#region Tests

func _test_tag_creation():
var enemy:= GFTag.new_in_world(world)
var robot:=GFEntity.new_in_world(world) \
.add(enemy)

assert_true(robot.has_entity(enemy), "Expected `robot` to have the `enemy` tag")

#endregion

#region Classes

class Eats extends GFTag: pass

class Grass extends GFTag: pass

#endregion