From 1fc8d3ff77556f671d22324ddcd790686f937b01 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sat, 2 Nov 2019 03:48:02 +0700 Subject: [PATCH] instantiate with meta calls --- .../enduro2d/high/components/behaviour.hpp | 58 ++-- headers/enduro2d/high/world.hpp | 13 - samples/sources/sample_03/sample_03.cpp | 13 +- .../enduro2d/high/components/behaviour.cpp | 21 +- sources/enduro2d/high/world.cpp | 300 ++++++++---------- untests/sources/untests_high/luasol.cpp | 48 +-- untests/sources/untests_high/node.cpp | 4 +- 7 files changed, 220 insertions(+), 237 deletions(-) diff --git a/headers/enduro2d/high/components/behaviour.hpp b/headers/enduro2d/high/components/behaviour.hpp index 19f5636e..a01a179c 100644 --- a/headers/enduro2d/high/components/behaviour.hpp +++ b/headers/enduro2d/high/components/behaviour.hpp @@ -9,26 +9,20 @@ #include "../_high.hpp" #include "../factory.hpp" -#include "../assets/script_asset.hpp" namespace e2d { class behaviour final { public: behaviour() = default; - behaviour(const script_asset::ptr& script); - behaviour& meta(sol::table&& value) noexcept; - behaviour& meta(const sol::table& value); + behaviour& meta(sol::table value) noexcept; [[nodiscard]] sol::table& meta() noexcept; [[nodiscard]] const sol::table& meta() const noexcept; - - behaviour& script(const script_asset::ptr& value) noexcept; - [[nodiscard]] const script_asset::ptr& script() const noexcept; private: + str name_; sol::table meta_; - script_asset::ptr script_; }; template <> @@ -48,19 +42,11 @@ namespace e2d namespace e2d { - inline behaviour::behaviour(const script_asset::ptr& value) - : script_(value) {} - - inline behaviour& behaviour::meta(sol::table&& value) noexcept { + inline behaviour& behaviour::meta(sol::table value) noexcept { meta_ = std::move(value); return *this; } - inline behaviour& behaviour::meta(const sol::table& value) { - meta_ = value; - return *this; - } - inline sol::table& behaviour::meta() noexcept { return meta_; } @@ -68,13 +54,39 @@ namespace e2d inline const sol::table& behaviour::meta() const noexcept { return meta_; } +} - inline behaviour& behaviour::script(const script_asset::ptr& value) noexcept { - script_ = value; - return *this; - } +namespace e2d::behaviours +{ + enum class call_result { + failed, + success, + method_not_found, + }; - inline const script_asset::ptr& behaviour::script() const noexcept { - return script_; + template < typename... Args > + call_result call_meta_method(behaviour& behaviour, str_view method, Args&&... args) { + if ( method.empty() || !behaviour.meta() || !behaviour.meta().valid() ) { + return call_result::method_not_found; + } + + sol::optional f = behaviour.meta()[method]; + if ( !f ) { + return call_result::method_not_found; + } + + sol::protected_function_result r = f->call( + behaviour.meta(), + std::forward(args)...); + if ( !r.valid() ) { + the().error("BEHAVIOUR: Behaviour method error:\n" + "--> Method: %0\n" + "--> Error: %1", + method, + sol::error(r).what()); + return call_result::failed; + } + + return call_result::success; } } diff --git a/headers/enduro2d/high/world.hpp b/headers/enduro2d/high/world.hpp index aa33caa2..291b1190 100644 --- a/headers/enduro2d/high/world.hpp +++ b/headers/enduro2d/high/world.hpp @@ -25,29 +25,16 @@ namespace e2d gobject instantiate(); gobject instantiate(const prefab& prefab); - gobject instantiate(const gobject& parent); gobject instantiate(const node_iptr& parent); - - gobject instantiate(const prefab& prefab, const gobject& parent); gobject instantiate(const prefab& prefab, const node_iptr& parent); - gobject instantiate(const gobject& parent, const t3f& transform); gobject instantiate(const node_iptr& parent, const t3f& transform); - - gobject instantiate(const prefab& prefab, const gobject& parent, const t3f& transform); gobject instantiate(const prefab& prefab, const node_iptr& parent, const t3f& transform); void destroy_instance(gobject& inst) noexcept; void finalize_instances() noexcept; - - gobject resolve(ecs::entity_id ent) const noexcept; - gobject resolve(const ecs::const_entity& ent) const noexcept; - private: - void destroy_instances_() noexcept; - void finalize_instance_(gobject& inst) noexcept; private: ecs::registry registry_; - hash_map gobjects_; gobject_destroying_states destroying_states_; }; } diff --git a/samples/sources/sample_03/sample_03.cpp b/samples/sources/sample_03/sample_03.cpp index 06b2a686..daefe26b 100644 --- a/samples/sources/sample_03/sample_03.cpp +++ b/samples/sources/sample_03/sample_03.cpp @@ -103,7 +103,7 @@ namespace the().instantiate( prefab, - scene_i, + scene_i.component()->node(), make_trs3(v3f{0,50.f,0}, q4f::identity(), v3f{20.f})); } @@ -117,7 +117,7 @@ namespace the().instantiate( prefab, - scene_i, + scene_i.component()->node(), math::make_translation_trs3(v3f{0,-50.f,0})); } @@ -139,7 +139,10 @@ namespace {-80.f + j * 40.f, -200.f + i * 40.f, 0}, q4f::identity(), {2.f,2.f,1.f}}; - gobject inst = the().instantiate(prefab_a, scene_i, trans); + gobject inst = the().instantiate( + prefab_a, + scene_i.component()->node(), + trans); prefab prefab_b = prefab_a; prefab_b.prototype() @@ -149,7 +152,9 @@ namespace q4f::identity(), v3f{0.3f,0.3f,3.f}))); - the().instantiate(prefab_b, inst); + the().instantiate( + prefab_b, + inst.component()->node()); } } diff --git a/sources/enduro2d/high/components/behaviour.cpp b/sources/enduro2d/high/components/behaviour.cpp index 94678ef8..95f8143a 100644 --- a/sources/enduro2d/high/components/behaviour.cpp +++ b/sources/enduro2d/high/components/behaviour.cpp @@ -6,6 +6,8 @@ #include +#include + namespace e2d { const char* factory_loader::schema_source = R"json({ @@ -24,6 +26,7 @@ namespace e2d if ( ctx.root.HasMember("script") ) { auto script = ctx.dependencies.find_asset( path::combine(ctx.parent_address, ctx.root["script"].GetString())); + if ( !script ) { the().error("BEHAVIOUR: Dependency 'script' is not found:\n" "--> Parent address: %0\n" @@ -32,7 +35,23 @@ namespace e2d ctx.root["script"].GetString()); return false; } - component.script(script); + + //TODO(BlackMat): thread safe? + sol::protected_function_result meta = script->content().call(); + + if ( !meta.valid() ) { + the().error("BEHAVIOUR: Behaviour script error:\n" + "--> Error: %0", + sol::error(meta).what()); + return false; + } + + if ( meta.get_type() != sol::type::table ) { + the().error("BEHAVIOUR: Behaviour script must return the meta table"); + return false; + } + + component.meta(std::move(meta)); } return true; diff --git a/sources/enduro2d/high/world.cpp b/sources/enduro2d/high/world.cpp index 76b9a827..767460f7 100644 --- a/sources/enduro2d/high/world.cpp +++ b/sources/enduro2d/high/world.cpp @@ -7,6 +7,8 @@ #include #include +#include +#include namespace { @@ -60,12 +62,104 @@ namespace }; } +namespace +{ + using namespace e2d; + + void delete_instance(const gobject& inst) noexcept { + gcomponent inst_a{inst}; + auto inst_n = inst_a ? inst_a->node() : nullptr; + + if ( inst_n ) { + inst_n->for_each_child([](const node_iptr& child_n){ + delete_instance(child_n->owner()); + }); + } + + if ( inst ) { + auto inst_g = dynamic_pointer_cast(inst.internal_state()); + inst_g->raw_entity().destroy(); + inst_g->mark_destroyed(); + inst_g->mark_invalided(); + } + } + + gobject new_instance(world& world, const prefab& prefab) { + gobject inst; + ecs::entity ent = world.registry().create_entity(prefab.prototype()); + + try { + inst = gobject{make_intrusive(world, ent)}; + } catch (...) { + ent.destroy(); + throw; + } + + try { + auto n = node::create(inst); + gcomponent inst_a{inst}; + if ( inst_a && inst_a->node() ) { + n->transform(inst_a->node()->transform()); + } + inst_a.assign(std::move(n)); + } catch (...) { + delete_instance(inst); + throw; + } + + try { + for ( const auto& child_prefab : prefab.children() ) { + auto child = new_instance(world, child_prefab); + try { + gcomponent inst_a{inst}; + gcomponent child_a{child}; + inst_a->node()->add_child(child_a->node()); + } catch (...) { + delete_instance(child); + throw; + } + } + } catch (...) { + delete_instance(inst); + throw; + } + + return inst; + } + + void shutdown_instance(gobject& inst) noexcept { + if ( gcomponent inst_a{inst}; inst_a ) { + nodes::for_extracted_nodes(inst_a->node(), [](const node_iptr& node){ + if ( gcomponent inst_b{node->owner()}; inst_b ) { + behaviours::call_meta_method( + *inst_b, + "on_shutdown", + node->owner()); + } + }); + } + } + + void start_instance(gobject& inst) { + if ( gcomponent inst_a{inst}; inst_a ) { + nodes::for_extracted_nodes(inst_a->node(), [&inst](const node_iptr& node){ + if ( gcomponent inst_b{node->owner()}; inst_b ) { + const auto result = behaviours::call_meta_method( + *inst_b, + "on_start", + node->owner()); + if ( result == behaviours::call_result::failed ) { + inst.component>().assign(); + } + } + }); + } + } +} + namespace e2d { - world::~world() noexcept { - destroy_instances_(); - finalize_instances(); - } + world::~world() noexcept = default; ecs::registry& world::registry() noexcept { return registry_; @@ -76,157 +170,61 @@ namespace e2d } gobject world::instantiate() { - gobject inst{make_intrusive( - *this, - registry_.create_entity())}; - gobjects_.emplace(inst.raw_entity().id(), inst); - - try { - auto n = node::create(inst); - gcomponent inst_a{inst}; - if ( inst_a && inst_a->node() ) { - n->transform(inst_a->node()->transform()); - } - inst_a.assign(n); - } catch (...) { - finalize_instance_(inst); - throw; - } - - return inst; + return instantiate(nullptr); } gobject world::instantiate(const prefab& prefab) { - gobject inst{make_intrusive( - *this, - registry_.create_entity(prefab.prototype()))}; - gobjects_.emplace(inst.raw_entity().id(), inst); + return instantiate(prefab, nullptr); + } - try { - auto n = node::create(inst); - gcomponent inst_a{inst}; - if ( inst_a && inst_a->node() ) { - n->transform(inst_a->node()->transform()); - } - inst_a.assign(n); - } catch (...) { - finalize_instance_(inst); - throw; + gobject world::instantiate(const node_iptr& parent) { + return instantiate(prefab(), parent); + } + + gobject world::instantiate(const prefab& prefab, const node_iptr& parent) { + gobject inst = new_instance(*this, prefab); + + if ( parent ) { + parent->add_child(inst.component()->node()); } try { - for ( const auto& child_prefab : prefab.children() ) { - auto child = instantiate(child_prefab); - try { - gcomponent inst_a{inst}; - gcomponent child_a{child}; - inst_a->node()->add_child(child_a->node()); - } catch (...) { - finalize_instance_(child); - throw; - } - } + start_instance(inst); } catch (...) { - finalize_instance_(inst); + delete_instance(inst); throw; } return inst; } - gobject world::instantiate(const gobject& parent) { - gobject go = instantiate(); - if ( go && parent ) { - try { - gcomponent parent_a{parent}; - if ( !parent_a ) { - parent_a.assign(node::create(parent)); - } - if ( !parent_a->node() ) { - parent_a->node(node::create(parent)); - } - parent_a->node()->add_child(gcomponent{go}->node()); - } catch (...) { - finalize_instance_(go); - throw; - } - } - return go; - } - - gobject world::instantiate(const node_iptr& parent) { - gobject go = instantiate(); - if ( go && parent ) { - parent->add_child(gcomponent{go}->node()); - } - return go; - } - - gobject world::instantiate(const prefab& prefab, const gobject& parent) { - gobject go = instantiate(prefab); - if ( go && parent ) { - try { - gcomponent parent_a{parent}; - if ( !parent_a ) { - parent_a.assign(node::create(parent)); - } - if ( !parent_a->node() ) { - parent_a->node(node::create(parent)); - } - parent_a->node()->add_child(gcomponent{go}->node()); - } catch (...) { - finalize_instance_(go); - throw; - } - } - return go; - } - - gobject world::instantiate(const prefab& prefab, const node_iptr& parent) { - gobject go = instantiate(prefab); - if ( go && parent ) { - parent->add_child(gcomponent{go}->node()); - } - return go; - } - - gobject world::instantiate(const gobject& parent, const t3f& transform) { - gobject go = instantiate(parent); - if ( go ) { - gcomponent{go}->node()->transform(transform); - } - return go; - } - gobject world::instantiate(const node_iptr& parent, const t3f& transform) { - gobject go = instantiate(parent); - if ( go ) { - gcomponent{go}->node()->transform(transform); - } - return go; - } - - gobject world::instantiate(const prefab& prefab, const gobject& parent, const t3f& transform) { - gobject go = instantiate(prefab, parent); - if ( go ) { - gcomponent{go}->node()->transform(transform); - } - return go; + return instantiate(prefab(), parent, transform); } gobject world::instantiate(const prefab& prefab, const node_iptr& parent, const t3f& transform) { - gobject go = instantiate(prefab, parent); - if ( go ) { - gcomponent{go}->node()->transform(transform); + gobject inst = new_instance(*this, prefab); + inst.component()->node()->transform(transform); + + if ( parent ) { + parent->add_child(inst.component()->node()); } - return go; + + try { + start_instance(inst); + } catch (...) { + delete_instance(inst); + throw; + } + + return inst; } void world::destroy_instance(gobject& inst) noexcept { auto gstate = inst ? dynamic_pointer_cast(inst.internal_state()) : nullptr; - if ( gstate && !gstate->destroyed() ) { + if ( gstate && !gstate->destroyed() && !gstate->invalided() ) { gstate->mark_destroyed(); destroying_states_.push_back(*gstate); } @@ -236,46 +234,8 @@ namespace e2d while ( !destroying_states_.empty() ) { gobject inst{&destroying_states_.front()}; destroying_states_.pop_front(); - finalize_instance_(inst); - } - } - - gobject world::resolve(ecs::entity_id ent) const noexcept { - E2D_ASSERT(registry_.valid_entity(ent)); - const auto iter = gobjects_.find(ent); - return iter != gobjects_.end() - ? iter->second - : gobject(); - } - - gobject world::resolve(const ecs::const_entity& ent) const noexcept { - E2D_ASSERT(registry_.valid_entity(ent)); - const auto iter = gobjects_.find(ent.id()); - return iter != gobjects_.end() - ? iter->second - : gobject(); - } - - void world::destroy_instances_() noexcept { - for ( auto& [_, go] : gobjects_ ) { - destroy_instance(go); - } - } - - void world::finalize_instance_(gobject& inst) noexcept { - gcomponent inst_a{inst}; - auto inst_n = inst_a ? inst_a->node() : nullptr; - - if ( inst_n ) { - inst_n->for_each_child([this](const node_iptr& child_n){ - destroy_instance(child_n->owner()); - }); - } - - if ( inst ) { - inst.raw_entity().destroy(); - gobjects_.erase(inst.raw_entity().id()); - dynamic_pointer_cast(inst.internal_state())->mark_invalided(); + shutdown_instance(inst); + delete_instance(inst); } } } diff --git a/untests/sources/untests_high/luasol.cpp b/untests/sources/untests_high/luasol.cpp index a85fa85b..cd115576 100644 --- a/untests/sources/untests_high/luasol.cpp +++ b/untests/sources/untests_high/luasol.cpp @@ -31,20 +31,20 @@ TEST_CASE("luasol") { SECTION("vec2/vec3/vec4") { v2f r0 = l.with_state([](sol::state& lua){ return lua.script(R"lua( - local v = v2f.new(1,2) - return v2f.new((v + v + 2).y) + local v = e2d.v2f.new(1,2) + return e2d.v2f.new((v + v + 2).y) )lua"); }); v3f r1 = l.with_state([](sol::state& lua){ return lua.script(R"lua( - local v = v3f.new(1,2,3) - return v3f.new((v + v + 2).y) + local v = e2d.v3f.new(1,2,3) + return e2d.v3f.new((v + v + 2).y) )lua"); }); v4f r2 = l.with_state([](sol::state& lua){ return lua.script(R"lua( - local v = v4f.new(1,2,3,4) - return v4f.new((v + v + 2).y) + local v = e2d.v4f.new(1,2,3,4) + return e2d.v4f.new((v + v + 2).y) )lua"); }); REQUIRE(r0 == v2f(6)); @@ -55,7 +55,7 @@ TEST_CASE("luasol") { SECTION("quat") { v3f r0 = l.with_state([](sol::state& lua){ return lua.script(R"lua( - return v3f.new(1,2,3) * q4f.make_quat_from_axis_angle(radf.new(10), v3f.new(1,2,3)) + return e2d.v3f.new(1,2,3) * e2d.q4f.make_quat_from_axis_angle(e2d.radf.new(10), e2d.v3f.new(1,2,3)) )lua"); }); REQUIRE(r0 == v3f(1,2,3) * math::make_quat_from_axis_angle(radf(10.f), v3f(1,2,3))); @@ -64,23 +64,23 @@ TEST_CASE("luasol") { SECTION("mat2/mat2/mat3") { std::pair r0 = l.with_state([](sol::state& lua){ return lua.script(R"lua( - local m = m2f.make_scale_matrix2(2,3) - local rm, s = m2f.inversed(m) - return rm * m2f.identity(), s + local m = e2d.m2f.make_scale_matrix2(2,3) + local rm, s = e2d.m2f.inversed(m) + return rm * e2d.m2f.identity(), s )lua"); }); std::pair r1 = l.with_state([](sol::state& lua){ return lua.script(R"lua( - local m = m3f.make_rotation_matrix3(degf.new(45),2,3,4) - local rm, s = m3f.inversed(m) - return rm * m3f.identity(), s + local m = e2d.m3f.make_rotation_matrix3(e2d.degf.new(45),2,3,4) + local rm, s = e2d.m3f.inversed(m) + return rm * e2d.m3f.identity(), s )lua"); }); std::pair r2 = l.with_state([](sol::state& lua){ return lua.script(R"lua( - local m = m4f.make_translation_matrix4(2,3,4) - local rm, s = m4f.inversed(m) - return rm * m4f.identity(), s + local m = e2d.m4f.make_translation_matrix4(2,3,4) + local rm, s = e2d.m4f.inversed(m) + return rm * e2d.m4f.identity(), s )lua"); }); REQUIRE(r0.second); @@ -94,15 +94,15 @@ TEST_CASE("luasol") { SECTION("rect/aabb") { bool r0 = l.with_state([](sol::state& lua){ return lua.script(R"lua( - local b = b2f.unit() * 2 - return b:inside(v2f.new(1.5,1.5)) + local b = e2d.b2f.unit() * 2 + return b:inside(e2d.v2f.new(1.5,1.5)) )lua"); }); REQUIRE(r0); bool r1 = l.with_state([](sol::state& lua){ return lua.script(R"lua( - local b = b3f.unit() * 2 - return b:overlaps(b3f.new(1.5,1.5,1.5,2,2,2)) + local b = e2d.b3f.unit() * 2 + return b:overlaps(e2d.b3f.new(1.5,1.5,1.5,2,2,2)) )lua"); }); REQUIRE(r1); @@ -111,14 +111,14 @@ TEST_CASE("luasol") { SECTION("trs2/trs3") { radf r0 = l.with_state([](sol::state& lua){ return lua.script(R"lua( - local t = t2f.make_rotation_trs2(degf.new(45)) + local t = e2d.t2f.make_rotation_trs2(e2d.degf.new(45)) return t.rotation )lua"); }); REQUIRE(r0 == math::to_rad(degf(45.f))); v3f r1 = l.with_state([](sol::state& lua){ return lua.script(R"lua( - local t = t3f.make_translation_trs3(v3f.new(1,2,3)) + local t = e2d.t3f.make_translation_trs3(e2d.v3f.new(1,2,3)) return t.translation )lua"); }); @@ -128,13 +128,13 @@ TEST_CASE("luasol") { SECTION("color/color32") { color r0 = l.with_state([](sol::state& lua){ return lua.script(R"lua( - return color.white() * 0.5 + return e2d.color.white() * 0.5 )lua"); }); REQUIRE(r0 == color(0.5f,0.5f,0.5f,0.5f)); color32 r1 = l.with_state([](sol::state& lua){ return lua.script(R"lua( - return color32.white() - 1 + return e2d.color32.white() - 1 )lua"); }); REQUIRE(r1 == color32(254,254,254,254)); diff --git a/untests/sources/untests_high/node.cpp b/untests/sources/untests_high/node.cpp index 4079a4ce..5c78f4ca 100644 --- a/untests/sources/untests_high/node.cpp +++ b/untests/sources/untests_high/node.cpp @@ -617,13 +617,13 @@ TEST_CASE("node") { const vector ns{p, c1, c2, c3}; { vector ns2; - REQUIRE(4u == p->extract_all_nodes(std::back_inserter(ns2))); + REQUIRE(4u == nodes::extract_nodes(p, std::back_inserter(ns2))); REQUIRE(ns == ns2); } { const_node_iptr cp = p; vector ns2; - REQUIRE(4u == cp->extract_all_nodes(std::back_inserter(ns2))); + REQUIRE(4u == nodes::extract_nodes(cp, std::back_inserter(ns2))); REQUIRE(ns.size() == ns2.size()); for ( std::size_t i = 0; i < ns.size(); ++i ) { REQUIRE(ns[i] == ns2[i]);