Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LibGodot with GDExtension #72883

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ env_base.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix

env_base.__class__.add_shared_library = methods.add_shared_library
env_base.__class__.add_library = methods.add_library
env_base.__class__.add_program = methods.add_program
env_base.__class__.CommandNoCache = methods.CommandNoCache
env_base.__class__.Run = methods.Run
env_base.__class__.disable_warnings = methods.disable_warnings
Expand Down Expand Up @@ -198,6 +197,14 @@ opts.Add("custom_modules", "A list of comma-separated directory paths containing
opts.Add(BoolVariable("custom_modules_recursive", "Detect custom modules recursively for each specified path.", True))

# Advanced options
opts.Add(
EnumVariable(
"library_type",
"Build library type",
"executable",
("executable", "static_library", "shared_library"),
)
)
opts.Add(BoolVariable("dev_mode", "Alias for dev options: verbose=yes warnings=extra werror=yes tests=yes", False))
opts.Add(BoolVariable("tests", "Build the unit tests", False))
opts.Add(BoolVariable("fast_unsafe", "Enable unsafe options for faster rebuilds", False))
Expand Down Expand Up @@ -270,6 +277,15 @@ opts.Update(env_base)

selected_platform = ""

if env_base["library_type"] == "static_library":
env_base.Append(CPPDEFINES=["LIBRARY_ENABLED"])
elif env_base["library_type"] == "shared_library":
env_base.Append(CPPDEFINES=["LIBRARY_ENABLED"])
env_base.Append(CCFLAGS=["-fPIC"])
env_base.Append(STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME=True)
else:
env_base.__class__.add_program = methods.add_program

if env_base["platform"] != "":
selected_platform = env_base["platform"]
elif env_base["p"] != "":
Expand Down
1 change: 1 addition & 0 deletions core/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ SConscript("debugger/SCsub")
SConscript("input/SCsub")
SConscript("variant/SCsub")
SConscript("extension/SCsub")
SConscript("libgodot/SCsub")
SConscript("object/SCsub")
SConscript("templates/SCsub")
SConscript("string/SCsub")
Expand Down
5 changes: 5 additions & 0 deletions core/config/project_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ const PackedStringArray ProjectSettings::get_required_features() {
// Returns the features supported by this build of Godot. Includes all required features.
const PackedStringArray ProjectSettings::_get_supported_features() {
PackedStringArray features = get_required_features();

#ifdef LIBRARY_ENABLED
features.append("Embedded");
#endif

#ifdef MODULE_MONO_ENABLED
features.append("C#");
#endif
Expand Down
24 changes: 18 additions & 6 deletions core/extension/gdextension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,9 +470,14 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb
}

GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr;
GDExtensionBool ret = initialization_function(&gdextension_get_proc_address, this, &initialization);
return initialize_extension_function(initialization_function, p_entry_symbol);
}

if (ret) {
Error GDExtension::initialize_extension_function(GDExtensionInitializationFunction initialization_function, const String &p_entry_symbol) {
if (library == nullptr) {
embedded = true;
}
if (initialization_function(&gdextension_get_proc_address, this, &initialization)) {
level_initialized = -1;
return OK;
} else {
Expand All @@ -482,6 +487,9 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb
}

void GDExtension::close_library() {
if (embedded) {
return;
}
ERR_FAIL_COND(library == nullptr);
OS::get_singleton()->close_dynamic_library(library);

Expand All @@ -495,17 +503,21 @@ void GDExtension::close_library() {
library = nullptr;
}

bool GDExtension::is_embedded() const {
return embedded;
}

bool GDExtension::is_library_open() const {
return library != nullptr;
return (library != nullptr) || embedded;
Comment on lines -499 to +511
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having to check for embedded at runtime in so many places doesn't feel quite right. Compiling Godot in this "LibGodot mode" is not the common case. Can all these checks be #ifndef'd away for when running Godot normally?

}

GDExtension::InitializationLevel GDExtension::get_minimum_library_initialization_level() const {
ERR_FAIL_COND_V(library == nullptr, INITIALIZATION_LEVEL_CORE);
ERR_FAIL_COND_V(library == nullptr && !embedded, INITIALIZATION_LEVEL_CORE);
return InitializationLevel(initialization.minimum_initialization_level);
}

void GDExtension::initialize_library(InitializationLevel p_level) {
ERR_FAIL_COND(library == nullptr);
ERR_FAIL_COND(library == nullptr && !embedded);
ERR_FAIL_COND_MSG(p_level <= int32_t(level_initialized), vformat("Level '%d' must be higher than the current level '%d'", p_level, level_initialized));

level_initialized = int32_t(p_level);
Expand All @@ -515,7 +527,7 @@ void GDExtension::initialize_library(InitializationLevel p_level) {
initialization.initialize(initialization.userdata, GDExtensionInitializationLevel(p_level));
}
void GDExtension::deinitialize_library(InitializationLevel p_level) {
ERR_FAIL_COND(library == nullptr);
ERR_FAIL_COND(library == nullptr && !embedded);
ERR_FAIL_COND(p_level > int32_t(level_initialized));

level_initialized = int32_t(p_level) - 1;
Expand Down
4 changes: 3 additions & 1 deletion core/extension/gdextension.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
class GDExtension : public Resource {
GDCLASS(GDExtension, Resource)

bool embedded = false;
void *library = nullptr; // pointer if valid,
String library_path;
#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
Expand Down Expand Up @@ -76,6 +77,7 @@ class GDExtension : public Resource {
static String get_extension_list_config_file();
static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr);

Error initialize_extension_function(GDExtensionInitializationFunction initialization_function, const String &p_entry_symbol);
Error open_library(const String &p_path, const String &p_entry_symbol);
void close_library();

Expand All @@ -90,7 +92,7 @@ class GDExtension : public Resource {
INITIALIZATION_LEVEL_SCENE = GDEXTENSION_INITIALIZATION_SCENE,
INITIALIZATION_LEVEL_EDITOR = GDEXTENSION_INITIALIZATION_EDITOR
};

bool is_embedded() const;
bool is_library_open() const;

InitializationLevel get_minimum_library_initialization_level() const;
Expand Down
2 changes: 2 additions & 0 deletions core/extension/gdextension_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1077,10 +1077,12 @@ static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionC
return nullptr;
}
ERR_FAIL_COND_V(!mb, nullptr);
#ifndef LIBRARY_ENABLED
if (mb->get_hash() != p_hash) {
ERR_PRINT("Hash mismatch for method '" + classname + "." + methodname + "'.");
return nullptr;
}
#endif
Comment on lines +1080 to +1085
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we want to disable checking for hash mismatches here? Given that GDExtension does ptrcalls to any methods returned, calling a method with an unexpected signature can lead to crashes.

return (GDExtensionMethodBindPtr)mb;
}

Expand Down
6 changes: 6 additions & 0 deletions core/extension/gdextension_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@

#include "gdextension_manager.h"
#include "core/io/file_access.h"
#ifdef LIBRARY_ENABLED
#include "core/libgodot/libgodot.h"
#endif

GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String &p_path) {
if (gdextension_map.has(p_path)) {
Expand Down Expand Up @@ -143,6 +146,9 @@ void GDExtensionManager::load_extensions() {
ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, "Error loading extension: " + s);
}
}
#ifdef LIBRARY_ENABLED
libgodot_init_resource();
#endif
}

GDExtensionManager *GDExtensionManager::get_singleton() {
Expand Down
3 changes: 3 additions & 0 deletions core/io/logger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ void RotatedFileLogger::rotate_file() {
}

file = FileAccess::open(base_path, FileAccess::WRITE);
if (file == nullptr) {
return;
}
file->detach_from_objectdb(); // Note: This FileAccess instance will exist longer than ObjectDB, therefore can't be registered in ObjectDB.
}

Expand Down
5 changes: 5 additions & 0 deletions core/libgodot/SCsub
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env python

Import("env")
env_libgodot = env.Clone()
env_libgodot.add_source_files(env.core_sources, "*.cpp")
85 changes: 85 additions & 0 deletions core/libgodot/libgodot.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**************************************************************************/
/* libgodot.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. */
/**************************************************************************/

#ifdef LIBRARY_ENABLED
#include "libgodot.h"
#include "core/extension/gdextension_manager.h"

#ifdef __cplusplus
extern "C" {
#endif

void *godotsharp_game_main_init;
GDExtensionInitializationFunction initialization_function;
void (*scene_load_function)(void *);

void *libgodot_sharp_main_init() {
return godotsharp_game_main_init;
}

LIBGODOT_API void libgodot_mono_bind(void *sharp_main_init, void (*scene_function_bind)(void *)) {
godotsharp_game_main_init = sharp_main_init;
scene_load_function = scene_function_bind;
}

LIBGODOT_API void libgodot_gdextension_bind(GDExtensionInitializationFunction initialization_bind, void (*scene_function_bind)(void *)) {
initialization_function = initialization_bind;
scene_load_function = scene_function_bind;
}

void libgodot_scene_load(void *scene) {
if (scene_load_function != nullptr) {
scene_load_function(scene);
}
}

int libgodot_is_scene_loadable() {
return scene_load_function != nullptr;
}

void libgodot_init_resource() {
if (initialization_function != nullptr) {
Ref<GDExtension> libgodot;
libgodot.instantiate();
Error err = libgodot->initialize_extension_function(initialization_function, "LibGodot");
if (err != OK) {
ERR_PRINT("LibGodot Had an error initialize_extension_function'");
} else {
print_verbose("LibGodot initialization");
libgodot->set_path("res://LibGodotGDExtension");
GDExtensionManager::get_singleton()->load_extension("res://LibGodotGDExtension");
}
}
}

#ifdef __cplusplus
}
#endif
#endif
71 changes: 71 additions & 0 deletions core/libgodot/libgodot.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/

#ifndef LIBGODOT_H
#define LIBGODOT_H

#if defined(LIBRARY_ENABLED)

#if defined(WINDOWS_ENABLED) | defined(UWP_ENABLED)
#define LIBGODOT_API __declspec(dllexport)
#elif defined(ANDROID_ENABLED)
#include <jni.h>
#define LIBGODOT_API JNIEXPORT
#else
#define LIBGODOT_API
#endif

#include "core/extension/gdextension_interface.h"

#ifdef __cplusplus
extern "C" {
#endif

void libgodot_init_resource();

void libgodot_scene_load(void *scene);

int libgodot_is_scene_loadable();

Faolan-Rad marked this conversation as resolved.
Show resolved Hide resolved
void *libgodot_sharp_main_init();

LIBGODOT_API void libgodot_mono_bind(void *sharp_main_init, void (*scene_function_bind)(void *));

LIBGODOT_API void libgodot_gdextension_bind(GDExtensionInitializationFunction initialization_bind, void (*scene_function_bind)(void *));

LIBGODOT_API int godot_main(int argc, char *argv[]);

#ifdef __cplusplus
}
#endif

#endif

#endif // LIBGODOT_H
4 changes: 3 additions & 1 deletion editor/editor_file_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2488,7 +2488,9 @@ bool EditorFileSystem::_scan_extensions() {
Vector<String> loaded_extensions = GDExtensionManager::get_singleton()->get_loaded_extensions();
for (int i = 0; i < loaded_extensions.size(); i++) {
if (!extensions.has(loaded_extensions[i])) {
extensions_removed.push_back(loaded_extensions[i]);
if (!GDExtensionManager::get_singleton()->get_extension(loaded_extensions[i])->is_embedded()) {
extensions_removed.push_back(loaded_extensions[i]);
}
}
}

Expand Down
Loading