Skip to content

Commit

Permalink
Fix runtime release crash and add functionality to scene.hpp (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mangonels authored Jun 2, 2024
1 parent a027cca commit 660f0a5
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 73 deletions.
4 changes: 4 additions & 0 deletions src/entity/level.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ namespace rl
godot::Node* box{ this->find_child(name::level::physics_box) };
m_physics_box = gdcast<godot::RigidBody2D>(box);

resource::preload::packed_scene<Player> player_scene{ path::scene::Player };
m_player = player_scene.instantiate();
m_player->set_controller(memnew(PlayerController));

this->add_child(m_player);
this->add_child(m_projectile_spawner);

Expand Down
3 changes: 1 addition & 2 deletions src/entity/level.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ namespace rl
std::atomic<bool> m_active{ false };
godot::Node* m_background{ nullptr };
ProjectileSpawner* m_projectile_spawner{ memnew(rl::ProjectileSpawner) };
resource::preload::scene<Player> player_scene{ path::scene::Player };
Player* m_player{ player_scene.instantiate(memnew(PlayerController)) };
Player* m_player{ nullptr };
godot::RigidBody2D* m_physics_box{ nullptr };
};
}
2 changes: 1 addition & 1 deletion src/entity/projectile/projectile_spawner.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ namespace rl
// the time point that keeps track of when the last projectile was spawned.
clock_t::time_point m_prev_spawn_time{ clock_t::now() };
// preloaded packed scene that will be instantiated per spawn
resource::preload::scene<Projectile> m_scene{ path::scene::Bullet };
resource::preload::packed_scene<Projectile> m_scene{ path::scene::Bullet };
};
}
4 changes: 2 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ namespace rl
{
Main::Main()
{
resource::preload::scene<Level> level{ path::scene::Level1 };
resource::preload::scene<MainDialog> dialog{ path::ui::MainDialog };
resource::preload::packed_scene<Level> level{ path::scene::Level1 };
resource::preload::packed_scene<MainDialog> dialog{ path::ui::MainDialog };

m_active_level = level.instantiate();
runtime_assert(m_active_level != nullptr);
Expand Down
41 changes: 25 additions & 16 deletions src/util/conversions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,30 @@

namespace rl::inline utils
{
/** Converts std string types to godot::String at compile time */
template <typename TStr>
struct gd_str_conv
{
explicit constexpr gd_str_conv(TStr&& s)
: m_str{ std::forward<TStr>(s) }
{
}

explicit operator godot::String()
requires std::same_as<TStr, std::string>
{
return godot::String(m_str.c_str());
}

explicit operator godot::String()
requires std::same_as<TStr, std::string_view>
{
return godot::String(m_str.data());
}

TStr m_str{};
};

template <typename TOut, typename TIn>
requires std::derived_from<std::remove_cvref_t<TIn>, godot::Object>
constexpr inline TOut* gdcast(TIn* obj)
Expand All @@ -37,26 +61,11 @@ namespace rl::inline utils
return std::string(in.ascii().ptr());
}

template <>
inline auto to(const godot::String& in) -> std::string_view
{
static_assert(std::is_same_v<godot::String, std::remove_cvref_t<decltype(in)>>);
return std::string_view(in.ascii().ptr());
}

template <>
inline auto to(const godot::StringName& in) -> std::string
{
static_assert(std::is_same_v<godot::StringName, std::remove_cvref_t<decltype(in)>>);
godot::String tmp(in);
return std::string(tmp.ascii().ptr());
}

template <>
inline auto to(const godot::StringName& in) -> std::string_view
{
static_assert(std::is_same_v<godot::StringName, std::remove_cvref_t<decltype(in)>>);
godot::String tmp(in);
return std::string_view(tmp.ascii().ptr());
return std::string(tmp.utf8().ptr());
}
}
152 changes: 100 additions & 52 deletions src/util/scene.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,91 @@

#include <concepts>

#include <godot_cpp/classes/dir_access.hpp>
#include <godot_cpp/classes/file_access.hpp>
#include <godot_cpp/classes/packed_scene.hpp>
#include <godot_cpp/classes/resource.hpp>
#include <godot_cpp/classes/resource_loader.hpp>
#include <godot_cpp/classes/resource_saver.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
#include <godot_cpp/classes/window.hpp>

#include "core/assert.hpp"
#include "util/conversions.hpp"

namespace rl
{
class Character;
}

namespace rl::inline utils
{
namespace scene::node
namespace scene
{
template <typename TNode>
requires std::derived_from<TNode, godot::Node>
static inline void set_unique_name(TNode* node, const char* name)
namespace node
{
runtime_assert(node != nullptr);
node->set_name(name);
node->set_unique_name_in_owner(true);
}
}
template <typename TNode>
requires std::derived_from<TNode, godot::Node>
static inline void set_unique_name(TNode* node, const char* name)
{
runtime_assert(node != nullptr);
node->set_name(name);
node->set_unique_name_in_owner(true);
}

namespace scene::tree
{
template <typename TNode>
requires std::derived_from<TNode, godot::Node>
static inline godot::SceneTree* get(TNode* node)
{
godot::SceneTree* scene_tree{ node->get_tree() };
return scene_tree;
/** Sets the owner of a node and all it's children. */
template <typename TNodeA, typename TNodeB>
requires std::derived_from<TNodeB, godot::Node> &&
std::derived_from<TNodeA, godot::Node>
static inline void set_owner(TNodeA* node, TNodeB* owner)
{
runtime_assert(node != nullptr && owner != nullptr);
const int node_child_count = node->get_child_count();
for (int i = 0; i < node_child_count; ++i)
{
auto child = node->get_child(i);
child->set_owner(owner);
set_owner(child, owner);
}
}
}

template <typename TNode>
requires std::derived_from<TNode, godot::Node>
static inline godot::Node* edited_root(TNode* node)
namespace tree
{
godot::Node* edited_root{ node->get_tree()->get_edited_scene_root() };
return edited_root;
template <typename TNode>
requires std::derived_from<TNode, godot::Node>
static inline godot::SceneTree* get(TNode* node)
{
godot::SceneTree* scene_tree{ node->get_tree() };
return scene_tree;
}

template <typename TNode>
requires std::derived_from<TNode, godot::Node>
static inline godot::Node* edited_root(TNode* node)
{
godot::Node* edited_root{ node->get_tree()->get_edited_scene_root() };
return edited_root;
}

template <typename TNode>
requires std::derived_from<TNode, godot::Node>
static inline godot::Node* root_node(TNode* node)
{
godot::SceneTree* scene_tree{ tree::get(node) };
godot::Window* root_window{ scene_tree->get_root() };
godot::Node* root_node{ gdcast<godot::Node>(root_window) };
return root_node;
}
}

template <typename TNode>
requires std::derived_from<TNode, godot::Node>
static inline godot::Node* root_node(TNode* node)
namespace packer
{
godot::SceneTree* scene_tree{ tree::get(node) };
godot::Window* root_window{ scene_tree->get_root() };
godot::Node* root_node{ gdcast<godot::Node>(root_window) };
return root_node;
/**
@return PackedScene from godot::Node parameter.
*/
template <typename TNode>
requires std::derived_from<TNode, godot::Node>
static inline godot::PackedScene* pack(TNode* node)
{
node::set_owner<TNode, TNode>(node, node);
godot::PackedScene* package = memnew(godot::PackedScene);
package->pack(node);
return package;
}
}
}

Expand All @@ -71,49 +100,68 @@ namespace rl::inline utils
}
}

