Browse Source

LibGodot: Core - Build Godot Engine as a Library

* Add a new GodotInstance GDCLASS that provides startup and iteration commands to control a Godot instance.
* Adds a libgodot_create_godot_instance entry point that creates a new Godot instance and returns a GodotInstance object.
* Adds a libgodot_destroy_godot_instance entry point that destroys the Godot instance.

Sample Apps: https://github.com/migeran/libgodot_project

Developed by [Migeran](https://migeran.com)

Sponsors & Acknowledgements:

* Initial development sponsored by [Smirk Software](https://www.smirk.gg/)
* Rebasing to Godot 4.3 and further development sponsored by [Xibbon Inc.](https://xibbon.com)
* The GDExtension registration of the host process & build system changes were based
  on @Faolan-Rad's LibGodot PR: https://github.com/godotengine/godot/pull/72883
* Thanks to Ben Rog-Wilhelm (Zorbathut) for creating a smaller, minimal version for easier review.
* Thanks to Ernest Lee (iFire) for his support

Co-Authored-By: Gabor Koncz <[email protected]>
Co-Authored-By: Ben Rog-Wilhelm <[email protected]>
Gergely Kis 2 weeks ago
parent
commit
6c44c80c62

+ 15 - 0
SConstruct

@@ -264,6 +264,14 @@ opts.Add(
         True,
         True,
     )
     )
 )
 )
+opts.Add(
+    EnumVariable(
+        "library_type",
+        "Build library type",
+        "executable",
+        ("executable", "static_library", "shared_library"),
+    )
+)
 
 
 # Thirdparty libraries
 # Thirdparty libraries
 opts.Add(BoolVariable("builtin_brotli", "Use the built-in Brotli library", True))
 opts.Add(BoolVariable("builtin_brotli", "Use the built-in Brotli library", True))
@@ -556,6 +564,13 @@ if not env["deprecated"]:
 if env["precision"] == "double":
 if env["precision"] == "double":
     env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])
     env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])
 
 
+# Library Support
+if env["library_type"] != "executable":
+    if "library" not in env.get("supported", []):
+        print_error(f"Library builds unsupported for {env['platform']}")
+        Exit(255)
+    env.Append(CPPDEFINES=["LIBGODOT_ENABLED"])
+
 # Default num_jobs to local cpu count if not user specified.
 # Default num_jobs to local cpu count if not user specified.
 # SCons has a peculiarity where user-specified options won't be overridden
 # SCons has a peculiarity where user-specified options won't be overridden
 # by SetOption, so we can rely on this to know if we should use our default.
 # by SetOption, so we can rely on this to know if we should use our default.

+ 5 - 0
core/config/project_settings.cpp

