diff --git a/.gitignore b/.gitignore index d9888cc..55c4914 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ addons.zip *.so *.dylib *.dll +*.o +__pycache__ diff --git a/README.md b/README.md index 236b4c0..f298649 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,14 @@ A Godot addon allowing for loading and interacting with [WebAssembly (Wasm)](https://webassembly.org) modules from GDScript. Note that this project is still in development. -This [GDNative](https://docs.godotengine.org/en/stable/tutorials/scripting/gdnative/what_is_gdnative.html) addon uses [Wasmer](https://wasmer.io) as the WebAssembly runtime. +Godot Wasm can be used either as a [GDNative](https://docs.godotengine.org/en/stable/tutorials/scripting/gdnative/what_is_gdnative.html) addon or Godot module. It uses [Wasmer](https://wasmer.io) as the WebAssembly runtime. ## Installation +Godot Wasm supports installation via addon or module. Installing as an addon is far faster and simpler and requires merely including the asset in your Godot project while installing the module requires recompilation of the Godot engine. Note that exporting to web/HTML5 is only supported for modules (see [#15](https://github.com/ashtonmeuser/godot-wasm/issues/15)). + +### Addon + Installation in a Godot project involves simply downloading and installing a zip file from Godot's UI. Recompilation of the engine is *not* required. 1. Download the Godot Wasm addon zip file from the [releases page](https://github.com/ashtonmeuser/godot-wasm/releases). @@ -33,6 +37,19 @@ Installation in a Godot project involves simply downloading and installing a zip Alternatively, you can use the Asset Library tab within the Godot editor, search for "Wasm", and follow the prompts to install. Yet another alternative is downloading directly from the [asset page](https://godotengine.org/asset-library/asset/1798) and following the installation instructions above. Note that the Asset Library has an approval process that can take several days and may therefore be a version or two behind. +### Godot Module + +Installation as a Godot module requires recompilation of the Godot engine. This enables exporting to web/HTML5. + +1. Clone or download the [Godot engine](https://github.com/godotengine/godot) following [this guide](https://docs.godotengine.org/en/3.5/development/compiling/getting_source.html). +1. Download the project source via the [releases page](https://github.com/ashtonmeuser/godot-wasm/releases) or Code → Download ZIP on GitHub. +1. Include the entire Godot Wasm directory within the *godot/modules* directory. +1. Rename the Godot Wasm directory to *wasm*. All project files e.g. *SCsub* should now be in *godot/modules/wasm*. + +Recompile the Godot engine following [this guide](https://docs.godotengine.org/en/3.5/development/compiling/index.html#toc-devel-compiling). More information on custom Godot modules can be found in [this guide](https://docs.godotengine.org/en/3.5/development/cpp/custom_modules_in_cpp.html). + +To export using the customized engine, you'll also need to recompile the export templates (documented in the Engine compilation guide). + ## Usage Once installed as an addon in a Godot project, the Godot Wasm addon class can be accessed via `Wasm`. @@ -56,7 +73,7 @@ Once installed as an addon in a Godot project, the Godot Wasm addon class can be ### Exporting Godot Project > **Note** -> Exporting to web/HTML5 using a GDNative addon is not supported by Godot. See [godotengine/godot#12243](https://github.com/godotengine/godot/issues/12243). +> Exporting to web/HTML5 using a GDNative addon is not supported by Godot. See [godotengine/godot#12243](https://github.com/godotengine/godot/issues/12243). Install as a Godot Module to enable web/HTML5 exports. Exporting from Godot may require the following additional steps. See the export configuration of the [example Godot project](https://github.com/ashtonmeuser/godot-wasm/tree/master/examples) for a practical illustration. diff --git a/SConstruct b/SConstruct index 97f261c..323f355 100644 --- a/SConstruct +++ b/SConstruct @@ -5,6 +5,7 @@ import re from urllib import request import tarfile +# Initial options inheriting from CLI args opts = Variables([], ARGUMENTS) # Define options @@ -56,7 +57,7 @@ if env['download_wasmer'] or not os.path.isdir('wasmer'): # Check platform specifics if env['platform'] == 'osx': - env.Append(CCFLAGS=['-arch', 'x86_64']) + env.Append(CCFLAGS=['-arch', 'x86_64', '-Wall']) env.Append(CXXFLAGS=['-std=c++17']) env.Append(LINKFLAGS=['-arch', 'x86_64', '-framework', 'Security']) if env['target'] in ('debug', 'd'): diff --git a/SCsub b/SCsub new file mode 100644 index 0000000..a27cecf --- /dev/null +++ b/SCsub @@ -0,0 +1,28 @@ +# Import env and create module-specific clone +Import('env') +module_env = env.Clone() + +# Check platform specifics +if env['platform']=='linuxbsd': + module_env['LIBSUFFIX'] = '.a' + module_env.Append(CXXFLAGS=['-std=c++17']) +elif env['platform']=='osx': + module_env['LIBSUFFIX'] = '.a' + module_env.Append(CXXFLAGS=['-std=c++17']) + env.Append(LINKFLAGS=['-framework', 'Security']) +elif env['platform']=='windows': + module_env['LIBSUFFIX'] = '.lib' + +# Explicit static libraries +wasmer_library = env.File('{}wasmer{}'.format(env['LIBPREFIX'], module_env.get('LIBWASMERSUFFIX', module_env['LIBSUFFIX']))) + +# Linked libraries (global env) and includes (cloned env) +env.Append(LIBPATH=[env.Dir('wasmer/lib').abspath]) +env.Append(LIBS=[wasmer_library]) +module_env.Append(CPPPATH=['wasmer/include']) + +# Defines for module agnosticism +module_env.Append(CPPDEFINES=["GODOT_MODULE"]) + +# Module sources +module_env.add_source_files(env.modules_sources, ['register_types.cpp', env.Glob('src/*.cpp', exclude='src/godot-library.cpp')]) diff --git a/config.py b/config.py new file mode 100644 index 0000000..1c8cd12 --- /dev/null +++ b/config.py @@ -0,0 +1,5 @@ +def can_build(env, platform): + return True + +def configure(env): + pass diff --git a/register_types.cpp b/register_types.cpp new file mode 100644 index 0000000..0d8edb5 --- /dev/null +++ b/register_types.cpp @@ -0,0 +1,16 @@ +#ifdef GODOT_MODULE + +#include "register_types.h" + +#include "core/class_db.h" +#include "src/godot-wasm.h" +#include "src/stream-peer-wasm.h" + +void register_wasm_types() { + ClassDB::register_class(); + ClassDB::register_class(); +} + +void unregister_wasm_types() { } + +#endif diff --git a/register_types.h b/register_types.h new file mode 100644 index 0000000..b53d714 --- /dev/null +++ b/register_types.h @@ -0,0 +1,2 @@ +void register_wasm_types(); +void unregister_wasm_types(); diff --git a/src/defs.h b/src/defs.h index 155a155..df4d848 100644 --- a/src/defs.h +++ b/src/defs.h @@ -1,17 +1,37 @@ #ifndef GODOT_WASM_DEFS_H #define GODOT_WASM_DEFS_H +#ifdef GODOT_MODULE // Godot includes when building module + #include "core/io/stream_peer.h" +#else // Godot addon includes + #include + #include +#endif + namespace { #ifdef __GNUC__ #define UNLIKELY(cond) __builtin_expect(!!(cond), 0) #else #define UNLIKELY(cond) cond #endif - #define GDERROR(error) GODOT_##error - #define PRINT_ERROR(message) godot::Godot::print_error("Godot Wasm: " + godot::String(message), __func__, __FILE__, __LINE__); + #ifdef GODOT_MODULE + #define NS + #define godot_error Error + #define PRINT_ERROR(message) print_error("Godot Wasm: " + String(message)) + #define REGISTRATION_METHOD _bind_methods + #else + #define NS godot + #define OK GODOT_OK + #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 PRINT_ERROR(message) godot::Godot::print_error("Godot Wasm: " + godot::String(message), __func__, __FILE__, __LINE__) + #define GDCLASS GODOT_CLASS + #define REGISTRATION_METHOD _register_methods + #endif #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 NULL_VARIANT godot::Variant() + #define NULL_VARIANT NS::Variant() #define PAGE_SIZE 65536 } diff --git a/src/godot-library.cpp b/src/godot-library.cpp index 6b7e6b7..9ec0635 100644 --- a/src/godot-library.cpp +++ b/src/godot-library.cpp @@ -1,3 +1,5 @@ +#ifndef GODOT_MODULE + #include "godot-wasm.h" #include "stream-peer-wasm.h" @@ -15,3 +17,5 @@ extern "C" void GDN_EXPORT wasm_nativescript_init(void *handle) { godot::register_class(); godot::register_class(); } + +#endif diff --git a/src/godot-wasm.cpp b/src/godot-wasm.cpp index f31f408..e68e76e 100644 --- a/src/godot-wasm.cpp +++ b/src/godot-wasm.cpp @@ -6,37 +6,39 @@ namespace { }; struct context_callback: public context_extern { - godot::Object* target; - godot::String method; // External name; doesn't necessarily match import name + NS::Object* target; + NS::String method; // External name; doesn't necessarily match import name + context_callback() { } + context_callback(uint16_t i): context_extern { i } { } }; - godot::Variant decode_variant(wasm_val_t value) { + NS::Variant decode_variant(wasm_val_t value) { switch (value.kind) { - case WASM_I32: return godot::Variant(value.of.i32); - case WASM_I64: return godot::Variant(value.of.i64); - case WASM_F32: return godot::Variant(value.of.f32); - case WASM_F64: return godot::Variant(value.of.f64); + case WASM_I32: return NS::Variant(value.of.i32); + case WASM_I64: return NS::Variant(value.of.i64); + case WASM_F32: return NS::Variant(value.of.f32); + case WASM_F64: return NS::Variant(value.of.f64); case WASM_ANYREF: if (value.of.ref == NULL) return NULL_VARIANT; default: throw std::invalid_argument("Unsupported Wasm type"); } } - wasm_val_t encode_variant(godot::Variant variant) { + wasm_val_t encode_variant(NS::Variant variant) { switch (variant.get_type()) { - case godot::Variant::INT: return WASM_I64_VAL((int64_t)variant); - case godot::Variant::REAL: return WASM_F64_VAL((float64_t)variant); + case NS::Variant::INT: return WASM_I64_VAL((int64_t)variant); + case NS::Variant::REAL: return WASM_F64_VAL((float64_t)variant); default: throw std::invalid_argument("Unsupported Godot variant type"); } } - godot::String decode_name(const wasm_name_t* name) { - return godot::String(std::string(name->data, name->size).c_str()); + NS::String decode_name(const wasm_name_t* name) { + return NS::String(std::string(name->data, name->size).c_str()); } - void push_results(godot::Variant variant, wasm_val_vec_t* results) { // TODO: Rename + void push_results(NS::Variant variant, wasm_val_vec_t* results) { // TODO: Rename if (results->size <= 0) return; - if (variant.get_type() == godot::Variant::ARRAY) { - godot::Array array = (godot::Array)variant; + if (variant.get_type() == NS::Variant::ARRAY) { + NS::Array array = variant.operator NS::Array(); if (array.size() != results->size) throw std::length_error("Results length mismatch"); for (uint16_t i = 0; i < results->size; i++) results->data[i] = encode_variant(array[i]); } else if (results->size == 1) { @@ -50,15 +52,15 @@ namespace { return exports.data[index]; } - godot::Variant::Type get_value_type(const wasm_valkind_t& kind) { + NS::Variant::Type get_value_type(const wasm_valkind_t& kind) { switch (kind) { - case WASM_I32: case WASM_I64: return godot::Variant::Type::INT; - case WASM_F32: case WASM_F64: return godot::Variant::Type::REAL; + case WASM_I32: case WASM_I64: return NS::Variant::Type::INT; + case WASM_F32: case WASM_F64: return NS::Variant::Type::REAL; default: throw std::invalid_argument("Unsupported value kind"); } } - godot::Array get_extern_signature(const wasm_module_t* module, uint16_t index, bool import) { + NS::Array get_extern_signature(const wasm_module_t* module, uint16_t index, bool import) { // Grab the extern from module imports or exports const wasm_externtype_t* type; if (import) { @@ -77,15 +79,18 @@ namespace { wasm_functype_t* func_type = wasm_externtype_as_functype((wasm_externtype_t*)type); const wasm_valtype_vec_t* func_params = wasm_functype_params(func_type); const wasm_valtype_vec_t* func_results = wasm_functype_results(func_type); - godot::Array param_types, result_types; + NS::Array signature, param_types, result_types; for (uint16_t i = 0; i < func_params->size; i++) param_types.append(get_value_type(wasm_valtype_kind(func_params->data[i]))); for (uint16_t i = 0; i < func_results->size; i++) result_types.append(get_value_type(wasm_valtype_kind(func_results->data[i]))); - return godot::Array().make(param_types, result_types); + signature.append(param_types); + signature.append(result_types); + return signature; } case WASM_EXTERN_GLOBAL: { wasm_globaltype_t* global_type = wasm_externtype_as_globaltype((wasm_externtype_t*)type); - const godot::Variant variant_type = get_value_type(wasm_valtype_kind(wasm_globaltype_content(global_type))); - const godot::Variant variant_mutability = godot::Variant(wasm_globaltype_mutability(global_type) == WASM_VAR ? true : false); - return godot::Array().make(variant_type, variant_mutability); + NS::Array signature; + signature.append(get_value_type(wasm_valtype_kind(wasm_globaltype_content(global_type)))); + signature.append(NS::Variant(wasm_globaltype_mutability(global_type) == WASM_VAR ? true : false)); + return signature; } default: throw std::invalid_argument("Extern type has no signature"); } } @@ -100,11 +105,11 @@ namespace { // This is invoked by Wasm module calls to imported functions // Must be free function so context is passed via the env void pointer context_callback* context = (context_callback*)env; - godot::Array params = godot::Array(); + NS::Array params = NS::Array(); // TODO: Check if args and results match expected sizes for (uint16_t i = 0; i < args->size; i++) params.push_back(decode_variant(args->data[i])); // TODO: Ensure target is valid and has method - godot::Variant variant = context->target->callv(context->method, params); + NS::Variant variant = context->target->callv(context->method, params); try { push_results(variant, results); } catch (const std::invalid_argument& e) { FAIL(e.what(), make_trap(e)); } catch (const std::length_error& e) { FAIL(e.what(), make_trap(e)); } @@ -113,14 +118,25 @@ namespace { } namespace godot { - void Wasm::_register_methods() { - register_method("compile", &Wasm::compile); - register_method("instantiate", &Wasm::instantiate); - register_method("load", &Wasm::load); - register_method("inspect", &Wasm::inspect); - register_method("global", &Wasm::global); - register_method("function", &Wasm::function); - register_property>("stream", &Wasm::stream, NULL); + void Wasm::REGISTRATION_METHOD() { + #ifdef GODOT_MODULE + ClassDB::bind_method(D_METHOD("compile", "bytecode"), &Wasm::compile); + ClassDB::bind_method(D_METHOD("instantiate", "import_map"), &Wasm::instantiate); + ClassDB::bind_method(D_METHOD("load", "bytecode", "import_map"), &Wasm::load); + ClassDB::bind_method(D_METHOD("inspect"), &Wasm::inspect); + ClassDB::bind_method(D_METHOD("global", "name"), &Wasm::global); + ClassDB::bind_method(D_METHOD("function", "name", "args"), &Wasm::function); + ClassDB::bind_method(D_METHOD("get_stream"), &Wasm::get_stream); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream"), "", "get_stream"); + #else + register_method("compile", &Wasm::compile); + register_method("instantiate", &Wasm::instantiate); + register_method("load", &Wasm::load); + register_method("inspect", &Wasm::inspect); + register_method("global", &Wasm::global); + register_method("function", &Wasm::function); + register_property>("stream", &Wasm::stream, NULL); + #endif } Wasm::Wasm() { @@ -141,6 +157,10 @@ namespace godot { void Wasm::_init() { } + Ref Wasm::get_stream() const { + return stream; + }; + godot_error Wasm::compile(PoolByteArray bytecode) { // Reset instance = NULL; @@ -155,17 +175,17 @@ namespace godot { memcpy(wasm_bytes.data, bytecode.read().ptr(), bytecode.size()); // Validate binary - FAIL_IF(!wasm_module_validate(store, &wasm_bytes), "Invalid binary", GDERROR(ERR_INVALID_DATA)); + FAIL_IF(!wasm_module_validate(store, &wasm_bytes), "Invalid binary", ERR_INVALID_DATA); // Compile module = wasm_module_new(store, &wasm_bytes); wasm_byte_vec_delete(&wasm_bytes); - FAIL_IF(module == NULL, "Compilation failed", GDERROR(ERR_COMPILATION_FAILED)); + FAIL_IF(module == NULL, "Compilation failed", ERR_COMPILATION_FAILED); // Map names to export indices map_names(); - return GDERROR(OK); + return OK; } godot_error Wasm::instantiate(const Dictionary import_map) { @@ -173,9 +193,9 @@ namespace godot { std::vector externs; for (const auto &tuple: import_funcs) { const Array& import = ((Dictionary)import_map["functions"])[tuple.first]; - FAIL_IF(import.size() != 2, "Invalid or missing import function " + tuple.first, GDERROR(ERR_CANT_CREATE)); - FAIL_IF(import[0].get_type() != Variant::OBJECT, "Invalid import target", GDERROR(ERR_CANT_CREATE)); - FAIL_IF(import[1].get_type() != Variant::STRING, "Invalid import method", GDERROR(ERR_CANT_CREATE)); + FAIL_IF(import.size() != 2, "Invalid or missing import function " + tuple.first, ERR_CANT_CREATE); + FAIL_IF(import[0].get_type() != Variant::OBJECT, "Invalid import target", ERR_CANT_CREATE); + FAIL_IF(import[1].get_type() != Variant::STRING, "Invalid import method", ERR_CANT_CREATE); context_callback* context = (context_callback*)&tuple.second; context->target = import[0]; context->method = import[1]; @@ -186,18 +206,18 @@ namespace godot { // Instantiate with imports instance = wasm_instance_new(store, module, &imports, NULL); - FAIL_IF(instance == NULL, "Instantiation failed", GDERROR(ERR_CANT_CREATE)); + FAIL_IF(instance == NULL, "Instantiation failed", ERR_CANT_CREATE); // Set stream peer memory reference stream->memory = wasm_extern_as_memory(get_export_data(instance, memory_index)); - return GDERROR(OK); + return OK; } godot_error Wasm::load(PoolByteArray bytecode, const Dictionary import_map) { // Compile and instantiate in one go godot_error err = compile(bytecode); - if (err != GDERROR(OK)) return err; + if (err != OK) return err; return instantiate(import_map); } @@ -298,7 +318,7 @@ namespace godot { const String key = decode_name(wasm_importtype_module(imports.data[i])) + "." + decode_name(wasm_importtype_name(imports.data[i])); switch (kind) { case WASM_EXTERN_FUNC: - import_funcs[key] = context_callback { i }; + import_funcs[key] = { i }; break; default: throw std::invalid_argument("Import type not implemented"); } @@ -313,10 +333,10 @@ namespace godot { const String key = decode_name(wasm_exporttype_name(exports.data[i])); switch (kind) { case WASM_EXTERN_FUNC: - export_funcs[key] = context_extern { i }; + export_funcs[key] = { i }; break; case WASM_EXTERN_GLOBAL: - export_globals[key] = context_extern { i }; + export_globals[key] = { i }; break; case WASM_EXTERN_MEMORY: memory_index = i; @@ -331,8 +351,6 @@ namespace godot { wasm_module_imports(module, &imports); const wasm_externtype_t* type = wasm_importtype_type(imports.data[context->index]); const wasm_functype_t* func_type = wasm_externtype_as_functype((wasm_externtype_t*)type); - wasm_valtype_vec_t* params = (wasm_valtype_vec_t*)wasm_functype_params(func_type); - wasm_valtype_vec_t* results = (wasm_valtype_vec_t*)wasm_functype_results(func_type); wasm_func_t* func = wasm_func_new_with_env(store, func_type, callback_wrapper, context, NULL); return func; } diff --git a/src/godot-wasm.h b/src/godot-wasm.h index 8d92476..dddbafe 100644 --- a/src/godot-wasm.h +++ b/src/godot-wasm.h @@ -5,7 +5,6 @@ #include #include #include -#include #include "wasmer.h" #include "defs.h" #include "stream-peer-wasm.h" @@ -17,7 +16,7 @@ namespace { namespace godot { class Wasm : public Reference { - GODOT_CLASS(Wasm, Reference) + GDCLASS(Wasm, Reference); private: wasm_engine_t* engine; @@ -32,7 +31,7 @@ namespace godot { wasm_func_t* create_callback(context_callback* context); public: - static void _register_methods(); + static void REGISTRATION_METHOD(); Wasm(); ~Wasm(); void _init(); @@ -44,6 +43,7 @@ namespace godot { Variant global(String name); uint64_t mem_size(); Ref stream; + Ref get_stream() const; }; } diff --git a/src/stream-peer-wasm.cpp b/src/stream-peer-wasm.cpp index 85e3306..4306bf6 100644 --- a/src/stream-peer-wasm.cpp +++ b/src/stream-peer-wasm.cpp @@ -1,33 +1,33 @@ #include "stream-peer-wasm.h" namespace { - #define GDNATIVE_VERSION { 3, 5 } // No clue if this is correct - - // StreamPeerGDNative interface - godot_error _get_data(void *user, uint8_t *p_buffer, int p_bytes) { return ((godot::StreamPeerWasm *)user)->get_data(p_buffer, p_bytes); } - godot_error _get_partial_data(void *user, uint8_t *p_buffer, int p_bytes, int *r_received) { return ((godot::StreamPeerWasm *)user)->get_partial_data(p_buffer, p_bytes, r_received); } - godot_error _put_data(void *user, const uint8_t *p_data, int p_bytes) { return ((godot::StreamPeerWasm *)user)->put_data(p_data, p_bytes); } - godot_error _put_partial_data(void *user, const uint8_t *p_data, int p_bytes, int *r_sent) { return ((godot::StreamPeerWasm *)user)->put_partial_data(p_data, p_bytes, r_sent); } - int _get_available_bytes(const void *user) { return ((godot::StreamPeerWasm *)user)->get_available_bytes(); } + #ifdef GODOT_MODULE + #define INTERFACE_DEFINE + #define INTERFACE_INIT + #else + #define INTERFACE_DEFINE interface = { { 3, 5 }, this, &_get_data, &_get_partial_data, &_put_data, &_put_partial_data, &_get_available_bytes, NULL } + #define INTERFACE_INIT net_api->godot_net_bind_stream_peer(_owner, &interface) + godot_error _get_data(void* user, uint8_t* p_buffer, int p_bytes) { return ((godot::StreamPeerWasm*)user)->get_data(p_buffer, p_bytes); } + godot_error _get_partial_data(void* user, uint8_t* p_buffer, int p_bytes, int* r_received) { return ((godot::StreamPeerWasm*)user)->get_partial_data(p_buffer, p_bytes, *r_received); } + godot_error _put_data(void* user, const uint8_t* p_data, int p_bytes) { return ((godot::StreamPeerWasm*)user)->put_data(p_data, p_bytes); } + godot_error _put_partial_data(void* user, const uint8_t* p_data, int p_bytes, int* r_sent) { return ((godot::StreamPeerWasm*)user)->put_partial_data(p_data, p_bytes, *r_sent); } + int _get_available_bytes(const void* user) { return ((godot::StreamPeerWasm*)user)->get_available_bytes(); } + #endif } namespace godot { - void StreamPeerWasm::_register_methods() { - register_method("seek", &StreamPeerWasm::seek); - register_method("get_position", &StreamPeerWasm::get_position); + void StreamPeerWasm::REGISTRATION_METHOD() { + #ifdef GODOT_MODULE + ClassDB::bind_method(D_METHOD("seek", "p_pos"), &StreamPeerWasm::seek); + ClassDB::bind_method(D_METHOD("get_position"), &StreamPeerWasm::get_position); + #else + register_method("seek", &StreamPeerWasm::seek); + register_method("get_position", &StreamPeerWasm::get_position); + #endif } StreamPeerWasm::StreamPeerWasm() { - interface = { - GDNATIVE_VERSION, - this, - &_get_data, - &_get_partial_data, - &_put_data, - &_put_partial_data, - &_get_available_bytes, - NULL - }; + INTERFACE_DEFINE; pointer = 0; memory = NULL; } @@ -35,7 +35,7 @@ namespace godot { StreamPeerWasm::~StreamPeerWasm() { } void StreamPeerWasm::_init() { - net_api->godot_net_bind_stream_peer(_owner, &interface); + INTERFACE_INIT; } Ref StreamPeerWasm::seek(int p_pos) { @@ -50,33 +50,33 @@ namespace godot { } godot_error StreamPeerWasm::get_data(uint8_t *p_buffer, int p_bytes) { - FAIL_IF(memory == NULL, "Invalid stream peer memory", GDERROR(ERR_INVALID_DATA)); + FAIL_IF(memory == NULL, "Invalid stream peer memory", ERR_INVALID_DATA); byte_t* data = wasm_memory_data(memory) + pointer; memcpy(p_buffer, data, p_bytes); pointer += p_bytes; - return GDERROR(OK); + return OK; } - godot_error StreamPeerWasm::get_partial_data(uint8_t *p_buffer, int p_bytes, int *r_received) { - *r_received = p_bytes; + godot_error StreamPeerWasm::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) { + r_received = p_bytes; return get_data(p_buffer, p_bytes); } godot_error StreamPeerWasm::put_data(const uint8_t *p_data, int p_bytes) { - FAIL_IF(memory == NULL, "Invalid stream peer memory", GDERROR(ERR_INVALID_DATA)); - if (p_bytes <= 0) return GDERROR(OK); + FAIL_IF(memory == NULL, "Invalid stream peer memory", ERR_INVALID_DATA); + if (p_bytes <= 0) return OK; byte_t* data = wasm_memory_data(memory) + pointer; memcpy(data, p_data, p_bytes); pointer += p_bytes; - return GDERROR(OK); + return OK; } - godot_error StreamPeerWasm::put_partial_data(const uint8_t *p_data, int p_bytes, int *r_sent) { - *r_sent = p_bytes; + godot_error StreamPeerWasm::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { + r_sent = p_bytes; return put_data(p_data, p_bytes); } - int StreamPeerWasm::get_available_bytes() { + int StreamPeerWasm::get_available_bytes() const { return 0; // Not relevant } } diff --git a/src/stream-peer-wasm.h b/src/stream-peer-wasm.h index 4b042f9..09d9847 100644 --- a/src/stream-peer-wasm.h +++ b/src/stream-peer-wasm.h @@ -1,32 +1,41 @@ #ifndef STREAM_PEER_WASM_H #define STREAM_PEER_WASM_H -#include -#include #include "wasmer.h" #include "defs.h" +namespace { + #ifdef GODOT_MODULE + #define SUPER_CLASS StreamPeer + #define INTERFACE_DECLARE + #else + #define SUPER_CLASS StreamPeerGDNative + #define INTERFACE_DECLARE godot_net_stream_peer interface + #endif +} + namespace godot { - class StreamPeerWasm : public StreamPeerGDNative { - GODOT_CLASS(StreamPeerWasm, StreamPeerGDNative) + class StreamPeerWasm : public SUPER_CLASS { + GDCLASS(StreamPeerWasm, SUPER_CLASS); private: - godot_net_stream_peer interface; + INTERFACE_DECLARE; uint32_t pointer; public: - static void _register_methods(); + StreamPeerWasm* i; + static void REGISTRATION_METHOD(); StreamPeerWasm(); ~StreamPeerWasm(); void _init(); wasm_memory_t* memory; Ref seek(int p_pos); uint32_t get_position(); - virtual godot_error get_data(uint8_t *p_buffer, int p_bytes); - virtual godot_error get_partial_data(uint8_t *p_buffer, int p_bytes, int *r_received); - virtual godot_error put_data(const uint8_t *p_data, int p_bytes); - virtual godot_error put_partial_data(const uint8_t *p_data, int p_bytes, int *r_sent); - virtual int get_available_bytes(); + godot_error put_data(const uint8_t* p_data, int p_bytes); + godot_error put_partial_data(const uint8_t* p_data, int p_bytes, int& r_sent); + godot_error get_data(uint8_t* p_buffer, int p_bytes); + godot_error get_partial_data(uint8_t* p_buffer, int p_bytes, int& r_received); + int get_available_bytes() const; }; }