namespace saver
{
static inline godot::ResourceSaver* get()
{
return godot::ResourceSaver::get_singleton();
}
}

namespace preload
{
template <typename TObj, typename TScene = godot::PackedScene>
requires std::derived_from<TScene, godot::Resource> &&
std::convertible_to<TObj, godot::Object>
class scene
class packed_scene
{
public:
using scene_t = TScene;
using object_t = TObj;

public:
scene(const char* resource_path)
/* Pack as preload from path. */
packed_scene(godot::String resource_load_path)
{
auto fh{ godot::FileAccess::open(resource_path, godot::FileAccess::READ) };
bool file_access_success{ !fh.is_null() };
godot::ResourceLoader* resource_loader{ resource::loader::get() };

bool file_access_success{ resource_loader->exists(resource_load_path) };
runtime_assert(file_access_success);

if (file_access_success)
{
godot::ResourceLoader* resource_loader{ resource::loader::get() };
m_packed_resource = resource_loader->load(resource_path);
m_packed_resource = resource_loader->load(resource_load_path);
initialized = m_packed_resource.is_valid();
}
}

template <typename... TArgs>
[[nodiscard]] auto instantiate(TArgs... args) -> TObj*
/* Pack from existing instance. */
packed_scene(godot::Node* node)
{
assertion(initialized, "instantiation invoked from uninitialized scene loader");
m_packed_resource = scene::packer::pack(node);
initialized = m_packed_resource.is_valid();
}

[[nodiscard]] auto instantiate() -> object_t*
{
assertion(initialized,
"Resource instantiation invoked from uninitialized scene loader.");
if (!initialized) [[unlikely]]
return nullptr;

TObj* obj{ gdcast<TObj>(m_packed_resource->instantiate()) };
object_t* obj{ gdcast<object_t>(m_packed_resource->instantiate()) };
runtime_assert(obj != nullptr);

if constexpr (std::is_base_of_v<Character, object_t>)
return obj;
}

/** Save this resource to specified path. */
void save(godot::String& resource_save_path)
{
if (initialized)
{
if (obj != nullptr)
obj->set_controller(args...);
auto error = saver::get()->save(m_packed_resource, resource_save_path);
assertion(error != godot::Error::OK, "Packed resource save failed.");
}

return obj;
}

private:
Expand Down

0 comments on commit 660f0a5

Please sign in to comment.