@@ -83,6 +83,11 @@ const PackedStringArray ProjectSettings::get_required_features() {
 // Returns the features supported by this build of Godot. Includes all required features.
 // Returns the features supported by this build of Godot. Includes all required features.
 const PackedStringArray ProjectSettings::_get_supported_features() {
 const PackedStringArray ProjectSettings::_get_supported_features() {
 	PackedStringArray features = get_required_features();
 	PackedStringArray features = get_required_features();
+
+#ifdef LIBGODOT_ENABLED
+	features.append("LibGodot");
+#endif
+
 #ifdef MODULE_MONO_ENABLED
 #ifdef MODULE_MONO_ENABLED
 	features.append("C#");
 	features.append("C#");
 #endif
 #endif

+ 74 - 0
core/extension/gdextension_function_loader.cpp

@@ -0,0 +1,74 @@
+/**************************************************************************/
+/*  gdextension_function_loader.cpp                                       */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#include "gdextension_function_loader.h"
+
+#include "gdextension.h"
+
+Error GDExtensionFunctionLoader::open_library(const String &p_path) {
+	ERR_FAIL_COND_V_MSG(!p_path.begins_with("libgodot://"), ERR_FILE_NOT_FOUND, "Function based GDExtensions should have a path starting with libgodot://");
+	ERR_FAIL_COND_V_MSG(!initialization_function, ERR_DOES_NOT_EXIST, "Initialization function is required for function based GDExtensions.");
+
+	library_path = p_path;
+
+	return OK;
+}
+
+Error GDExtensionFunctionLoader::initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) {
+	ERR_FAIL_COND_V_MSG(!initialization_function, ERR_DOES_NOT_EXIST, "Initialization function is required for function based GDExtensions.");
+	GDExtensionBool ret = initialization_function(p_get_proc_address, p_extension.ptr(), r_initialization);
+
+	if (ret) {
+		return OK;
+	} else {
+		ERR_FAIL_V_MSG(FAILED, "GDExtension initialization function for '" + library_path + "' returned an error.");
+	}
+}
+
+void GDExtensionFunctionLoader::close_library() {
+	initialization_function = nullptr;
+	library_path.clear();
+}
+
+bool GDExtensionFunctionLoader::is_library_open() const {
+	return !library_path.is_empty();
+}
+
+bool GDExtensionFunctionLoader::has_library_changed() const {
+	return false;
+}
+
+bool GDExtensionFunctionLoader::library_exists() const {
+	return true;
+}
+
+void GDExtensionFunctionLoader::set_initialization_function(GDExtensionInitializationFunction p_initialization_function) {
+	initialization_function = p_initialization_function;
+}

+ 54 - 0
core/extension/gdextension_function_loader.h

@@ -0,0 +1,54 @@
+/**************************************************************************/
+/*  gdextension_function_loader.h                                         */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#pragma once
+
+#include "core/extension/gdextension_loader.h"
+#include "core/os/shared_object.h"
+
+class GDExtension;
+
+class GDExtensionFunctionLoader : public GDExtensionLoader {
+	friend class GDExtensionManager;
+	friend class GDExtension;
+
+	String library_path;
+	GDExtensionInitializationFunction initialization_function = nullptr;
+
+public:
+	virtual Error open_library(const String &p_path) override;
+	virtual Error initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) override;
+	virtual void close_library() override;
+	virtual bool is_library_open() const override;
+	virtual bool has_library_changed() const override;
+	virtual bool library_exists() const override;
+
+	void set_initialization_function(GDExtensionInitializationFunction p_initialization_function);
+};

+ 10 - 1
core/extension/gdextension_manager.cpp

@@ -30,6 +30,7 @@
 
 
 #include "gdextension_manager.h"
 #include "gdextension_manager.h"
 
 
+#include "core/extension/gdextension_function_loader.h"
 #include "core/extension/gdextension_library_loader.h"
 #include "core/extension/gdextension_library_loader.h"
 #include "core/extension/gdextension_special_compat_hashes.h"
 #include "core/extension/gdextension_special_compat_hashes.h"
 #include "core/io/dir_access.h"
 #include "core/io/dir_access.h"
@@ -114,7 +115,14 @@ GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String &
 
 
 	Ref<GDExtensionLibraryLoader> loader;
 	Ref<GDExtensionLibraryLoader> loader;
 	loader.instantiate();
 	loader.instantiate();
-	return GDExtensionManager::get_singleton()->load_extension_with_loader(p_path, loader);
+	return load_extension_with_loader(p_path, loader);
+}
+
+GDExtensionManager::LoadStatus GDExtensionManager::load_extension_from_function(const String &p_path, GDExtensionConstPtr<const GDExtensionInitializationFunction> p_init_func) {
+	Ref<GDExtensionFunctionLoader> func_loader;
+	func_loader.instantiate();
+	func_loader->set_initialization_function((GDExtensionInitializationFunction)*p_init_func.data);
+	return load_extension_with_loader(p_path, func_loader);
 }
 }
 
 
 GDExtensionManager::LoadStatus GDExtensionManager::load_extension_with_loader(const String &p_path, const Ref<GDExtensionLoader> &p_loader) {
 GDExtensionManager::LoadStatus GDExtensionManager::load_extension_with_loader(const String &p_path, const Ref<GDExtensionLoader> &p_loader) {
@@ -454,6 +462,7 @@ GDExtensionManager *GDExtensionManager::get_singleton() {
 
 
 void GDExtensionManager::_bind_methods() {
 void GDExtensionManager::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("load_extension", "path"), &GDExtensionManager::load_extension);
 	ClassDB::bind_method(D_METHOD("load_extension", "path"), &GDExtensionManager::load_extension);
+	ClassDB::bind_method(D_METHOD("load_extension_from_function", "path", "init_func"), &GDExtensionManager::load_extension_from_function);
 	ClassDB::bind_method(D_METHOD("reload_extension", "path"), &GDExtensionManager::reload_extension);
 	ClassDB::bind_method(D_METHOD("reload_extension", "path"), &GDExtensionManager::reload_extension);
 	ClassDB::bind_method(D_METHOD("unload_extension", "path"), &GDExtensionManager::unload_extension);
 	ClassDB::bind_method(D_METHOD("unload_extension", "path"), &GDExtensionManager::unload_extension);
 	ClassDB::bind_method(D_METHOD("is_extension_loaded", "path"), &GDExtensionManager::is_extension_loaded);
 	ClassDB::bind_method(D_METHOD("is_extension_loaded", "path"), &GDExtensionManager::is_extension_loaded);

+ 4 - 0
core/extension/gdextension_manager.h

@@ -31,6 +31,9 @@
 #pragma once
 #pragma once
 
 
 #include "core/extension/gdextension.h"
 #include "core/extension/gdextension.h"
+#include "core/variant/native_ptr.h"
+
+GDVIRTUAL_NATIVE_PTR(GDExtensionInitializationFunction)
 
 
 class GDExtensionManager : public Object {
 class GDExtensionManager : public Object {
 	GDCLASS(GDExtensionManager, Object);
 	GDCLASS(GDExtensionManager, Object);
@@ -66,6 +69,7 @@ private:
 
 
 public:
 public:
 	LoadStatus load_extension(const String &p_path);
 	LoadStatus load_extension(const String &p_path);
+	LoadStatus load_extension_from_function(const String &p_path, GDExtensionConstPtr<const GDExtensionInitializationFunction> p_init_func);
 	LoadStatus load_extension_with_loader(const String &p_path, const Ref<GDExtensionLoader> &p_loader);
 	LoadStatus load_extension_with_loader(const String &p_path, const Ref<GDExtensionLoader> &p_loader);
 	LoadStatus reload_extension(const String &p_path);
 	LoadStatus reload_extension(const String &p_path);
 	LoadStatus unload_extension(const String &p_path);
 	LoadStatus unload_extension(const String &p_path);

+ 126 - 0
core/extension/godot_instance.cpp

@@ -0,0 +1,126 @@
+/**************************************************************************/
+/*  godot_instance.cpp                                                    */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#include "godot_instance.h"
+
+#include "core/extension/gdextension_manager.h"
+#include "core/os/main_loop.h"
+#include "main/main.h"
+#include "servers/display/display_server.h"
+
+void GodotInstance::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("start"), &GodotInstance::start);
+	ClassDB::bind_method(D_METHOD("is_started"), &GodotInstance::is_started);
+	ClassDB::bind_method(D_METHOD("iteration"), &GodotInstance::iteration);
+	ClassDB::bind_method(D_METHOD("focus_in"), &GodotInstance::focus_in);
+	ClassDB::bind_method(D_METHOD("focus_out"), &GodotInstance::focus_out);
+	ClassDB::bind_method(D_METHOD("pause"), &GodotInstance::pause);
+	ClassDB::bind_method(D_METHOD("resume"), &GodotInstance::resume);
+}
+
+GodotInstance::GodotInstance() {
+}
+
+GodotInstance::~GodotInstance() {
+}
+
+bool GodotInstance::initialize(GDExtensionInitializationFunction p_init_func) {
+	print_verbose("Godot Instance initialization");
+	GDExtensionManager *gdextension_manager = GDExtensionManager::get_singleton();
+	GDExtensionConstPtr<const GDExtensionInitializationFunction> ptr((const GDExtensionInitializationFunction *)&p_init_func);
+	GDExtensionManager::LoadStatus status = gdextension_manager->load_extension_from_function("libgodot://main", ptr);
+	return status == GDExtensionManager::LoadStatus::LOAD_STATUS_OK;
+}
+
+bool GodotInstance::start() {
+	print_verbose("GodotInstance::start()");
+	Error err = Main::setup2();
+	if (err != OK) {
+		return false;
+	}
+	started = Main::start() == EXIT_SUCCESS;
+	if (started) {
+		OS::get_singleton()->get_main_loop()->initialize();
+	}
+	return started;
+}
+
+bool GodotInstance::is_started() {
+	return started;
+}
+
+bool GodotInstance::iteration() {
+	DisplayServer::get_singleton()->process_events();
+	return Main::iteration();
+}
+
+void GodotInstance::stop() {
+	print_verbose("GodotInstance::stop()");
+	if (started) {
+		OS::get_singleton()->get_main_loop()->finalize();
+	}
+	started = false;
+}
+
+void GodotInstance::focus_out() {
+	print_verbose("GodotInstance::focus_out()");
+	if (started) {
+		if (OS::get_singleton()->get_main_loop()) {
+			OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
+		}
+	}
+}
+
+void GodotInstance::focus_in() {
+	print_verbose("GodotInstance::focus_in()");
+	if (started) {
+		if (OS::get_singleton()->get_main_loop()) {
+			OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
+		}
+	}
+}
+
+void GodotInstance::pause() {
+	print_verbose("GodotInstance::pause()");
+	if (started) {
+		if (OS::get_singleton()->get_main_loop()) {
+			OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_PAUSED);
+		}
+	}
+}
+
+void GodotInstance::resume() {
+	print_verbose("GodotInstance::resume()");
+	if (started) {
+		if (OS::get_singleton()->get_main_loop()) {
+			OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_RESUMED);
+		}
+	}
+}

+ 59 - 0
core/extension/godot_instance.h

@@ -0,0 +1,59 @@
+/**************************************************************************/
+/*  godot_instance.h                                                      */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#pragma once
+
+#include "core/extension/gdextension_interface.h"
+#include "core/object/class_db.h"
+
+class GodotInstance : public Object {
+	GDCLASS(GodotInstance, Object);
+
+	bool started = false;
+
+protected:
+	static void _bind_methods();
+
+public:
+	GodotInstance();
+	~GodotInstance();
+
+	bool initialize(GDExtensionInitializationFunction p_init_func);
+
+	bool start();
+	bool is_started();
+	bool iteration();
+	void stop();
+
+	void focus_out();
+	void focus_in();
+	void pause();
+	void resume();
+};

+ 73 - 0
core/extension/libgodot.h

@@ -0,0 +1,73 @@
+/**************************************************************************/
+/*  libgodot.h                                                            */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#pragma once
+
+#include "gdextension_interface.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Export macros for DLL visibility
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#define LIBGODOT_API __declspec(dllexport)
+#elif defined(__GNUC__) || defined(__clang__)
+#define LIBGODOT_API __attribute__((visibility("default")))
+#endif // if defined(_MSC_VER)
+
+/**
+ * @name libgodot_create_godot_instance
+ * @since 4.6
+ *
+ * Creates a new Godot instance.
+ *
+ * @param p_argc The number of command line arguments.
+ * @param p_argv The C-style array of command line arguments.
+ * @param p_init_func GDExtension initialization function of the host application.
+ *
+ * @return A pointer to created \ref GodotInstance GDExtension object or nullptr if there was an error.
+ */
+LIBGODOT_API GDExtensionObjectPtr libgodot_create_godot_instance(int p_argc, char *p_argv[], GDExtensionInitializationFunction p_init_func);
+
+/**
+ * @name libgodot_destroy_godot_instance
+ * @since 4.6
+ *
+ * Destroys an existing Godot instance.
+ *
+ * @param p_godot_instance The reference to the GodotInstance object to destroy.
+ *
+ */
+LIBGODOT_API void libgodot_destroy_godot_instance(GDExtensionObjectPtr p_godot_instance);
+
+#ifdef __cplusplus
+}
+#endif

