instantiate with meta calls

This commit is contained in:
2019-11-02 03:48:02 +07:00
parent ee0393d5f7
commit 1fc8d3ff77
7 changed files with 220 additions and 237 deletions

View File

@@ -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<sol::protected_function> f = behaviour.meta()[method];
if ( !f ) {
return call_result::method_not_found;
}
sol::protected_function_result r = f->call(
behaviour.meta(),
std::forward<Args>(args)...);
if ( !r.valid() ) {
the<debug>().error("BEHAVIOUR: Behaviour method error:\n"
"--> Method: %0\n"
"--> Error: %1",
method,
sol::error(r).what());
return call_result::failed;
}
return call_result::success;
}
}

View File

@@ -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<ecs::entity_id, gobject> gobjects_;
gobject_destroying_states destroying_states_;
};
}

View File

@@ -103,7 +103,7 @@ namespace
the<world>().instantiate(
prefab,
scene_i,
scene_i.component<actor>()->node(),
make_trs3(v3f{0,50.f,0}, q4f::identity(), v3f{20.f}));
}
@@ -117,7 +117,7 @@ namespace
the<world>().instantiate(
prefab,
scene_i,
scene_i.component<actor>()->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<world>().instantiate(prefab_a, scene_i, trans);
gobject inst = the<world>().instantiate(
prefab_a,
scene_i.component<actor>()->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<world>().instantiate(prefab_b, inst);
the<world>().instantiate(
prefab_b,
inst.component<actor>()->node());
}
}

View File

@@ -6,6 +6,8 @@
#include <enduro2d/high/components/behaviour.hpp>
#include <enduro2d/high/assets/script_asset.hpp>
namespace e2d
{
const char* factory_loader<behaviour>::schema_source = R"json({
@@ -24,6 +26,7 @@ namespace e2d
if ( ctx.root.HasMember("script") ) {
auto script = ctx.dependencies.find_asset<script_asset>(
path::combine(ctx.parent_address, ctx.root["script"].GetString()));
if ( !script ) {
the<debug>().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<debug>().error("BEHAVIOUR: Behaviour script error:\n"
"--> Error: %0",
sol::error(meta).what());
return false;
}
if ( meta.get_type() != sol::type::table ) {
the<debug>().error("BEHAVIOUR: Behaviour script must return the meta table");
return false;
}
component.meta(std::move(meta));
}
return true;

View File

@@ -7,6 +7,8 @@
#include <enduro2d/high/world.hpp>
#include <enduro2d/high/components/actor.hpp>
#include <enduro2d/high/components/behaviour.hpp>
#include <enduro2d/high/components/disabled.hpp>
namespace
{
@@ -60,12 +62,104 @@ namespace
};
}
namespace
{
using namespace e2d;
void delete_instance(const gobject& inst) noexcept {
gcomponent<actor> 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<gobject_state>(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<gobject_state>(world, ent)};
} catch (...) {
ent.destroy();
throw;
}
try {
auto n = node::create(inst);
gcomponent<actor> 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<actor> inst_a{inst};
gcomponent<actor> 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<actor> inst_a{inst}; inst_a ) {
nodes::for_extracted_nodes(inst_a->node(), [](const node_iptr& node){
if ( gcomponent<behaviour> inst_b{node->owner()}; inst_b ) {
behaviours::call_meta_method(
*inst_b,
"on_shutdown",
node->owner());
}
});
}
}
void start_instance(gobject& inst) {
if ( gcomponent<actor> inst_a{inst}; inst_a ) {
nodes::for_extracted_nodes(inst_a->node(), [&inst](const node_iptr& node){
if ( gcomponent<behaviour> 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<disabled<behaviour>>().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<gobject_state>(
*this,
registry_.create_entity())};
gobjects_.emplace(inst.raw_entity().id(), inst);
try {
auto n = node::create(inst);
gcomponent<actor> 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<gobject_state>(
*this,
registry_.create_entity(prefab.prototype()))};
gobjects_.emplace(inst.raw_entity().id(), inst);
return instantiate(prefab, nullptr);
}
try {
auto n = node::create(inst);
gcomponent<actor> 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<actor>()->node());
}
try {
for ( const auto& child_prefab : prefab.children() ) {
auto child = instantiate(child_prefab);
try {
gcomponent<actor> inst_a{inst};
gcomponent<actor> 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<actor> 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<actor>{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<actor>{go}->node());
}
return go;
}
gobject world::instantiate(const prefab& prefab, const gobject& parent) {
gobject go = instantiate(prefab);
if ( go && parent ) {
try {
gcomponent<actor> 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<actor>{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<actor>{go}->node());
}
return go;
}
gobject world::instantiate(const gobject& parent, const t3f& transform) {
gobject go = instantiate(parent);
if ( go ) {
gcomponent<actor>{go}->node()->transform(transform);
}
return go;
}
gobject world::instantiate(const node_iptr& parent, const t3f& transform) {
gobject go = instantiate(parent);
if ( go ) {
gcomponent<actor>{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<actor>{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<actor>{go}->node()->transform(transform);
gobject inst = new_instance(*this, prefab);
inst.component<actor>()->node()->transform(transform);
if ( parent ) {
parent->add_child(inst.component<actor>()->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<gobject_state>(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<actor> 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<gobject_state>(inst.internal_state())->mark_invalided();
shutdown_instance(inst);
delete_instance(inst);
}
}
}

View File

@@ -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<m2f, bool> 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<m3f, bool> 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<m4f, bool> 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));

View File

@@ -617,13 +617,13 @@ TEST_CASE("node") {
const vector<node_iptr> ns{p, c1, c2, c3};
{
vector<node_iptr> 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<const_node_iptr> 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]);