diff --git a/SConstruct b/SConstruct index d0f3394..20d6d6e 100644 --- a/SConstruct +++ b/SConstruct @@ -1,17 +1,28 @@ #!python +import os from utils import download_wasmer, download_wasmtime, WASMER_VER_DEFAULT, WASMTIME_VER_DEFAULT # Initial options inheriting from CLI args opts = Variables([], ARGUMENTS) # Define options +opts.Add(EnumVariable("target", "Compilation target", "release", ["debug", "release"], {"d": "debug"})) +opts.Add(EnumVariable("platform", "Platform", "", ["", "windows", "linux", "osx"], {"x11": "linux", "macos": "osx"})) +opts.Add(BoolVariable("use_llvm", "Use LLVM/Clang compiler", "no")) opts.Add(EnumVariable("wasm_runtime", "Wasm runtime used", "wasmer", ["wasmer", "wasmtime"])) opts.Add(BoolVariable("download_runtime", "(Re)download runtime library", "no")) opts.Add("runtime_version", "Runtime library version", None) -# SConstruct environment from Godot CPP -env = SConscript("godot-cpp/SConstruct") -opts.Update(env) +# Standard flags CC, CCX, etc. with options +env = DefaultEnvironment(variables=opts) + +# Process some arguments +if env["platform"] == "": + exit("Invalid platform selected") + +if env["use_llvm"]: + env["CC"] = "clang" + env["CXX"] = "clang++" # Download runtime if required if env["wasm_runtime"] == "wasmer": @@ -20,7 +31,19 @@ elif env["wasm_runtime"] == "wasmtime": download_wasmtime(env, env["download_runtime"], env.get("runtime_version", WASMTIME_VER_DEFAULT)) # Check platform specifics -if env["platform"] == "windows": +if env["platform"] in ["osx", "macos"]: + env.Prepend(CFLAGS=["-std=gnu11"]) + env.Prepend(CXXFLAGS=["-std=gnu++14"]) + env.Append(CCFLAGS=["-arch", "x86_64", "-Wall", "-g", "-O3"]) + env.Append(LINKFLAGS=["-arch", "x86_64", "-framework", "Security"]) +elif env["platform"] == "linux": + env.Prepend(CFLAGS=["-std=gnu11"]) + env.Prepend(CXXFLAGS=["-std=gnu++14"]) + env.Append(CCFLAGS=["-fPIC", "-g", "-O3"]) +elif env["platform"] == "windows": + env.Prepend(CCFLAGS=["/std:c++14", "-W3", "-GR", "-O2", "-EHsc", "-MD"]) + env.Append(ENV=os.environ) # Keep session env variables to support VS 2017 prompt + env.Append(CPPDEFINES=["WIN32", "_WIN32", "_WINDOWS", "_CRT_SECURE_NO_WARNINGS", "NDEBUG"]) if env.get("use_mingw"): # MinGW env["LIBRUNTIMESUFFIX"] = ".a" env.Append(LIBS=["userenv"]) @@ -29,10 +52,11 @@ if env["platform"] == "windows": # Force Windows SDK library suffix (see https://github.com/godotengine/godot/issues/23687) env.Append(LINKFLAGS=["bcrypt.lib", "userenv.lib", "ws2_32.lib", "advapi32.lib"]) -# Defines for GDExtension specific API -env.Append(CPPDEFINES=["GDEXTENSION"]) +# Defines for GDNative specific API +env.Append(CPPDEFINES=["GDNATIVE"]) # Explicit static libraries +cpp_lib = env.File("godot-cpp/bin/libgodot-cpp.{}.{}.64{}".format(env["platform"], env["target"], env["LIBSUFFIX"])) runtime_lib = env.File( "{runtime}/lib/{prefix}{runtime}{suffix}".format( runtime=env["wasm_runtime"], @@ -42,11 +66,20 @@ runtime_lib = env.File( ) # CPP includes and libraries -env.Append(CPPPATH=[".", "{}/include".format(env["wasm_runtime"])]) -env.Append(LIBS=[runtime_lib]) +env.Append( + CPPPATH=[ + ".", + "godot-cpp/godot-headers", + "godot-cpp/include", + "{}/include".format(env["wasm_runtime"]), + "godot-cpp/include/core", + "godot-cpp/include/gen", + ] +) +env.Append(LIBS=[cpp_lib, runtime_lib]) # Godot Wasm sources -source = ["register_types.cpp", env.Glob("src/*.cpp")] +source = [env.Glob("src/*.cpp")] # Builders library = env.SharedLibrary(target="addons/godot-wasm/bin/{}/godot-wasm".format(env["platform"]), source=source) diff --git a/addons/godot-wasm/Wasm.gdns b/addons/godot-wasm/Wasm.gdns new file mode 100644 index 0000000..9c408df --- /dev/null +++ b/addons/godot-wasm/Wasm.gdns @@ -0,0 +1,8 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://addons/godot-wasm/godot-wasm.gdnlib" type="GDNativeLibrary" id=1] + +[resource] +class_name = "Wasm" +library = ExtResource( 1 ) +script_class_name = "Wasm" diff --git a/addons/godot-wasm/WasmMemory.gdns b/addons/godot-wasm/WasmMemory.gdns new file mode 100644 index 0000000..1ad17a6 --- /dev/null +++ b/addons/godot-wasm/WasmMemory.gdns @@ -0,0 +1,8 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://addons/godot-wasm/godot-wasm.gdnlib" type="GDNativeLibrary" id=1] + +[resource] +class_name = "WasmMemory" +library = ExtResource( 1 ) +script_class_name = "WasmMemory" diff --git a/addons/godot-wasm/godot-wasm.gdextension b/addons/godot-wasm/godot-wasm.gdextension deleted file mode 100644 index 6c605be..0000000 --- a/addons/godot-wasm/godot-wasm.gdextension +++ /dev/null @@ -1,14 +0,0 @@ -[configuration] -entry_symbol = "wasm_library_init" - -[libraries] -windows.debug.x86_64 = "bin/windows/godot-wasm.dll" -windows.release.x86_64 = "bin/windows/godot-wasm.dll" -windows.debug.x86_32 = "bin/windows/godot-wasm.dll" -windows.release.x86_32 = "bin/windows/godot-wasm.dll" -linux.debug.x86_64 = "bin/linux/libgodot-wasm.so" -linux.release.x86_64 = "bin/linux/libgodot-wasm.so" -linux.debug.x86_32 = "bin/linux/libgodot-wasm.so" -linux.release.x86_32 = "bin/linux/libgodot-wasm.so" -macos.debug = "bin/macos/libgodot-wasm.dylib" -macos.release = "bin/macos/libgodot-wasm.dylib" diff --git a/addons/godot-wasm/godot-wasm.gdnlib b/addons/godot-wasm/godot-wasm.gdnlib new file mode 100644 index 0000000..5f01566 --- /dev/null +++ b/addons/godot-wasm/godot-wasm.gdnlib @@ -0,0 +1,19 @@ +[general] + +singleton=false +load_once=true +symbol_prefix="wasm_" +reloadable=true + +[entry] + +OSX.64="res://addons/godot-wasm/bin/osx/libgodot-wasm.dylib" +Windows.64="res://addons/godot-wasm/bin/windows/godot-wasm.dll" +X11.64="res://addons/godot-wasm/bin/linux/libgodot-wasm.so" +Server.64="res://addons/godot-wasm/bin/linux/libgodot-wasm.so" + +[dependencies] + +OSX.64=[ ] +Windows.64=[ ] +X11.64=[ ] diff --git a/godot-cpp b/godot-cpp index 3a9118c..bbcf901 160000 --- a/godot-cpp +++ b/godot-cpp @@ -1 +1 @@ -Subproject commit 3a9118cb0dafef74773216c47a030c3a4a490747 +Subproject commit bbcf901eab69dc3d9d017942219291e78cb211b5 diff --git a/register_types.cpp b/register_types.cpp index dbdee2e..3f9ee95 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -1,36 +1,15 @@ +#ifdef GODOT_MODULE + #include "register_types.h" +#include "core/class_db.h" #include "src/godot-wasm.h" #include "src/wasm-memory.h" -using namespace godot; - -void initialize_wasm_module(ModuleInitializationLevel p_level) { - if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { - return; - } - - ClassDB::register_class(); - ClassDB::register_class(); -} - -void uninitialize_wasm_module(ModuleInitializationLevel p_level) { - if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { - return; - } +void register_wasm_types() { + ClassDB::register_class(); + ClassDB::register_class(); } -#ifndef GODOT_MODULE - -extern "C" { - GDExtensionBool GDE_EXPORT wasm_library_init(const GDExtensionInterface *p_interface, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { - godot::GDExtensionBinding::InitObject init_obj(p_interface, p_library, r_initialization); - - init_obj.register_initializer(initialize_wasm_module); - init_obj.register_terminator(uninitialize_wasm_module); - init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); - - return init_obj.init(); - } -} +void unregister_wasm_types() { } #endif diff --git a/register_types.h b/register_types.h index 045db7d..2edafef 100644 --- a/register_types.h +++ b/register_types.h @@ -1,18 +1,7 @@ #ifndef GODOT_WASM_REGISTER_TYPES_H #define GODOT_WASM_REGISTER_TYPES_H -// This define is only needed because the GODOT_MODULE define isn't detected here -// May be related to https://github.com/godotengine/godot/issues/75914 -#ifdef GDEXTENSION - #define NS godot - #include -#else - #define NS - #include "modules/register_module_types.h" - #include "core/object/class_db.h" -#endif - -void initialize_wasm_module(NS::ModuleInitializationLevel p_level); -void uninitialize_wasm_module(NS::ModuleInitializationLevel p_level); +void register_wasm_types(); +void unregister_wasm_types(); #endif diff --git a/src/defs.h b/src/defs.h index a0d46ce..eaccddd 100644 --- a/src/defs.h +++ b/src/defs.h @@ -3,16 +3,13 @@ #ifdef GODOT_MODULE // Godot includes when building module #include "core/os/os.h" - #include "core/os/time.h" #include "core/crypto/crypto.h" #include "core/io/stream_peer.h" #else // Godot addon includes - #include "godot_cpp/classes/ref_counted.hpp" - #include "godot_cpp/classes/os.hpp" - #include "godot_cpp/classes/time.hpp" - #include "godot_cpp/classes/crypto.hpp" - #include "godot_cpp/classes/stream_peer_extension.hpp" - #include "godot_cpp/variant/utility_functions.hpp" + #include + #include + #include + #include #endif #ifdef GODOT_MODULE @@ -22,19 +19,28 @@ #define REGISTRATION_METHOD _bind_methods #define RANDOM_BYTES(n) Crypto::create()->generate_random_bytes(n) #else - #define PRINT(message) UtilityFunctions::print(String(message)) - #define PRINT_ERROR(message) _err_print_error(__FUNCTION__, __FILE__, __LINE__, "Godot Wasm: " + String(message)) - #define godot_error Error - #define REGISTRATION_METHOD _bind_methods - #define RANDOM_BYTES(n) Crypto().generate_random_bytes(n) + #define OK GODOT_OK + #define FAILED GODOT_FAILED + #define ERR_INVALID_DATA GODOT_ERR_INVALID_DATA + #define ERR_COMPILATION_FAILED GODOT_ERR_COMPILATION_FAILED + #define ERR_CANT_CREATE GODOT_ERR_CANT_CREATE + #define ERR_PARAMETER_RANGE_ERROR GODOT_ERR_PARAMETER_RANGE_ERROR + #define PRINT(message) Godot::print(String(message)) + #define PRINT_ERROR(message) Godot::print_error("Godot Wasm: " + String(message), __func__, __FILE__, __LINE__) + #define GDCLASS GODOT_CLASS + #define REGISTRATION_METHOD _register_methods + #define RANDOM_BYTES(n) Crypto::_new()->generate_random_bytes(n) #endif +#define FLOAT REAL +#define RefCounted Reference +#define PackedByteArray PoolByteArray #define FAIL(message, ret) do { PRINT_ERROR(message); return ret; } while (0) #define FAIL_IF(cond, message, ret) if (unlikely(cond)) FAIL(message, ret) -#define INSTANTIATE_REF(ref) ref.instantiate() -#define BYTE_ARRAY_POINTER(array) array.ptr() -#define CMDLINE_ARGS OS::get_singleton()->get_cmdline_user_args() -#define TIME_REALTIME Time::get_singleton()->get_unix_time_from_system() * 1000000000 -#define TIME_MONOTONIC Time::get_singleton()->get_ticks_usec() * 1000 +#define INSTANTIATE_REF(ref) ref.instance() +#define BYTE_ARRAY_POINTER(array) array.read().ptr() +#define CMDLINE_ARGS PoolStringArray() // User CLI args unsupported in Godot 3 +#define TIME_REALTIME OS::get_singleton()->get_system_time_msecs() * 1000000 +#define TIME_MONOTONIC OS::get_singleton()->get_ticks_usec() * 1000 #define NULL_VARIANT Variant() #define PAGE_SIZE 65536 diff --git a/src/godot-library.cpp b/src/godot-library.cpp new file mode 100644 index 0000000..2fa2630 --- /dev/null +++ b/src/godot-library.cpp @@ -0,0 +1,21 @@ +#ifndef GODOT_MODULE + +#include "godot-wasm.h" +#include "wasm-memory.h" + +extern "C" void GDN_EXPORT wasm_gdnative_init(godot_gdnative_init_options *o) { + godot::Godot::gdnative_init(o); +} + +extern "C" void GDN_EXPORT wasm_gdnative_terminate(godot_gdnative_terminate_options *o) { + godot::Godot::gdnative_terminate(o); +} + +extern "C" void GDN_EXPORT wasm_nativescript_init(void *handle) { + godot::Godot::nativescript_init(handle); + + godot::register_class(); + godot::register_class(); +} + +#endif diff --git a/src/wasm-memory.cpp b/src/wasm-memory.cpp index 9b63bfa..9d0f4db 100644 --- a/src/wasm-memory.cpp +++ b/src/wasm-memory.cpp @@ -86,48 +86,34 @@ namespace godot { return pointer; } - godot_error WasmMemory::INTERFACE_GET_DATA { + godot_error WasmMemory::get_data(uint8_t *buffer, int bytes) { FAIL_IF(memory == NULL, "Invalid memory", ERR_INVALID_DATA); byte_t* data = wasm_memory_data(memory) + pointer; memcpy(buffer, data, bytes); pointer += bytes; - #ifndef GODOT_MODULE - *received = bytes; - #endif return OK; } - godot_error WasmMemory::INTERFACE_GET_PARTIAL_DATA { - #ifdef GODOT_MODULE - received = bytes; - return get_data(buffer, bytes); - #else - return _get_data(buffer, bytes, received); - #endif + godot_error WasmMemory::get_partial_data(uint8_t *buffer, int bytes, int &received) { + received = bytes; + return get_data(buffer, bytes); } - godot_error WasmMemory::INTERFACE_PUT_DATA { + godot_error WasmMemory::put_data(const uint8_t *buffer, int bytes) { FAIL_IF(memory == NULL, "Invalid memory", ERR_INVALID_DATA); if (bytes <= 0) return OK; byte_t* data = wasm_memory_data(memory) + pointer; memcpy(data, buffer, bytes); pointer += bytes; - #ifndef GODOT_MODULE - *sent = bytes; - #endif return OK; } - godot_error WasmMemory::INTERFACE_PUT_PARTIAL_DATA { - #ifdef GODOT_MODULE - sent = bytes; - return put_data(buffer, bytes); - #else - return _put_data(buffer, bytes, sent); - #endif + godot_error WasmMemory::put_partial_data(const uint8_t *buffer, int bytes, int &sent) { + sent = bytes; + return put_data(buffer, bytes); } - int32_t WasmMemory::INTERFACE_GET_AVAILABLE_BYTES { + int WasmMemory::get_available_bytes() const { return 0; // Not relevant } } diff --git a/src/wasm-memory.h b/src/wasm-memory.h index 7f3b853..c6c21e1 100644 --- a/src/wasm-memory.h +++ b/src/wasm-memory.h @@ -6,19 +6,9 @@ #ifdef GODOT_MODULE #define SUPER_CLASS StreamPeer #define INTERFACE_DECLARE - #define INTERFACE_GET_DATA get_data(uint8_t *buffer, int32_t bytes) - #define INTERFACE_GET_PARTIAL_DATA get_partial_data(uint8_t *buffer, int32_t bytes, int32_t &received) - #define INTERFACE_PUT_DATA put_data(const uint8_t *buffer, int32_t bytes) - #define INTERFACE_PUT_PARTIAL_DATA put_partial_data(const uint8_t *buffer, int bytes, int32_t &sent) - #define INTERFACE_GET_AVAILABLE_BYTES get_available_bytes() const #else - #define SUPER_CLASS StreamPeerExtension - #define INTERFACE_DECLARE - #define INTERFACE_GET_DATA _get_data(uint8_t *buffer, int32_t bytes, int32_t *received) - #define INTERFACE_GET_PARTIAL_DATA _get_partial_data(uint8_t *buffer, int32_t bytes, int32_t *received) - #define INTERFACE_PUT_DATA _put_data(const uint8_t *buffer, int32_t bytes, int32_t *sent) - #define INTERFACE_PUT_PARTIAL_DATA _put_partial_data(const uint8_t *buffer, int32_t bytes, int32_t *sent) - #define INTERFACE_GET_AVAILABLE_BYTES _get_available_bytes() const + #define SUPER_CLASS StreamPeerGDNative + #define INTERFACE_DECLARE godot_net_stream_peer interface #endif namespace godot { @@ -41,11 +31,11 @@ namespace godot { godot_error grow(uint32_t pages); Ref seek(int p_pos); uint32_t get_position() const; - godot_error INTERFACE_GET_DATA override; - godot_error INTERFACE_GET_PARTIAL_DATA override; - godot_error INTERFACE_PUT_DATA override; - godot_error INTERFACE_PUT_PARTIAL_DATA override; - int32_t INTERFACE_GET_AVAILABLE_BYTES override; + godot_error get_data(uint8_t* buffer, int bytes); + godot_error get_partial_data(uint8_t* buffer, int bytes, int& received); + godot_error put_data(const uint8_t* buffer, int bytes); + godot_error put_partial_data(const uint8_t* buffer, int bytes, int& sent); + int get_available_bytes() const; }; }