+ 3 - 0
core/register_core_types.cpp

@@ -39,6 +39,7 @@
 #include "core/debugger/engine_profiler.h"
 #include "core/debugger/engine_profiler.h"
 #include "core/extension/gdextension.h"
 #include "core/extension/gdextension.h"
 #include "core/extension/gdextension_manager.h"
 #include "core/extension/gdextension_manager.h"
+#include "core/extension/godot_instance.h"
 #include "core/input/input.h"
 #include "core/input/input.h"
 #include "core/input/input_map.h"
 #include "core/input/input_map.h"
 #include "core/input/shortcut.h"
 #include "core/input/shortcut.h"
@@ -289,6 +290,8 @@ void register_core_types() {
 
 
 	GDREGISTER_CLASS(GDExtension);
 	GDREGISTER_CLASS(GDExtension);
 
 
+	GDREGISTER_ABSTRACT_CLASS(GodotInstance);
+
 	GDREGISTER_ABSTRACT_CLASS(GDExtensionManager);
 	GDREGISTER_ABSTRACT_CLASS(GDExtensionManager);
 
 
 	GDREGISTER_ABSTRACT_CLASS(ResourceUID);
 	GDREGISTER_ABSTRACT_CLASS(ResourceUID);

+ 1 - 0
core/variant/native_ptr.h

@@ -31,6 +31,7 @@
 #pragma once
 #pragma once
 
 
 #include "core/math/audio_frame.h"
 #include "core/math/audio_frame.h"
+#include "core/variant/binder_common.h"
 #include "core/variant/method_ptrcall.h"
 #include "core/variant/method_ptrcall.h"
 #include "core/variant/type_info.h"
 #include "core/variant/type_info.h"
 
 

+ 8 - 0
doc/classes/GDExtensionManager.xml

@@ -39,6 +39,14 @@
 				Loads an extension by absolute file path. The [param path] needs to point to a valid [GDExtension]. Returns [constant LOAD_STATUS_OK] if successful.
 				Loads an extension by absolute file path. The [param path] needs to point to a valid [GDExtension]. Returns [constant LOAD_STATUS_OK] if successful.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="load_extension_from_function">
+			<return type="int" enum="GDExtensionManager.LoadStatus" />
+			<param index="0" name="path" type="String" />
+			<param index="1" name="init_func" type="const GDExtensionInitializationFunction*" />
+			<description>
+				Loads the extension already in address space via the given path and initialization function. The [param path] needs to be unique and start with [code]"libgodot://"[/code]. Returns [constant LOAD_STATUS_OK] if successful.
+			</description>
+		</method>
 		<method name="reload_extension">
 		<method name="reload_extension">
 			<return type="int" enum="GDExtensionManager.LoadStatus" />
 			<return type="int" enum="GDExtensionManager.LoadStatus" />
 			<param index="0" name="path" type="String" />
 			<param index="0" name="path" type="String" />

+ 55 - 0
doc/classes/GodotInstance.xml

@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GodotInstance" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+	<brief_description>
+		Provides access to an embedded Godot instance.
+	</brief_description>
+	<description>
+		GodotInstance represents a running Godot instance that is controlled from an outside codebase, without a perpetual main loop. It is created by the C API [code]libgodot_create_godot_instance[/code]. Only one may be created per process.
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+		<method name="focus_in">
+			<return type="void" />
+			<description>
+				Notifies the instance that it is now in focus.
+			</description>
+		</method>
+		<method name="focus_out">
+			<return type="void" />
+			<description>
+				Notifies the instance that it is now not in focus.
+			</description>
+		</method>
+		<method name="is_started">
+			<return type="bool" />
+			<description>
+				Returns [code]true[/code] if this instance has been fully started.
+			</description>
+		</method>
+		<method name="iteration">
+			<return type="bool" />
+			<description>
+				Runs a single iteration of the main loop. Returns [code]true[/code] if the engine is attempting to quit.
+			</description>
+		</method>
+		<method name="pause">
+			<return type="void" />
+			<description>
+				Notifies the instance that it is going to be paused.
+			</description>
+		</method>
+		<method name="resume">
+			<return type="void" />
+			<description>
+				Notifies the instance that it is being resumed.
+			</description>
+		</method>
+		<method name="start">
+			<return type="bool" />
+			<description>
+				Finishes this instance's startup sequence. Returns [code]true[/code] on success.
+			</description>
+		</method>
+	</methods>
+</class>

+ 7 - 3
methods.py

@@ -88,15 +88,19 @@ def redirect_emitter(target, source, env):
     Emitter to automatically redirect object/library build files to the `bin/obj` directory,
     Emitter to automatically redirect object/library build files to the `bin/obj` directory,
     retaining subfolder structure. External build files will attempt to retain subfolder
     retaining subfolder structure. External build files will attempt to retain subfolder
     structure relative to their environment's parent directory, sorted under `bin/obj/external`.
     structure relative to their environment's parent directory, sorted under `bin/obj/external`.
-    If `redirect_build_objects` is `False`, or an external build file isn't relative to the
-    passed environment, this emitter does nothing.
+    If `redirect_build_objects` is `False`, an external build file isn't relative to the passed
+    environment, or a file is being written directly into `bin`, this emitter does nothing.
     """
     """
     if not env["redirect_build_objects"]:
     if not env["redirect_build_objects"]:
         return target, source
         return target, source
 
 
     redirected_targets = []
     redirected_targets = []
     for item in target:
     for item in target:
-        if base_folder in (path := Path(item.get_abspath()).resolve()).parents:
+        path = Path(item.get_abspath()).resolve()
+
+        if path.parent == base_folder / "bin":
+            pass
+        elif base_folder in path.parents:
             item = env.File(f"#bin/obj/{path.relative_to(base_folder)}")
             item = env.File(f"#bin/obj/{path.relative_to(base_folder)}")
         elif (alt_base := Path(env.Dir(".").get_abspath()).resolve().parent) in path.parents:
         elif (alt_base := Path(env.Dir(".").get_abspath()).resolve().parent) in path.parents:
             item = env.File(f"#bin/obj/external/{path.relative_to(alt_base)}")
             item = env.File(f"#bin/obj/external/{path.relative_to(alt_base)}")

+ 12 - 1
platform/linuxbsd/SCsub

@@ -13,6 +13,11 @@ common_linuxbsd = [
     "freedesktop_at_spi_monitor.cpp",
     "freedesktop_at_spi_monitor.cpp",
 ]
 ]
 
 
