From e2f8df8c5b3b6a1d4c18a75ff1c8293cc5909bc0 Mon Sep 17 00:00:00 2001 From: reduz Date: Tue, 24 Aug 2021 19:03:10 -0300 Subject: [PATCH] Add ability to register singletons from engine API * Exposed functions in Engine to register and unregister singletons. * Added the concept of user singletons, which can be removed (the system ones can't). --- core/config/engine.cpp | 30 +++++++++++++++++++++++++++--- core/config/engine.h | 7 +++++-- core/core_bind.cpp | 35 +++++++++++++++++++++++++++++++++-- core/core_bind.h | 7 +++++-- doc/classes/Engine.xml | 22 ++++++++++++++++++++-- 5 files changed, 90 insertions(+), 11 deletions(-) diff --git a/core/config/engine.cpp b/core/config/engine.cpp index 495670bc88..d8fbb50a75 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -199,17 +199,41 @@ bool Engine::is_printing_error_messages() const { } void Engine::add_singleton(const Singleton &p_singleton) { + ERR_FAIL_COND_MSG(singleton_ptrs.has(p_singleton.name), "Can't register singleton that already exists: " + String(p_singleton.name)); singletons.push_back(p_singleton); singleton_ptrs[p_singleton.name] = p_singleton.ptr; } -Object *Engine::get_singleton_object(const String &p_name) const { +Object *Engine::get_singleton_object(const StringName &p_name) const { const Map::Element *E = singleton_ptrs.find(p_name); - ERR_FAIL_COND_V_MSG(!E, nullptr, "Failed to retrieve non-existent singleton '" + p_name + "'."); + ERR_FAIL_COND_V_MSG(!E, nullptr, "Failed to retrieve non-existent singleton '" + String(p_name) + "'."); return E->get(); } -bool Engine::has_singleton(const String &p_name) const { +bool Engine::is_singleton_user_created(const StringName &p_name) const { + ERR_FAIL_COND_V(!singleton_ptrs.has(p_name), false); + + for (const Singleton &E : singletons) { + if (E.name == p_name && E.user_created) { + return true; + } + } + + return false; +} +void Engine::remove_singleton(const StringName &p_name) { + ERR_FAIL_COND(!singleton_ptrs.has(p_name)); + + for (List::Element *E = singletons.front(); E; E = E->next()) { + if (E->get().name == p_name) { + singletons.erase(E); + singleton_ptrs.erase(p_name); + return; + } + } +} + +bool Engine::has_singleton(const StringName &p_name) const { return singleton_ptrs.has(p_name); } diff --git a/core/config/engine.h b/core/config/engine.h index e6b5df2d5a..ae33acede2 100644 --- a/core/config/engine.h +++ b/core/config/engine.h @@ -42,6 +42,7 @@ public: StringName name; Object *ptr; StringName class_name; //used for binding generation hinting + bool user_created = false; Singleton(const StringName &p_name = StringName(), Object *p_ptr = nullptr, const StringName &p_class_name = StringName()); }; @@ -109,8 +110,10 @@ public: void add_singleton(const Singleton &p_singleton); void get_singletons(List *p_singletons); - bool has_singleton(const String &p_name) const; - Object *get_singleton_object(const String &p_name) const; + bool has_singleton(const StringName &p_name) const; + Object *get_singleton_object(const StringName &p_name) const; + void remove_singleton(const StringName &p_name); + bool is_singleton_user_created(const StringName &p_name) const; #ifdef TOOLS_ENABLED _FORCE_INLINE_ void set_editor_hint(bool p_enabled) { editor_hint = p_enabled; } diff --git a/core/core_bind.cpp b/core/core_bind.cpp index efb4b84716..83a3b80803 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -2165,14 +2165,41 @@ bool Engine::is_in_physics_frame() const { return ::Engine::get_singleton()->is_in_physics_frame(); } -bool Engine::has_singleton(const String &p_name) const { +bool Engine::has_singleton(const StringName &p_name) const { return ::Engine::get_singleton()->has_singleton(p_name); } -Object *Engine::get_singleton_object(const String &p_name) const { +Object *Engine::get_singleton_object(const StringName &p_name) const { return ::Engine::get_singleton()->get_singleton_object(p_name); } +void Engine::register_singleton(const StringName &p_name, Object *p_object) { + ERR_FAIL_COND_MSG(has_singleton(p_name), "Singleton already registered: " + String(p_name)); + ERR_FAIL_COND_MSG(p_name.operator String().is_valid_identifier(), "Singleton name is not a valid identifier: " + String(p_name)); + ::Engine::Singleton s; + s.class_name = p_name; + s.name = p_name; + s.ptr = p_object; + s.user_created = true; + ::Engine::get_singleton()->add_singleton(s); + ; +} +void Engine::unregister_singleton(const StringName &p_name) { + ERR_FAIL_COND_MSG(!has_singleton(p_name), "Attempt to remove unregisteres singleton: " + String(p_name)); + ERR_FAIL_COND_MSG(!::Engine::get_singleton()->is_singleton_user_created(p_name), "Attempt to remove non-user created singleton: " + String(p_name)); + ::Engine::get_singleton()->remove_singleton(p_name); +} + +Vector Engine::get_singleton_list() const { + List<::Engine::Singleton> singletons; + ::Engine::get_singleton()->get_singletons(&singletons); + Vector ret; + for (List<::Engine::Singleton>::Element *E = singletons.front(); E; E = E->next()) { + ret.push_back(E->get().name); + } + return ret; +} + void Engine::set_editor_hint(bool p_enabled) { ::Engine::get_singleton()->set_editor_hint(p_enabled); } @@ -2220,6 +2247,10 @@ void Engine::_bind_methods() { ClassDB::bind_method(D_METHOD("has_singleton", "name"), &Engine::has_singleton); ClassDB::bind_method(D_METHOD("get_singleton", "name"), &Engine::get_singleton_object); + ClassDB::bind_method(D_METHOD("register_singleton", "name", "instance"), &Engine::register_singleton); + ClassDB::bind_method(D_METHOD("unregister_singleton", "name"), &Engine::unregister_singleton); + ClassDB::bind_method(D_METHOD("get_singleton_list"), &Engine::get_singleton_list); + ClassDB::bind_method(D_METHOD("set_editor_hint", "enabled"), &Engine::set_editor_hint); ClassDB::bind_method(D_METHOD("is_editor_hint"), &Engine::is_editor_hint); diff --git a/core/core_bind.h b/core/core_bind.h index 1dbe49f418..a6fac63edd 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -639,8 +639,11 @@ public: bool is_in_physics_frame() const; - bool has_singleton(const String &p_name) const; - Object *get_singleton_object(const String &p_name) const; + bool has_singleton(const StringName &p_name) const; + Object *get_singleton_object(const StringName &p_name) const; + void register_singleton(const StringName &p_name, Object *p_object); + void unregister_singleton(const StringName &p_name); + Vector get_singleton_list() const; void set_editor_hint(bool p_enabled); bool is_editor_hint() const; diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml index 886a18900e..8b399f64c9 100644 --- a/doc/classes/Engine.xml +++ b/doc/classes/Engine.xml @@ -84,11 +84,16 @@ - + Returns a global singleton with given [code]name[/code]. Often used for plugins, e.g. GodotPayments. + + + + + @@ -125,7 +130,7 @@ - + Returns [code]true[/code] if a singleton with given [code]name[/code] exists in global scope. @@ -136,6 +141,19 @@ Returns [code]true[/code] if the game is inside the fixed process and physics phase of the game loop. + + + + + + + + + + + + +