Merge branch 'feature/safe_gobject' into feature/scripting

This commit is contained in:
2019-10-12 21:41:35 +07:00
8 changed files with 170 additions and 95 deletions

View File

@@ -74,11 +74,6 @@ namespace e2d
class atlas;
class flipbook;
class gobject;
template < typename T >
class gcomponent;
template < typename T >
class const_gcomponent;
class model;
class node;
class prefab;
@@ -89,6 +84,12 @@ namespace e2d
class luasol;
class starter;
class world;
class gobject;
template < typename T >
class gcomponent;
template < typename T >
class const_gcomponent;
}
namespace sol

View File

@@ -8,17 +8,6 @@
#include "_high.hpp"
namespace e2d
{
class gobject;
template < typename T >
class gcomponent;
template < typename T >
class const_gcomponent;
}
namespace e2d
{
class gobject final {
@@ -33,6 +22,8 @@ namespace e2d
bool valid() const noexcept;
explicit operator bool() const noexcept;
void destroy() noexcept;
template < typename T >
gcomponent<T> component() noexcept;
@@ -110,31 +101,23 @@ namespace e2d
namespace e2d
{
class gobject_destroying_state_ilist_tag;
using gobject_destroying_states = intrusive_list<
gobject::state,
gobject_destroying_state_ilist_tag>;
class gobject::state
: private noncopyable
, public ref_counter<state> {
, public ref_counter<state>
, public intrusive_list_hook<gobject_destroying_state_ilist_tag> {
public:
virtual ~state() noexcept {}
virtual bool valid() const noexcept = 0;
virtual void destroy() noexcept = 0;
virtual bool invalided() const noexcept = 0;
virtual ecs::entity raw_entity() noexcept = 0;
virtual ecs::const_entity raw_entity() const noexcept = 0;
};
inline const gobject::state_iptr& gobject::internal_state() const noexcept {
return state_;
}
inline gobject::gobject(state_iptr state)
: state_(std::move(state)) {}
inline bool gobject::valid() const noexcept {
return state_ && state_->valid();
}
inline gobject::operator bool() const noexcept {
return valid();
}
template < typename T >
gcomponent<T> gobject::component() noexcept {
return gcomponent<T>(*this);
@@ -145,16 +128,6 @@ namespace e2d
return const_gcomponent<T>(*this);
}
inline ecs::entity gobject::raw_entity() noexcept {
E2D_ASSERT(valid());
return state_->raw_entity();
}
inline ecs::const_entity gobject::raw_entity() const noexcept {
E2D_ASSERT(valid());
return state_->raw_entity();
}
template < typename T >
ecs::component<T> gobject::raw_component() noexcept {
E2D_ASSERT(valid());

View File

@@ -42,6 +42,8 @@ namespace e2d
static node_iptr create(gobject owner, const node_iptr& parent, const t3f& transform);
void owner(gobject owner) noexcept;
gobject& owner() noexcept;
const gobject& owner() const noexcept;
void transform(const t3f& transform) noexcept;

View File

@@ -51,12 +51,17 @@ namespace e2d
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(const gobject& inst) noexcept;
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

@@ -0,0 +1,41 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#include <enduro2d/high/gobject.hpp>
namespace e2d
{
const gobject::state_iptr& gobject::internal_state() const noexcept {
return state_;
}
gobject::gobject(state_iptr state)
: state_(std::move(state)) {}
bool gobject::valid() const noexcept {
return state_ && !state_->invalided();
}
gobject::operator bool() const noexcept {
return valid();
}
void gobject::destroy() noexcept {
if ( valid() ) {
state_->destroy();
}
}
ecs::entity gobject::raw_entity() noexcept {
E2D_ASSERT(valid());
return state_->raw_entity();
}
ecs::const_entity gobject::raw_entity() const noexcept {
E2D_ASSERT(valid());
return state_->raw_entity();
}
}

View File

@@ -69,6 +69,10 @@ namespace e2d
owner_ = std::move(owner);
}
gobject& node::owner() noexcept {
return owner_;
}
const gobject& node::owner() const noexcept {
return owner_;
}

View File

@@ -65,6 +65,7 @@ namespace
the<world>().registry().process_systems_in_range(
world::priority_update_section_begin,
world::priority_update_section_end);
the<world>().finalize_instances();
return !the<window>().should_close()
|| (application_ && !application_->on_should_close());
}

View File

@@ -13,49 +13,58 @@ namespace
using namespace e2d;
class gobject_state final : public gobject::state {
private:
enum flag_masks : u32 {
fm_invalided = 1u << 0,
fm_destroying = 1u << 1,
};
public:
gobject_state(ecs::registry& registry)
: entity_(registry.create_entity()) {}
gobject_state(world& w, ecs::entity e)
: world_(w)
, entity_(std::move(e)) {}
gobject_state(ecs::registry& registry, const ecs::prototype& proto)
: entity_(registry.create_entity(proto)) {}
~gobject_state() final {
destroy();
void mark_invalided() noexcept {
math::set_flags_inplace(flags_, fm_invalided);
}
void destroy() noexcept {
if ( valid() ) {
entity_.destroy();
valid_ = false;
}
void mark_destroying() noexcept {
math::set_flags_inplace(flags_, fm_destroying);
}
bool valid() const noexcept final {
return valid_;
bool destroying() const noexcept {
return math::check_any_flags(flags_, fm_destroying);
}
public:
void destroy() noexcept final {
gobject go{this};
world_.destroy_instance(go);
}
bool invalided() const noexcept final {
return math::check_any_flags(flags_, fm_invalided);
}
ecs::entity raw_entity() noexcept final {
E2D_ASSERT(valid());
E2D_ASSERT(!invalided());
return entity_;
}
ecs::const_entity raw_entity() const noexcept final {
E2D_ASSERT(valid());
E2D_ASSERT(!invalided());
return entity_;
}
private:
bool valid_{true};
world& world_;
ecs::entity entity_;
u32 flags_{0u};
};
}
namespace e2d
{
world::~world() noexcept {
while ( !gobjects_.empty() ) {
destroy_instance(gobjects_.begin()->second);
}
destroy_instances_();
finalize_instances();
}
ecs::registry& world::registry() noexcept {
@@ -67,7 +76,9 @@ namespace e2d
}
gobject world::instantiate() {
gobject inst{make_intrusive<gobject_state>(registry_)};
gobject inst{make_intrusive<gobject_state>(
*this,
registry_.create_entity())};
gobjects_.emplace(inst.raw_entity().id(), inst);
try {
@@ -78,7 +89,7 @@ namespace e2d
}
inst_a.assign(n);
} catch (...) {
destroy_instance(inst);
finalize_instance_(inst);
throw;
}
@@ -86,7 +97,9 @@ namespace e2d
}
gobject world::instantiate(const prefab& prefab) {
gobject inst{make_intrusive<gobject_state>(registry_, prefab.prototype())};
gobject inst{make_intrusive<gobject_state>(
*this,
registry_.create_entity(prefab.prototype()))};
gobjects_.emplace(inst.raw_entity().id(), inst);
try {
@@ -97,7 +110,7 @@ namespace e2d
}
inst_a.assign(n);
} catch (...) {
destroy_instance(inst);
finalize_instance_(inst);
throw;
}
@@ -109,12 +122,12 @@ namespace e2d
gcomponent<actor> child_a{child};
inst_a->node()->add_child(child_a->node());
} catch (...) {
destroy_instance(child);
finalize_instance_(child);
throw;
}
}
} catch (...) {
destroy_instance(inst);
finalize_instance_(inst);
throw;
}
@@ -124,14 +137,19 @@ namespace e2d
gobject world::instantiate(const gobject& parent) {
gobject go = instantiate();
if ( go && parent ) {
gcomponent<actor> parent_a{parent};
if ( !parent_a ) {
parent_a.assign(node::create(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;
}
if ( !parent_a->node() ) {
parent_a->node(node::create(parent));
}
parent_a->node()->add_child(gcomponent<actor>{go}->node());
}
return go;
}
@@ -147,14 +165,19 @@ namespace e2d
gobject world::instantiate(const prefab& prefab, const gobject& parent) {
gobject go = instantiate(prefab);
if ( go && parent ) {
gcomponent<actor> parent_a{parent};
if ( !parent_a ) {
parent_a.assign(node::create(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;
}
if ( !parent_a->node() ) {
parent_a->node(node::create(parent));
}
parent_a->node()->add_child(gcomponent<actor>{go}->node());
}
return go;
}
@@ -199,19 +222,21 @@ namespace e2d
return go;
}
void world::destroy_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([this](const const_node_iptr& child_n){
destroy_instance(child_n->owner());
});
void world::destroy_instance(gobject& inst) noexcept {
auto gstate = inst
? dynamic_pointer_cast<gobject_state>(inst.internal_state())
: nullptr;
if ( gstate && !gstate->destroying() ) {
gstate->mark_destroying();
destroying_states_.push_back(*gstate);
}
}
if ( inst ) {
gobjects_.erase(inst.raw_entity().id());
dynamic_pointer_cast<gobject_state>(inst.internal_state())->destroy();
void world::finalize_instances() noexcept {
while ( !destroying_states_.empty() ) {
gobject inst{&destroying_states_.front()};
destroying_states_.pop_front();
finalize_instance_(inst);
}
}
@@ -230,4 +255,27 @@ namespace e2d
? 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();
}
}
}