+if env["library_type"] == "executable":
+    common_linuxbsd += ["godot_linuxbsd.cpp"]
+else:
+    common_linuxbsd += ["libgodot_linuxbsd.cpp"]
+
 if env["use_sowrap"]:
 if env["use_sowrap"]:
     common_linuxbsd.append("xkbcommon-so_wrap.c")
     common_linuxbsd.append("xkbcommon-so_wrap.c")
 
 
@@ -35,7 +40,13 @@ if env["dbus"]:
     if env["use_sowrap"]:
     if env["use_sowrap"]:
         common_linuxbsd.append("dbus-so_wrap.c")
         common_linuxbsd.append("dbus-so_wrap.c")
 
 
-prog = env.add_program("#bin/godot", ["godot_linuxbsd.cpp"] + common_linuxbsd)
+if env["library_type"] == "static_library":
+    prog = env.add_library("#bin/godot", common_linuxbsd)
+elif env["library_type"] == "shared_library":
+    env.Append(CCFLAGS=["-fPIC"])
+    prog = env.add_shared_library("#bin/godot", common_linuxbsd)
+else:
+    prog = env.add_program("#bin/godot", common_linuxbsd)
 
 
 if env["debug_symbols"] and env["separate_debug_symbols"]:
 if env["debug_symbols"] and env["separate_debug_symbols"]:
     env.AddPostAction(prog, env.Run(platform_linuxbsd_builders.make_debug_linuxbsd))
     env.AddPostAction(prog, env.Run(platform_linuxbsd_builders.make_debug_linuxbsd))

