mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-14 16:09:06 +07:00
Merge branch 'feature/safe_gobject' into feature/scripting
This commit is contained in:
@@ -74,11 +74,6 @@ namespace e2d
|
|||||||
|
|
||||||
class atlas;
|
class atlas;
|
||||||
class flipbook;
|
class flipbook;
|
||||||
class gobject;
|
|
||||||
template < typename T >
|
|
||||||
class gcomponent;
|
|
||||||
template < typename T >
|
|
||||||
class const_gcomponent;
|
|
||||||
class model;
|
class model;
|
||||||
class node;
|
class node;
|
||||||
class prefab;
|
class prefab;
|
||||||
@@ -89,6 +84,12 @@ namespace e2d
|
|||||||
class luasol;
|
class luasol;
|
||||||
class starter;
|
class starter;
|
||||||
class world;
|
class world;
|
||||||
|
|
||||||
|
class gobject;
|
||||||
|
template < typename T >
|
||||||
|
class gcomponent;
|
||||||
|
template < typename T >
|
||||||
|
class const_gcomponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace sol
|
namespace sol
|
||||||
|
|||||||
@@ -8,17 +8,6 @@
|
|||||||
|
|
||||||
#include "_high.hpp"
|
#include "_high.hpp"
|
||||||
|
|
||||||
namespace e2d
|
|
||||||
{
|
|
||||||
class gobject;
|
|
||||||
|
|
||||||
template < typename T >
|
|
||||||
class gcomponent;
|
|
||||||
|
|
||||||
template < typename T >
|
|
||||||
class const_gcomponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace e2d
|
namespace e2d
|
||||||
{
|
{
|
||||||
class gobject final {
|
class gobject final {
|
||||||
@@ -33,6 +22,8 @@ namespace e2d
|
|||||||
bool valid() const noexcept;
|
bool valid() const noexcept;
|
||||||
explicit operator bool() const noexcept;
|
explicit operator bool() const noexcept;
|
||||||
|
|
||||||
|
void destroy() noexcept;
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
gcomponent<T> component() noexcept;
|
gcomponent<T> component() noexcept;
|
||||||
|
|
||||||
@@ -110,31 +101,23 @@ namespace e2d
|
|||||||
|
|
||||||
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
|
class gobject::state
|
||||||
: private noncopyable
|
: private noncopyable
|
||||||
, public ref_counter<state> {
|
, public ref_counter<state>
|
||||||
|
, public intrusive_list_hook<gobject_destroying_state_ilist_tag> {
|
||||||
public:
|
public:
|
||||||
virtual ~state() noexcept {}
|
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::entity raw_entity() noexcept = 0;
|
||||||
virtual ecs::const_entity raw_entity() const 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 >
|
template < typename T >
|
||||||
gcomponent<T> gobject::component() noexcept {
|
gcomponent<T> gobject::component() noexcept {
|
||||||
return gcomponent<T>(*this);
|
return gcomponent<T>(*this);
|
||||||
@@ -145,16 +128,6 @@ namespace e2d
|
|||||||
return const_gcomponent<T>(*this);
|
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 >
|
template < typename T >
|
||||||
ecs::component<T> gobject::raw_component() noexcept {
|
ecs::component<T> gobject::raw_component() noexcept {
|
||||||
E2D_ASSERT(valid());
|
E2D_ASSERT(valid());
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ namespace e2d
|
|||||||
static node_iptr create(gobject owner, const node_iptr& parent, const t3f& transform);
|
static node_iptr create(gobject owner, const node_iptr& parent, const t3f& transform);
|
||||||
|
|
||||||
void owner(gobject owner) noexcept;
|
void owner(gobject owner) noexcept;
|
||||||
|
|
||||||
|
gobject& owner() noexcept;
|
||||||
const gobject& owner() const noexcept;
|
const gobject& owner() const noexcept;
|
||||||
|
|
||||||
void transform(const t3f& transform) noexcept;
|
void transform(const t3f& transform) noexcept;
|
||||||
|
|||||||
@@ -51,12 +51,17 @@ namespace e2d
|
|||||||
gobject instantiate(const prefab& prefab, const gobject& 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);
|
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(ecs::entity_id ent) const noexcept;
|
||||||
gobject resolve(const ecs::const_entity& ent) const noexcept;
|
gobject resolve(const ecs::const_entity& ent) const noexcept;
|
||||||
|
private:
|
||||||
|
void destroy_instances_() noexcept;
|
||||||
|
void finalize_instance_(gobject& inst) noexcept;
|
||||||
private:
|
private:
|
||||||
ecs::registry registry_;
|
ecs::registry registry_;
|
||||||
hash_map<ecs::entity_id, gobject> gobjects_;
|
hash_map<ecs::entity_id, gobject> gobjects_;
|
||||||
|
gobject_destroying_states destroying_states_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
41
sources/enduro2d/high/gobject.cpp
Normal file
41
sources/enduro2d/high/gobject.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,6 +69,10 @@ namespace e2d
|
|||||||
owner_ = std::move(owner);
|
owner_ = std::move(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gobject& node::owner() noexcept {
|
||||||
|
return owner_;
|
||||||
|
}
|
||||||
|
|
||||||
const gobject& node::owner() const noexcept {
|
const gobject& node::owner() const noexcept {
|
||||||
return owner_;
|
return owner_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ namespace
|
|||||||
the<world>().registry().process_systems_in_range(
|
the<world>().registry().process_systems_in_range(
|
||||||
world::priority_update_section_begin,
|
world::priority_update_section_begin,
|
||||||
world::priority_update_section_end);
|
world::priority_update_section_end);
|
||||||
|
the<world>().finalize_instances();
|
||||||
return !the<window>().should_close()
|
return !the<window>().should_close()
|
||||||
|| (application_ && !application_->on_should_close());
|
|| (application_ && !application_->on_should_close());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,49 +13,58 @@ namespace
|
|||||||
using namespace e2d;
|
using namespace e2d;
|
||||||
|
|
||||||
class gobject_state final : public gobject::state {
|
class gobject_state final : public gobject::state {
|
||||||
|
private:
|
||||||
|
enum flag_masks : u32 {
|
||||||
|
fm_invalided = 1u << 0,
|
||||||
|
fm_destroying = 1u << 1,
|
||||||
|
};
|
||||||
public:
|
public:
|
||||||
gobject_state(ecs::registry& registry)
|
gobject_state(world& w, ecs::entity e)
|
||||||
: entity_(registry.create_entity()) {}
|
: world_(w)
|
||||||
|
, entity_(std::move(e)) {}
|
||||||
|
|
||||||
gobject_state(ecs::registry& registry, const ecs::prototype& proto)
|
void mark_invalided() noexcept {
|
||||||
: entity_(registry.create_entity(proto)) {}
|
math::set_flags_inplace(flags_, fm_invalided);
|
||||||
|
|
||||||
~gobject_state() final {
|
|
||||||
destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy() noexcept {
|
void mark_destroying() noexcept {
|
||||||
if ( valid() ) {
|
math::set_flags_inplace(flags_, fm_destroying);
|
||||||
entity_.destroy();
|
|
||||||
valid_ = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool valid() const noexcept final {
|
bool destroying() const noexcept {
|
||||||
return valid_;
|
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 {
|
ecs::entity raw_entity() noexcept final {
|
||||||
E2D_ASSERT(valid());
|
E2D_ASSERT(!invalided());
|
||||||
return entity_;
|
return entity_;
|
||||||
}
|
}
|
||||||
|
|
||||||
ecs::const_entity raw_entity() const noexcept final {
|
ecs::const_entity raw_entity() const noexcept final {
|
||||||
E2D_ASSERT(valid());
|
E2D_ASSERT(!invalided());
|
||||||
return entity_;
|
return entity_;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
bool valid_{true};
|
world& world_;
|
||||||
ecs::entity entity_;
|
ecs::entity entity_;
|
||||||
|
u32 flags_{0u};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace e2d
|
namespace e2d
|
||||||
{
|
{
|
||||||
world::~world() noexcept {
|
world::~world() noexcept {
|
||||||
while ( !gobjects_.empty() ) {
|
destroy_instances_();
|
||||||
destroy_instance(gobjects_.begin()->second);
|
finalize_instances();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ecs::registry& world::registry() noexcept {
|
ecs::registry& world::registry() noexcept {
|
||||||
@@ -67,7 +76,9 @@ namespace e2d
|
|||||||
}
|
}
|
||||||
|
|
||||||
gobject world::instantiate() {
|
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);
|
gobjects_.emplace(inst.raw_entity().id(), inst);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -78,7 +89,7 @@ namespace e2d
|
|||||||
}
|
}
|
||||||
inst_a.assign(n);
|
inst_a.assign(n);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
destroy_instance(inst);
|
finalize_instance_(inst);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +97,9 @@ namespace e2d
|
|||||||
}
|
}
|
||||||
|
|
||||||
gobject world::instantiate(const prefab& prefab) {
|
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);
|
gobjects_.emplace(inst.raw_entity().id(), inst);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -97,7 +110,7 @@ namespace e2d
|
|||||||
}
|
}
|
||||||
inst_a.assign(n);
|
inst_a.assign(n);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
destroy_instance(inst);
|
finalize_instance_(inst);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,12 +122,12 @@ namespace e2d
|
|||||||
gcomponent<actor> child_a{child};
|
gcomponent<actor> child_a{child};
|
||||||
inst_a->node()->add_child(child_a->node());
|
inst_a->node()->add_child(child_a->node());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
destroy_instance(child);
|
finalize_instance_(child);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
destroy_instance(inst);
|
finalize_instance_(inst);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,14 +137,19 @@ namespace e2d
|
|||||||
gobject world::instantiate(const gobject& parent) {
|
gobject world::instantiate(const gobject& parent) {
|
||||||
gobject go = instantiate();
|
gobject go = instantiate();
|
||||||
if ( go && parent ) {
|
if ( go && parent ) {
|
||||||
gcomponent<actor> parent_a{parent};
|
try {
|
||||||
if ( !parent_a ) {
|
gcomponent<actor> parent_a{parent};
|
||||||
parent_a.assign(node::create(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;
|
return go;
|
||||||
}
|
}
|
||||||
@@ -147,14 +165,19 @@ namespace e2d
|
|||||||
gobject world::instantiate(const prefab& prefab, const gobject& parent) {
|
gobject world::instantiate(const prefab& prefab, const gobject& parent) {
|
||||||
gobject go = instantiate(prefab);
|
gobject go = instantiate(prefab);
|
||||||
if ( go && parent ) {
|
if ( go && parent ) {
|
||||||
gcomponent<actor> parent_a{parent};
|
try {
|
||||||
if ( !parent_a ) {
|
gcomponent<actor> parent_a{parent};
|
||||||
parent_a.assign(node::create(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;
|
return go;
|
||||||
}
|
}
|
||||||
@@ -199,19 +222,21 @@ namespace e2d
|
|||||||
return go;
|
return go;
|
||||||
}
|
}
|
||||||
|
|
||||||
void world::destroy_instance(const gobject& inst) noexcept {
|
void world::destroy_instance(gobject& inst) noexcept {
|
||||||
gcomponent<actor> inst_a{inst};
|
auto gstate = inst
|
||||||
auto inst_n = inst_a ? inst_a->node() : nullptr;
|
? dynamic_pointer_cast<gobject_state>(inst.internal_state())
|
||||||
|
: nullptr;
|
||||||
if ( inst_n ) {
|
if ( gstate && !gstate->destroying() ) {
|
||||||
inst_n->for_each_child([this](const const_node_iptr& child_n){
|
gstate->mark_destroying();
|
||||||
destroy_instance(child_n->owner());
|
destroying_states_.push_back(*gstate);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( inst ) {
|
void world::finalize_instances() noexcept {
|
||||||
gobjects_.erase(inst.raw_entity().id());
|
while ( !destroying_states_.empty() ) {
|
||||||
dynamic_pointer_cast<gobject_state>(inst.internal_state())->destroy();
|
gobject inst{&destroying_states_.front()};
|
||||||
|
destroying_states_.pop_front();
|
||||||
|
finalize_instance_(inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,4 +255,27 @@ namespace e2d
|
|||||||
? iter->second
|
? iter->second
|
||||||
: gobject();
|
: 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user