+ 1 - 1
platform/linuxbsd/detect.py

@@ -67,7 +67,7 @@ def get_doc_path():
 def get_flags():
 def get_flags():
     return {
     return {
         "arch": detect_arch(),
         "arch": detect_arch(),
-        "supported": ["mono"],
+        "supported": ["library", "mono"],
     }
     }
 
 
 
 

+ 71 - 0
platform/linuxbsd/libgodot_linuxbsd.cpp

@@ -0,0 +1,71 @@
+/**************************************************************************/
+/*  libgodot_linuxbsd.cpp                                                 */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#include "core/extension/libgodot.h"
+
+#include "core/extension/godot_instance.h"
+#include "main/main.h"
+
+#include "os_linuxbsd.h"
+
+static OS_LinuxBSD *os = nullptr;
+
+static GodotInstance *instance = nullptr;
+
+GDExtensionObjectPtr libgodot_create_godot_instance(int p_argc, char *p_argv[], GDExtensionInitializationFunction p_init_func) {
+	ERR_FAIL_COND_V_MSG(instance != nullptr, nullptr, "Only one Godot Instance may be created.");
+
+	os = new OS_LinuxBSD();
+
+	Error err = Main::setup(p_argv[0], p_argc - 1, &p_argv[1], false);
+	if (err != OK) {
+		return nullptr;
+	}
+
+	instance = memnew(GodotInstance);
+	if (!instance->initialize(p_init_func)) {
+		memdelete(instance);
+		// Note: When Godot Engine supports reinitialization, clear the instance pointer here.
+		//instance = nullptr;
+		return nullptr;
+	}
+
+	return (GDExtensionObjectPtr)instance;
+}
+
+void libgodot_destroy_godot_instance(GDExtensionObjectPtr p_godot_instance) {
+	GodotInstance *godot_instance = (GodotInstance *)p_godot_instance;
+	if (instance == godot_instance) {
+		godot_instance->stop();
+		memdelete(godot_instance);
+		instance = nullptr;
+		Main::cleanup();
+	}
+}

+ 11 - 2
platform/macos/SCsub

@@ -21,7 +21,6 @@ files = [
     "godot_window_delegate.mm",
     "godot_window_delegate.mm",
     "godot_window.mm",
     "godot_window.mm",
     "key_mapping_macos.mm",
     "key_mapping_macos.mm",
-    "godot_main_macos.mm",
     "godot_menu_delegate.mm",
     "godot_menu_delegate.mm",
     "godot_menu_item.mm",
     "godot_menu_item.mm",
     "godot_open_save_delegate.mm",
     "godot_open_save_delegate.mm",
@@ -39,7 +38,17 @@ if env.editor_build:
         "editor/embedded_process_macos.mm",
         "editor/embedded_process_macos.mm",
     ]
     ]
 
 
-prog = env.add_program("#bin/godot", files)
+if env["library_type"] == "executable":
+    files += ["godot_main_macos.mm"]
+else:
+    files += ["libgodot_macos.mm"]
+
+if env["library_type"] == "static_library":
+    prog = env.add_library("#bin/godot", files)
+elif env["library_type"] == "shared_library":
+    prog = env.add_shared_library("#bin/godot", files)
+else:
+    prog = env.add_program("#bin/godot", files)
 
 
 if env["debug_symbols"] and env["separate_debug_symbols"]:
 if env["debug_symbols"] and env["separate_debug_symbols"]:
     env.AddPostAction(prog, env.Run(platform_macos_builders.make_debug_macos))
     env.AddPostAction(prog, env.Run(platform_macos_builders.make_debug_macos))

+ 4 - 3
platform/macos/detect.py

@@ -61,7 +61,7 @@ def get_flags():
         "arch": detect_arch(),
         "arch": detect_arch(),
         "use_volk": False,
         "use_volk": False,
         "metal": True,
         "metal": True,
-        "supported": ["metal", "mono"],
+        "supported": ["library", "metal", "mono"],
     }
     }
 
 
 
 
@@ -172,8 +172,9 @@ def configure(env: "SConsEnvironment"):
             env.Append(CCFLAGS=["-fsanitize=thread"])
             env.Append(CCFLAGS=["-fsanitize=thread"])
             env.Append(LINKFLAGS=["-fsanitize=thread"])
             env.Append(LINKFLAGS=["-fsanitize=thread"])
 
 
-        env.Append(LINKFLAGS=["-Wl,-stack_size," + hex(STACK_SIZE_SANITIZERS)])
-    else:
+        if env["library_type"] == "executable":
+            env.Append(LINKFLAGS=["-Wl,-stack_size," + hex(STACK_SIZE_SANITIZERS)])
+    elif env["library_type"] == "executable":
         env.Append(LINKFLAGS=["-Wl,-stack_size," + hex(STACK_SIZE)])
         env.Append(LINKFLAGS=["-Wl,-stack_size," + hex(STACK_SIZE)])
 
 
     if env["use_coverage"]:
     if env["use_coverage"]:

+ 74 - 0
platform/macos/libgodot_macos.mm

@@ -0,0 +1,74 @@
+/**************************************************************************/
+/*  libgodot_macos.mm                                                     */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#include "core/extension/libgodot.h"
+
+#include "core/extension/godot_instance.h"
+#include "main/main.h"
+
+#include "os_macos.h"
+
+static OS_MacOS *os = nullptr;
+
+static GodotInstance *instance = nullptr;
+
+GDExtensionObjectPtr libgodot_create_godot_instance(int p_argc, char *p_argv[], GDExtensionInitializationFunction p_init_func) {
+	ERR_FAIL_COND_V_MSG(instance != nullptr, nullptr, "Only one Godot Instance may be created.");
+
+	uint32_t remaining_args = p_argc - 1;
+	os = new OS_MacOS_NSApp(p_argv[0], remaining_args, remaining_args > 0 ? &p_argv[1] : nullptr);
+
+	@autoreleasepool {
+		Error err = Main::setup(p_argv[0], remaining_args, remaining_args > 0 ? &p_argv[1] : nullptr, false);
+		if (err != OK) {
+			return nullptr;
+		}
+
+		instance = memnew(GodotInstance);
+		if (!instance->initialize(p_init_func)) {
+			memdelete(instance);
+			instance = nullptr;
+			return nullptr;
+		}
+
+		return (GDExtensionObjectPtr)instance;
+	}
+}
+
+void libgodot_destroy_godot_instance(GDExtensionObjectPtr p_godot_instance) {
+	GodotInstance *godot_instance = (GodotInstance *)p_godot_instance;
+	if (instance == godot_instance) {
+		godot_instance->stop();
+		memdelete(godot_instance);
+		// Note: When Godot Engine supports reinitialization, clear the instance pointer here.
+		//instance = nullptr;
+		Main::cleanup();
+	}
+}

+ 12 - 3
platform/windows/SCsub

@@ -13,7 +13,6 @@ from methods import redirect_emitter
 sources = []
 sources = []
 
 
 common_win = [
 common_win = [
-    "godot_windows.cpp",
     "os_windows.cpp",
     "os_windows.cpp",
     "display_server_windows.cpp",
     "display_server_windows.cpp",
     "key_mapping_windows.cpp",
     "key_mapping_windows.cpp",
@@ -28,6 +27,11 @@ common_win = [
     "drop_target_windows.cpp",
     "drop_target_windows.cpp",
 ]
 ]
 
 
+if env["library_type"] == "executable":
+    common_win += ["godot_windows.cpp"]
+else:
+    common_win += ["libgodot_windows.cpp"]
+
 if env.msvc:
 if env.msvc:
     common_win += ["crash_handler_windows_seh.cpp"]
     common_win += ["crash_handler_windows_seh.cpp"]
 else:
 else:
@@ -39,7 +43,7 @@ common_win_wrap = [
 
 
 env_wrap = env.Clone()
 env_wrap = env.Clone()
 
 
-if env["arch"] == "x86_64":
+if env["arch"] == "x86_64" and env["library_type"] == "executable":
     env_cpp_check = env.Clone()
     env_cpp_check = env.Clone()
     env_cpp_check.add_source_files(sources, ["cpu_feature_validation.c"])
     env_cpp_check.add_source_files(sources, ["cpu_feature_validation.c"])
     if env.msvc:
     if env.msvc:
@@ -76,7 +80,12 @@ sources += res_obj
 if env["accesskit"] and not env.msvc:
 if env["accesskit"] and not env.msvc:
     sources += env.DEFLIB("uiautomationcore")
     sources += env.DEFLIB("uiautomationcore")
 
 
-prog = env.add_program("#bin/godot", sources, PROGSUFFIX=env["PROGSUFFIX"])
+if env["library_type"] == "static_library":
+    prog = env.add_library("#bin/godot", sources, PROGSUFFIX=env["PROGSUFFIX"])
+elif env["library_type"] == "shared_library":
+    prog = env.add_shared_library("#bin/godot", sources, PROGSUFFIX=env["PROGSUFFIX"])
+else:
+    prog = env.add_program("#bin/godot", sources, PROGSUFFIX=env["PROGSUFFIX"])
 arrange_program_clean(prog)
 arrange_program_clean(prog)
 
 
 env.Depends(prog, "godot.manifest")
 env.Depends(prog, "godot.manifest")

+ 2 - 2
platform/windows/detect.py

@@ -233,7 +233,7 @@ def get_flags():
 
 
     return {
     return {
         "arch": arch,
         "arch": arch,
-        "supported": ["d3d12", "dcomp", "mono", "xaudio2"],
+        "supported": ["d3d12", "dcomp", "library", "mono", "xaudio2"],
     }
     }
 
 
 
 
@@ -243,7 +243,7 @@ def configure_msvc(env: "SConsEnvironment"):
     ## Build type
     ## Build type
 
 
     # TODO: Re-evaluate the need for this / streamline with common config.
     # TODO: Re-evaluate the need for this / streamline with common config.
-    if env["target"] == "template_release":
+    if env["target"] == "template_release" and env["library_type"] == "executable":
         env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"])
         env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"])
 
 
     if env["windows_subsystem"] == "gui":
     if env["windows_subsystem"] == "gui":

+ 71 - 0
platform/windows/libgodot_windows.cpp

@@ -0,0 +1,71 @@
+/**************************************************************************/
+/*  libgodot_windows.cpp                                                  */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#include "core/extension/libgodot.h"
+
+#include "core/extension/godot_instance.h"
+#include "main/main.h"
+
+#include "os_windows.h"
+
+static OS_Windows *os = nullptr;
+
+static GodotInstance *instance = nullptr;
+
+GDExtensionObjectPtr libgodot_create_godot_instance(int p_argc, char *p_argv[], GDExtensionInitializationFunction p_init_func) {
+	ERR_FAIL_COND_V_MSG(instance != nullptr, nullptr, "Only one Godot Instance may be created at a time.");
+
+	os = new OS_Windows(GetModuleHandle(nullptr));
+
+	Error err = Main::setup(p_argv[0], p_argc - 1, &p_argv[1], false);
+	if (err != OK) {
+		return nullptr;
+	}
+
+	instance = memnew(GodotInstance);
+	if (!instance->initialize(p_init_func)) {
+		memdelete(instance);
+		instance = nullptr;
+		return nullptr;
+	}
+
+	return (GDExtensionObjectPtr)instance;
+}
+
+void libgodot_destroy_godot_instance(GDExtensionObjectPtr p_godot_instance) {
+	GodotInstance *godot_instance = (GodotInstance *)p_godot_instance;
+	if (instance == godot_instance) {
+		godot_instance->stop();
+		memdelete(godot_instance);
+		// Note: When Godot Engine supports reinitialization, clear the instance pointer here.
+		//instance = nullptr;
+		Main::cleanup();
+	}
+}