diff options
Diffstat (limited to 'modules')
251 files changed, 32765 insertions, 2716 deletions
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index 95d93e6af6..ae9daa802f 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -152,7 +152,7 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, */ //must avoid this later - while (f->get_pos() < 128) + while (f->get_position() < 128) f->get_8(); DDSFormat dds_format; diff --git a/modules/enet/SCsub b/modules/enet/SCsub index 42a933a66d..4790c5099f 100644 --- a/modules/enet/SCsub +++ b/modules/enet/SCsub @@ -7,7 +7,7 @@ Import('env_modules') env_enet = env_modules.Clone() -if (env['builtin_enet'] != 'no'): +if env['builtin_enet']: thirdparty_dir = "#thirdparty/enet/" thirdparty_sources = [ "godot.cpp", diff --git a/modules/etc/SCsub b/modules/etc/SCsub index 8f5937017e..9c3e703f11 100644 --- a/modules/etc/SCsub +++ b/modules/etc/SCsub @@ -34,4 +34,8 @@ env_etc.Append(CPPPATH=[thirdparty_dir]) env_etc.add_source_files(env.modules_sources, "*.cpp") # upstream uses c++11 -env_etc.Append(CXXFLAGS="-std=gnu++11") +env_etc.Append(CCFLAGS="-std=gnu++11") +# -ffast-math seems to be incompatible with ec2comp on recent versions of +# GCC and Clang +if '-ffast-math' in env_etc['CCFLAGS']: + env_etc['CCFLAGS'].remove('-ffast-math') diff --git a/modules/etc/config.py b/modules/etc/config.py index 4b0b01b78e..7dc2cb59c1 100644 --- a/modules/etc/config.py +++ b/modules/etc/config.py @@ -6,6 +6,6 @@ def can_build(platform): def configure(env): # Tools only, disabled for non-tools # TODO: Find a cleaner way to achieve that - if (env["tools"] == "no"): - env["module_etc_enabled"] = "no" + if not env['tools']: + env['module_etc_enabled'] = False env.disabled_modules.append("etc") diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp index 9a15beb6eb..dc7d23bbd7 100644 --- a/modules/etc/image_etc.cpp +++ b/modules/etc/image_etc.cpp @@ -37,7 +37,6 @@ static Image::Format _get_etc2_mode(Image::DetectChannels format) { switch (format) { - case Image::DETECTED_L: case Image::DETECTED_R: return Image::FORMAT_ETC2_R11; @@ -47,7 +46,7 @@ static Image::Format _get_etc2_mode(Image::DetectChannels format) { case Image::DETECTED_RGB: return Image::FORMAT_ETC2_RGB8; - case Image::DETECTED_RGBA: + default: return Image::FORMAT_ETC2_RGBA8; // TODO: would be nice if we could use FORMAT_ETC2_RGB8A1 for FORMAT_RGBA5551 @@ -117,7 +116,7 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f return; } - int imgw = p_img->get_width(), imgh = p_img->get_height(); + uint32_t imgw = p_img->get_width(), imgh = p_img->get_height(); ERR_FAIL_COND(next_power_of_2(imgw) != imgw || next_power_of_2(imgh) != imgh); Image::Format etc_format = force_etc1_format ? Image::FORMAT_ETC : _get_etc2_mode(detected_channels); diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub index f22df4407c..19e384af73 100644 --- a/modules/freetype/SCsub +++ b/modules/freetype/SCsub @@ -6,7 +6,7 @@ from compat import isbasestring # Not building in a separate env as scene needs it # Thirdparty source files -if (env['builtin_freetype'] != 'no'): +if env['builtin_freetype']: thirdparty_dir = "#thirdparty/freetype/" thirdparty_sources = [ "src/autofit/autofit.c", @@ -65,7 +65,7 @@ if (env['builtin_freetype'] != 'no'): env.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include"]) # also requires libpng headers - if (env['builtin_libpng'] != 'no'): + if env['builtin_libpng']: env.Append(CPPPATH=["#thirdparty/libpng"]) lib = env.Library("freetype_builtin", thirdparty_sources) diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index 65970d48c1..a6ae143947 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -2,12 +2,157 @@ Import('env') -env.add_source_files(env.modules_sources, "*.cpp") -env.add_source_files(env.modules_sources, "godot/*.cpp") +gdn_env = env.Clone() -env.Append(CPPFLAGS=['-DGDAPI_BUILT_IN']) -env.Append(CPPPATH=['#modules/gdnative/']) +gdn_env.add_source_files(env.modules_sources, "gd_native_library_editor.cpp") +gdn_env.add_source_files(env.modules_sources, "gdnative.cpp") +gdn_env.add_source_files(env.modules_sources, "register_types.cpp") +gdn_env.add_source_files(env.modules_sources, "gdnative/*.cpp") +gdn_env.add_source_files(env.modules_sources, "nativescript/*.cpp") + +gdn_env.Append(CPPPATH=['#modules/gdnative/include/']) + +SConscript("nativearvr/SCsub") +SConscript("pluginscript/SCsub") + +def _spaced(e): + return e if e[-1] == '*' else e + ' ' + +def _build_gdnative_api_struct_header(api): + out = [ + '/* THIS FILE IS GENERATED DO NOT EDIT */', + '#ifndef GODOT_GDNATIVE_API_STRUCT_H', + '#define GODOT_GDNATIVE_API_STRUCT_H', + '', + '#include <gdnative/gdnative.h>', + '#include <nativearvr/godot_nativearvr.h>', + '#include <nativescript/godot_nativescript.h>', + '#include <pluginscript/godot_pluginscript.h>', + '', + '#define GDNATIVE_API_INIT(options) do { extern const godot_gdnative_api_struct *_gdnative_wrapper_api_struct; _gdnative_wrapper_api_struct = options->api_struct; } while (0)', + '', + '#ifdef __cplusplus', + 'extern "C" {', + '#endif', + '', + 'typedef struct godot_gdnative_api_struct {', + '\tvoid *next;', + '\tconst char *version;', + ] + + for funcdef in api['api']: + args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']]) + out.append('\t%s(*%s)(%s);' % (_spaced(funcdef['return_type']), funcdef['name'], args)) + + out += [ + '} godot_gdnative_api_struct;', + '', + '#ifdef __cplusplus', + '}', + '#endif', + '', + '#endif // GODOT_GDNATIVE_API_STRUCT_H', + '' + ] + return '\n'.join(out) + +def _build_gdnative_api_struct_source(api): + out = [ + '/* THIS FILE IS GENERATED DO NOT EDIT */', + '', + '#include <gdnative_api_struct.gen.h>', + '', + 'const char *_gdnative_api_version = "%s";' % api['version'], + 'extern const godot_gdnative_api_struct api_struct = {', + '\tNULL,', + '\t_gdnative_api_version,', + ] + + for funcdef in api['api']: + out.append('\t%s,' % funcdef['name']) + out.append('};\n') + + return '\n'.join(out) + +def build_gdnative_api_struct(target, source, env): + import json + from collections import OrderedDict + + with open(source[0].path, 'r') as fd: + api = json.load(fd) + + header, source = target + with open(header.path, 'w') as fd: + fd.write(_build_gdnative_api_struct_header(api)) + with open(source.path, 'w') as fd: + fd.write(_build_gdnative_api_struct_source(api)) + +_, gensource = gdn_env.Command(['include/gdnative_api_struct.gen.h', 'gdnative_api_struct.gen.cpp'], + 'gdnative_api.json', build_gdnative_api_struct) +gdn_env.add_source_files(env.modules_sources, [gensource]) -if "platform" in env and env["platform"] == "x11": # there has to be a better solution? - env.Append(LINKFLAGS=["-rdynamic"]) env.use_ptrcall = True + + +def _build_gdnative_wrapper_code(api): + out = [ + '/* THIS FILE IS GENERATED DO NOT EDIT */', + '', + '#include <gdnative/gdnative.h>', + '#include <nativescript/godot_nativescript.h>', + '#include <pluginscript/godot_pluginscript.h>', + '', + '#include <gdnative_api_struct.gen.h>', + '', + 'godot_gdnative_api_struct *_gdnative_wrapper_api_struct = 0;', + '', + '#ifdef __cplusplus', + 'extern "C" {', + '#endif', + '' + ] + + for funcdef in api['api']: + args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']]) + out.append('%s%s(%s) {' % (_spaced(funcdef['return_type']), funcdef['name'], args)) + + args = ', '.join(['%s' % n for t, n in funcdef['arguments']]) + + return_line = '\treturn ' if funcdef['return_type'] != 'void' else '\t' + return_line += '_gdnative_wrapper_api_struct->' + funcdef['name'] + '(' + args + ');' + + out.append(return_line) + out.append('}') + out.append('') + + out += [ + '#ifdef __cplusplus', + '}', + '#endif' + ] + + return '\n'.join(out) + + +def build_gdnative_wrapper_code(target, source, env): + import json + with open(source[0].path, 'r') as fd: + api = json.load(fd) + + wrapper_file = target[0] + with open(wrapper_file.path, 'w') as fd: + fd.write(_build_gdnative_wrapper_code(api)) + + + +if ARGUMENTS.get('gdnative_wrapper', False): + #build wrapper code + gensource, = gdn_env.Command('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', build_gdnative_wrapper_code) + + gd_wrapper_env = env.Clone() + gd_wrapper_env.Append(CPPPATH=['#modules/gdnative/include/']) + + # I think this doesn't work on MSVC yet... + gd_wrapper_env.Append(CCFLAGS=['-fPIC']) + + gd_wrapper_env.Library("#bin/gdnative_wrapper_code", [gensource]) diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py index 4f89ca0d4c..df3556249d 100644 --- a/modules/gdnative/config.py +++ b/modules/gdnative/config.py @@ -1,8 +1,12 @@ - def can_build(platform): - return False - + return True def configure(env): env.use_ptrcall = True + +def get_doc_classes(): + return ["GDNative", "GDNativeLibrary", "NativeScript", "ARVRInterfaceGDNative"] + +def get_doc_path(): + return "doc_classes" diff --git a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml new file mode 100644 index 0000000000..308a7d5946 --- /dev/null +++ b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="ARVRInterfaceGDNative" inherits="ARVRInterface" category="Core" version="3.0.alpha.custom_build"> + <brief_description> + GDNative wrapper for an ARVR interface + </brief_description> + <description> + This is a wrapper class for GDNative implementations of the ARVR interface. To use a GDNative ARVR interface simply instantiate this object and set your GDNative library containing the ARVR interface implementation. + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/modules/gdnative/doc_classes/GDNative.xml b/modules/gdnative/doc_classes/GDNative.xml new file mode 100644 index 0000000000..83a1cf06f0 --- /dev/null +++ b/modules/gdnative/doc_classes/GDNative.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="GDNative" inherits="Reference" category="Core" version="3.0.alpha.custom_build"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="call_native"> + <return type="Variant"> + </return> + <argument index="0" name="calling_type" type="String"> + </argument> + <argument index="1" name="procedure_name" type="String"> + </argument> + <argument index="2" name="arguments" type="Array"> + </argument> + <description> + </description> + </method> + <method name="get_library"> + <return type="GDNativeLibrary"> + </return> + <description> + </description> + </method> + <method name="initialize"> + <return type="bool"> + </return> + <description> + </description> + </method> + <method name="set_library"> + <return type="void"> + </return> + <argument index="0" name="library" type="GDNativeLibrary"> + </argument> + <description> + </description> + </method> + <method name="terminate"> + <return type="bool"> + </return> + <description> + </description> + </method> + </methods> + <members> + <member name="library" type="GDNativeLibrary" setter="set_library" getter="get_library"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml new file mode 100644 index 0000000000..361c89e6b3 --- /dev/null +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="GDNativeLibrary" inherits="Resource" category="Core" version="3.0.alpha.custom_build"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="get_active_library_path" qualifiers="const"> + <return type="String"> + </return> + <description> + </description> + </method> + <method name="get_library_path" qualifiers="const"> + <return type="String"> + </return> + <argument index="0" name="platform" type="String"> + </argument> + <description> + </description> + </method> + <method name="is_singleton_gdnative" qualifiers="const"> + <return type="bool"> + </return> + <description> + </description> + </method> + <method name="set_library_path"> + <return type="void"> + </return> + <argument index="0" name="platform" type="String"> + </argument> + <argument index="1" name="path" type="String"> + </argument> + <description> + </description> + </method> + <method name="set_singleton_gdnative"> + <return type="void"> + </return> + <argument index="0" name="singleton" type="bool"> + </argument> + <description> + </description> + </method> + </methods> + <members> + <member name="singleton_gdnative" type="bool" setter="set_singleton_gdnative" getter="is_singleton_gdnative"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml new file mode 100644 index 0000000000..b040cfd966 --- /dev/null +++ b/modules/gdnative/doc_classes/NativeScript.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="NativeScript" inherits="Script" category="Core" version="3.0.alpha.custom_build"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="get_class_name" qualifiers="const"> + <return type="String"> + </return> + <description> + </description> + </method> + <method name="get_library" qualifiers="const"> + <return type="GDNativeLibrary"> + </return> + <description> + </description> + </method> + <method name="new" qualifiers="vararg"> + <return type="Object"> + </return> + <description> + </description> + </method> + <method name="set_class_name"> + <return type="void"> + </return> + <argument index="0" name="class_name" type="String"> + </argument> + <description> + </description> + </method> + <method name="set_library"> + <return type="void"> + </return> + <argument index="0" name="library" type="GDNativeLibrary"> + </argument> + <description> + </description> + </method> + </methods> + <members> + <member name="class_name" type="String" setter="set_class_name" getter="get_class_name"> + </member> + <member name="library" type="GDNativeLibrary" setter="set_library" getter="get_library"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/modules/gdnative/gd_native_library_editor.cpp b/modules/gdnative/gd_native_library_editor.cpp new file mode 100644 index 0000000000..c37b7f473d --- /dev/null +++ b/modules/gdnative/gd_native_library_editor.cpp @@ -0,0 +1,148 @@ +/*************************************************************************/ +/* gd_native_library_editor.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 TOOLS_ENABLED +#include "gd_native_library_editor.h" + +#include "gdnative.h" + +void GDNativeLibraryEditor::_find_gdnative_singletons(EditorFileSystemDirectory *p_dir, const Set<String> &enabled_list) { + + // check children + + for (int i = 0; i < p_dir->get_file_count(); i++) { + String file_type = p_dir->get_file_type(i); + + if (file_type != "GDNativeLibrary") { + continue; + } + + Ref<GDNativeLibrary> lib = ResourceLoader::load(p_dir->get_file_path(i)); + if (lib.is_valid() && lib->is_singleton_gdnative()) { + String path = p_dir->get_file_path(i); + TreeItem *ti = libraries->create_item(libraries->get_root()); + ti->set_text(0, path.get_file()); + ti->set_tooltip(0, path); + ti->set_metadata(0, path); + ti->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); + ti->set_text(1, "Disabled,Enabled"); + bool enabled = enabled_list.has(path) ? true : false; + + ti->set_range(1, enabled ? 1 : 0); + ti->set_custom_color(1, enabled ? Color(0, 1, 0) : Color(1, 0, 0)); + } + } + + // check subdirectories + for (int i = 0; i < p_dir->get_subdir_count(); i++) { + _find_gdnative_singletons(p_dir->get_subdir(i), enabled_list); + } +} + +void GDNativeLibraryEditor::_update_libraries() { + + updating = true; + libraries->clear(); + libraries->create_item(); //rppt + + Vector<String> enabled_paths; + if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) { + enabled_paths = ProjectSettings::get_singleton()->get("gdnative/singletons"); + } + Set<String> enabled_list; + for (int i = 0; i < enabled_paths.size(); i++) { + enabled_list.insert(enabled_paths[i]); + } + + EditorFileSystemDirectory *fs = EditorFileSystem::get_singleton()->get_filesystem(); + if (fs) { + _find_gdnative_singletons(fs, enabled_list); + } + + updating = false; +} + +void GDNativeLibraryEditor::_item_edited() { + if (updating) + return; + + TreeItem *item = libraries->get_edited(); + if (!item) + return; + + bool enabled = item->get_range(1); + String path = item->get_metadata(0); + + Vector<String> enabled_paths; + if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) { + enabled_paths = ProjectSettings::get_singleton()->get("gdnative/singletons"); + } + + if (enabled) { + if (enabled_paths.find(path) == -1) { + enabled_paths.push_back(path); + } + } else { + enabled_paths.erase(path); + } + + if (enabled_paths.size()) { + ProjectSettings::get_singleton()->set("gdnative/singletons", enabled_paths); + } else { + ProjectSettings::get_singleton()->set("gdnative/singletons", Variant()); + } +} + +void GDNativeLibraryEditor::_notification(int p_what) { + + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + if (is_visible_in_tree()) { + _update_libraries(); + } + } +} + +void GDNativeLibraryEditor::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_item_edited"), &GDNativeLibraryEditor::_item_edited); +} + +GDNativeLibraryEditor::GDNativeLibraryEditor() { + libraries = memnew(Tree); + libraries->set_columns(2); + libraries->set_column_titles_visible(true); + libraries->set_column_title(0, TTR("Library")); + libraries->set_column_title(1, TTR("Status")); + libraries->set_hide_root(true); + add_margin_child(TTR("Libraries: "), libraries, true); + updating = false; + libraries->connect("item_edited", this, "_item_edited"); +} + +#endif // TOOLS_ENABLED diff --git a/modules/gdnative/gd_native_library_editor.h b/modules/gdnative/gd_native_library_editor.h new file mode 100644 index 0000000000..a11c4620dd --- /dev/null +++ b/modules/gdnative/gd_native_library_editor.h @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* gd_native_library_editor.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GD_NATIVE_LIBRARY_EDITOR_H +#define GD_NATIVE_LIBRARY_EDITOR_H + +#ifdef TOOLS_ENABLED +#include "editor/editor_file_system.h" +#include "editor/project_settings_editor.h" + +class GDNativeLibraryEditor : public VBoxContainer { + Tree *libraries; + + bool updating; + void _update_libraries(); + + void _find_gdnative_singletons(EditorFileSystemDirectory *p_dir, const Set<String> &enabled_list); + void _item_edited(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + GDNativeLibraryEditor(); +}; + +#endif +#endif // GD_NATIVE_LIBRARY_EDITOR_H diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index 7faf21c5a1..3fc04a5498 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -40,7 +40,10 @@ const String init_symbol = "godot_gdnative_init"; const String terminate_symbol = "godot_gdnative_terminate"; -String GDNativeLibrary::platform_names[NUM_PLATFORMS] = { +// Defined in gdnative_api_struct.gen.cpp +extern const godot_gdnative_api_struct api_struct; + +String GDNativeLibrary::platform_names[NUM_PLATFORMS + 1] = { "X11_32bit", "X11_64bit", "Windows_32bit", @@ -48,11 +51,15 @@ String GDNativeLibrary::platform_names[NUM_PLATFORMS] = { "OSX", "Android", - "iOS", - "WebAssembly" + "iOS_32bit", + "iOS_64bit", + + "WebAssembly", + + "" }; -String GDNativeLibrary::platform_lib_ext[NUM_PLATFORMS] = { +String GDNativeLibrary::platform_lib_ext[NUM_PLATFORMS + 1] = { "so", "so", "dll", @@ -60,25 +67,34 @@ String GDNativeLibrary::platform_lib_ext[NUM_PLATFORMS] = { "dylib", "so", + + "dylib", "dylib", - "wasm" + "wasm", + + "" }; -// TODO(karroffel): make this actually do the right thing. GDNativeLibrary::Platform GDNativeLibrary::current_platform = #if defined(X11_ENABLED) - X11_64BIT; + (sizeof(void *) == 8 ? X11_64BIT : X11_32BIT); #elif defined(WINDOWS_ENABLED) - WINDOWS_64BIT; + (sizeof(void *) == 8 ? WINDOWS_64BIT : WINDOWS_32BIT); #elif defined(OSX_ENABLED) OSX; +#elif defined(IPHONE_ENABLED) + (sizeof(void *) == 8 ? IOS_64BIT : IOS_32BIT); +#elif defined(ANDROID_ENABLED) + ANDROID; +#elif defined(JAVASCRIPT_ENABLED) + WASM; #else - X11_64BIT; // need a sensible default.. + NUM_PLATFORMS; #endif GDNativeLibrary::GDNativeLibrary() - : library_paths() { + : library_paths(), singleton_gdnative(false) { } GDNativeLibrary::~GDNativeLibrary() { @@ -87,6 +103,12 @@ GDNativeLibrary::~GDNativeLibrary() { void GDNativeLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("set_library_path", "platform", "path"), &GDNativeLibrary::set_library_path); ClassDB::bind_method(D_METHOD("get_library_path", "platform"), &GDNativeLibrary::get_library_path); + ClassDB::bind_method(D_METHOD("get_active_library_path"), &GDNativeLibrary::get_active_library_path); + + ClassDB::bind_method(D_METHOD("is_singleton_gdnative"), &GDNativeLibrary::is_singleton_gdnative); + ClassDB::bind_method(D_METHOD("set_singleton_gdnative", "singleton"), &GDNativeLibrary::set_singleton_gdnative); + + ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "singleton_gdnative"), "set_singleton_gdnative", "is_singleton_gdnative"); } bool GDNativeLibrary::_set(const StringName &p_name, const Variant &p_value) { @@ -151,7 +173,10 @@ String GDNativeLibrary::get_library_path(StringName p_platform) const { } String GDNativeLibrary::get_active_library_path() const { - return library_paths[GDNativeLibrary::current_platform]; + if (GDNativeLibrary::current_platform != NUM_PLATFORMS) { + return library_paths[GDNativeLibrary::current_platform]; + } + return ""; } GDNative::GDNative() { @@ -159,7 +184,6 @@ GDNative::GDNative() { } GDNative::~GDNative() { - // TODO(karroffel): implement ALL the things! } extern "C" void _api_anchor(); @@ -175,10 +199,7 @@ void GDNative::_bind_methods() { ClassDB::bind_method(D_METHOD("initialize"), &GDNative::initialize); ClassDB::bind_method(D_METHOD("terminate"), &GDNative::terminate); - // TODO(karroffel): get_native_(raw_)call_types binding? - - // TODO(karroffel): make this a varargs function? - ClassDB::bind_method(D_METHOD("call_native", "procedure_name", "arguments"), &GDNative::call_native); + ClassDB::bind_method(D_METHOD("call_native", "calling_type", "procedure_name", "arguments"), &GDNative::call_native); ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "GDNativeLibrary"), "set_library", "get_library"); } @@ -204,18 +225,18 @@ bool GDNative::initialize() { ERR_PRINT("No library set for this platform"); return false; } - +#ifdef IPHONE_ENABLED + String path = lib_path.replace("res://", "dylibs/"); +#else String path = ProjectSettings::get_singleton()->globalize_path(lib_path); +#endif Error err = OS::get_singleton()->open_dynamic_library(path, native_handle); if (err != OK) { return false; } void *library_init; - err = OS::get_singleton()->get_dynamic_library_symbol_handle( - native_handle, - init_symbol, - library_init); + err = get_symbol(init_symbol, library_init); if (err || !library_init) { OS::get_singleton()->close_dynamic_library(native_handle); @@ -229,11 +250,13 @@ bool GDNative::initialize() { godot_gdnative_init_options options; + options.api_struct = &api_struct; options.in_editor = Engine::get_singleton()->is_editor_hint(); options.core_api_hash = ClassDB::get_api_hash(ClassDB::API_CORE); options.editor_api_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR); options.no_api_hash = ClassDB::get_api_hash(ClassDB::API_NONE); options.gd_native_library = (godot_object *)(get_library().ptr()); + options.active_library_path = (godot_string *)&path; library_init_fpointer(&options); @@ -248,11 +271,8 @@ bool GDNative::terminate() { } void *library_terminate; - Error error = OS::get_singleton()->get_dynamic_library_symbol_handle( - native_handle, - terminate_symbol, - library_terminate); - if (error) { + Error error = get_symbol(terminate_symbol, library_terminate); + if (error || !library_terminate) { OS::get_singleton()->close_dynamic_library(native_handle); native_handle = NULL; return true; @@ -284,10 +304,6 @@ void GDNativeCallRegistry::register_native_call_type(StringName p_call_type, nat native_calls.insert(p_call_type, p_callback); } -void GDNativeCallRegistry::register_native_raw_call_type(StringName p_raw_call_type, native_raw_call_cb p_callback) { - native_raw_calls.insert(p_raw_call_type, p_callback); -} - Vector<StringName> GDNativeCallRegistry::get_native_call_types() { Vector<StringName> call_types; call_types.resize(native_calls.size()); @@ -300,18 +316,6 @@ Vector<StringName> GDNativeCallRegistry::get_native_call_types() { return call_types; } -Vector<StringName> GDNativeCallRegistry::get_native_raw_call_types() { - Vector<StringName> call_types; - call_types.resize(native_raw_calls.size()); - - size_t idx = 0; - for (Map<StringName, native_raw_call_cb>::Element *E = native_raw_calls.front(); E; E = E->next(), idx++) { - call_types[idx] = E->key(); - } - - return call_types; -} - Variant GDNative::call_native(StringName p_native_call_type, StringName p_procedure_name, Array p_arguments) { Map<StringName, native_call_cb>::Element *E = GDNativeCallRegistry::singleton->native_calls.find(p_native_call_type); @@ -320,20 +324,34 @@ Variant GDNative::call_native(StringName p_native_call_type, StringName p_proced return Variant(); } - String procedure_name = p_procedure_name; - godot_variant result = E->get()(native_handle, (godot_string *)&procedure_name, (godot_array *)&p_arguments); + void *procedure_handle; + + Error err = OS::get_singleton()->get_dynamic_library_symbol_handle( + native_handle, + p_procedure_name, + procedure_handle); + + if (err != OK || procedure_handle == NULL) { + return Variant(); + } + + godot_variant result = E->get()(procedure_handle, (godot_array *)&p_arguments); return *(Variant *)&result; } -void GDNative::call_native_raw(StringName p_raw_call_type, StringName p_procedure_name, void *data, int num_args, void **args, void *r_return) { +Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle) { - Map<StringName, native_raw_call_cb>::Element *E = GDNativeCallRegistry::singleton->native_raw_calls.find(p_raw_call_type); - if (!E) { - ERR_PRINT((String("No handler for native raw call type \"" + p_raw_call_type) + "\" found").utf8().get_data()); - return; + if (native_handle == NULL) { + ERR_PRINT("No valid library handle, can't get symbol from GDNative object"); + return ERR_CANT_OPEN; } - String procedure_name = p_procedure_name; - E->get()(native_handle, (godot_string *)&procedure_name, data, num_args, args, r_return); + Error result = OS::get_singleton()->get_dynamic_library_symbol_handle( + native_handle, + p_procedure_name, + r_handle, + true); + + return result; } diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h index bec746a441..e44cc55a79 100644 --- a/modules/gdnative/gdnative.h +++ b/modules/gdnative/gdnative.h @@ -35,7 +35,8 @@ #include "os/thread_safe.h" #include "resource.h" -#include <godot/gdnative.h> +#include "gdnative/gdnative.h" +#include "gdnative_api_struct.gen.h" class GDNativeLibrary : public Resource { GDCLASS(GDNativeLibrary, Resource) @@ -48,11 +49,17 @@ class GDNativeLibrary : public Resource { // NOTE(karroffel): I heard OSX 32 bit is dead, so 64 only OSX, - // TODO(karroffel): all different android versions and archs + // Android .so files must be located in directories corresponding to Android ABI names: + // https://developer.android.com/ndk/guides/abis.html + // Android runtime will select the matching library depending on the device. + // The value here must simply point to the .so name, for example: + // "res://libmy_gdnative.so" or "libmy_gdnative.so", + // while in the project the actual paths can be "lib/android/armeabi-v7a/libmy_gdnative.so", + // "lib/android/arm64-v8a/libmy_gdnative.so". ANDROID, - // TODO(karroffe): all different iOS versions and archs - IOS, + IOS_32BIT, + IOS_64BIT, // TODO(karroffel): figure out how to deal with web stuff at all... WASM, @@ -64,14 +71,15 @@ class GDNativeLibrary : public Resource { }; - static String platform_names[NUM_PLATFORMS]; - static String platform_lib_ext[NUM_PLATFORMS]; + static String platform_names[NUM_PLATFORMS + 1]; + static String platform_lib_ext[NUM_PLATFORMS + 1]; - // TODO(karroffel): make this actually do something lol. static Platform current_platform; String library_paths[NUM_PLATFORMS]; + bool singleton_gdnative; + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -87,10 +95,12 @@ public: String get_library_path(StringName p_platform) const; String get_active_library_path() const; + + _FORCE_INLINE_ bool is_singleton_gdnative() const { return singleton_gdnative; } + _FORCE_INLINE_ void set_singleton_gdnative(bool p_singleton) { singleton_gdnative = p_singleton; } }; -typedef godot_variant (*native_call_cb)(void *, godot_string *, godot_array *); -typedef void (*native_raw_call_cb)(void *, godot_string *, void *, int, void **, void *); +typedef godot_variant (*native_call_cb)(void *, godot_array *); struct GDNativeCallRegistry { static GDNativeCallRegistry *singleton; @@ -100,17 +110,13 @@ struct GDNativeCallRegistry { } inline GDNativeCallRegistry() - : native_calls(), - native_raw_calls() {} + : native_calls() {} Map<StringName, native_call_cb> native_calls; - Map<StringName, native_raw_call_cb> native_raw_calls; void register_native_call_type(StringName p_call_type, native_call_cb p_callback); - void register_native_raw_call_type(StringName p_raw_call_type, native_raw_call_cb p_callback); Vector<StringName> get_native_call_types(); - Vector<StringName> get_native_raw_call_types(); }; class GDNative : public Reference { @@ -138,7 +144,8 @@ public: bool terminate(); Variant call_native(StringName p_native_call_type, StringName p_procedure_name, Array p_arguments = Array()); - void call_native_raw(StringName p_raw_call_type, StringName p_procedure_name, void *data, int num_args, void **args, void *r_return); + + Error get_symbol(StringName p_procedure_name, void *&r_handle); }; #endif // GDNATIVE_H diff --git a/modules/gdnative/godot/array.cpp b/modules/gdnative/gdnative/array.cpp index c15ba30ca2..51c023981f 100644 --- a/modules/gdnative/godot/array.cpp +++ b/modules/gdnative/gdnative/array.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/array.h> +#include "gdnative/array.h" #include "core/array.h" #include "core/os/memory.h" @@ -61,7 +61,7 @@ void GDAPI godot_array_new_pool_color_array(godot_array *r_dest, const godot_poo memnew_placement(dest, Array); dest->resize(pca->size()); - for (size_t i = 0; i < dest->size(); i++) { + for (int i = 0; i < dest->size(); i++) { Variant v = pca->operator[](i); dest->operator[](i) = v; } @@ -73,7 +73,7 @@ void GDAPI godot_array_new_pool_vector3_array(godot_array *r_dest, const godot_p memnew_placement(dest, Array); dest->resize(pca->size()); - for (size_t i = 0; i < dest->size(); i++) { + for (int i = 0; i < dest->size(); i++) { Variant v = pca->operator[](i); dest->operator[](i) = v; } @@ -85,7 +85,7 @@ void GDAPI godot_array_new_pool_vector2_array(godot_array *r_dest, const godot_p memnew_placement(dest, Array); dest->resize(pca->size()); - for (size_t i = 0; i < dest->size(); i++) { + for (int i = 0; i < dest->size(); i++) { Variant v = pca->operator[](i); dest->operator[](i) = v; } @@ -97,7 +97,7 @@ void GDAPI godot_array_new_pool_string_array(godot_array *r_dest, const godot_po memnew_placement(dest, Array); dest->resize(pca->size()); - for (size_t i = 0; i < dest->size(); i++) { + for (int i = 0; i < dest->size(); i++) { Variant v = pca->operator[](i); dest->operator[](i) = v; } @@ -109,7 +109,7 @@ void GDAPI godot_array_new_pool_real_array(godot_array *r_dest, const godot_pool memnew_placement(dest, Array); dest->resize(pca->size()); - for (size_t i = 0; i < dest->size(); i++) { + for (int i = 0; i < dest->size(); i++) { Variant v = pca->operator[](i); dest->operator[](i) = v; } @@ -121,7 +121,7 @@ void GDAPI godot_array_new_pool_int_array(godot_array *r_dest, const godot_pool_ memnew_placement(dest, Array); dest->resize(pca->size()); - for (size_t i = 0; i < dest->size(); i++) { + for (int i = 0; i < dest->size(); i++) { Variant v = pca->operator[](i); dest->operator[](i) = v; } @@ -133,7 +133,7 @@ void GDAPI godot_array_new_pool_byte_array(godot_array *r_dest, const godot_pool memnew_placement(dest, Array); dest->resize(pca->size()); - for (size_t i = 0; i < dest->size(); i++) { + for (int i = 0; i < dest->size(); i++) { Variant v = pca->operator[](i); dest->operator[](i) = v; } diff --git a/modules/gdnative/godot/basis.cpp b/modules/gdnative/gdnative/basis.cpp index 8433355c12..b1327cdaef 100644 --- a/modules/gdnative/godot/basis.cpp +++ b/modules/gdnative/gdnative/basis.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/basis.h> +#include "gdnative/basis.h" #include "core/math/matrix3.h" #include "core/variant.h" @@ -107,24 +107,6 @@ godot_basis GDAPI godot_basis_scaled(const godot_basis *p_self, const godot_vect return dest; } -void GDAPI godot_basis_set_scale(godot_basis *p_self, const godot_vector3 *p_scale) { - Basis *self = (Basis *)p_self; - const Vector3 *scale = (const Vector3 *)p_scale; - self->set_scale(*scale); -} - -void GDAPI godot_basis_set_rotation_euler(godot_basis *p_self, const godot_vector3 *p_euler) { - Basis *self = (Basis *)p_self; - const Vector3 *euler = (const Vector3 *)p_euler; - self->set_rotation_euler(*euler); -} - -void GDAPI godot_basis_set_rotation_axis_angle(godot_basis *p_self, const godot_vector3 *p_axis, const godot_real p_angle) { - Basis *self = (Basis *)p_self; - const Vector3 *axis = (const Vector3 *)p_axis; - self->set_rotation_axis_angle(*axis, p_angle); -} - godot_vector3 GDAPI godot_basis_get_scale(const godot_basis *p_self) { godot_vector3 dest; const Basis *self = (const Basis *)p_self; diff --git a/modules/gdnative/godot/color.cpp b/modules/gdnative/gdnative/color.cpp index 3677fdc265..2a5c0887a1 100644 --- a/modules/gdnative/godot/color.cpp +++ b/modules/gdnative/gdnative/color.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/color.h> +#include "gdnative/color.h" #include "core/color.h" #include "core/variant.h" @@ -112,14 +112,14 @@ godot_string GDAPI godot_color_as_string(const godot_color *p_self) { return ret; } -godot_int GDAPI godot_color_to_32(const godot_color *p_self) { +godot_int GDAPI godot_color_to_rgba32(const godot_color *p_self) { const Color *self = (const Color *)p_self; - return self->to_32(); + return self->to_rgba32(); } -godot_int GDAPI godot_color_to_ARGB32(const godot_color *p_self) { +godot_int GDAPI godot_color_to_argb32(const godot_color *p_self) { const Color *self = (const Color *)p_self; - return self->to_ARGB32(); + return self->to_argb32(); } godot_real GDAPI godot_color_gray(const godot_color *p_self) { diff --git a/modules/gdnative/godot/dictionary.cpp b/modules/gdnative/gdnative/dictionary.cpp index 2996cc78a3..ed98cdbb00 100644 --- a/modules/gdnative/godot/dictionary.cpp +++ b/modules/gdnative/gdnative/dictionary.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/dictionary.h> +#include "gdnative/dictionary.h" #include "core/variant.h" // core/variant.h before to avoid compile errors with MSVC diff --git a/modules/gdnative/godot/gdnative.cpp b/modules/gdnative/gdnative/gdnative.cpp index 7cd52da34d..64a7c33cf8 100644 --- a/modules/gdnative/godot/gdnative.cpp +++ b/modules/gdnative/gdnative/gdnative.cpp @@ -27,11 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/gdnative.h> +#include "gdnative/gdnative.h" #include "class_db.h" #include "error_macros.h" -#include "gdnative.h" #include "global_constants.h" #include "os/os.h" #include "project_settings.h" @@ -42,6 +41,7 @@ extern "C" { #endif extern "C" void _string_api_anchor(); +extern "C" void _string_name_api_anchor(); extern "C" void _vector2_api_anchor(); extern "C" void _rect2_api_anchor(); extern "C" void _vector3_api_anchor(); @@ -62,6 +62,7 @@ extern "C" void _variant_api_anchor(); void _api_anchor() { _string_api_anchor(); + _string_name_api_anchor(); _vector2_api_anchor(); _rect2_api_anchor(); _vector3_api_anchor(); diff --git a/modules/gdnative/godot/node_path.cpp b/modules/gdnative/gdnative/node_path.cpp index 2309588a81..50fade5b94 100644 --- a/modules/gdnative/godot/node_path.cpp +++ b/modules/gdnative/gdnative/node_path.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/node_path.h> +#include "gdnative/node_path.h" #include "core/node_path.h" #include "core/variant.h" diff --git a/modules/gdnative/godot/plane.cpp b/modules/gdnative/gdnative/plane.cpp index f3d4b6971e..a5e05ffa6b 100644 --- a/modules/gdnative/godot/plane.cpp +++ b/modules/gdnative/gdnative/plane.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/plane.h> +#include "gdnative/plane.h" #include "core/math/plane.h" #include "core/variant.h" diff --git a/modules/gdnative/godot/pool_arrays.cpp b/modules/gdnative/gdnative/pool_arrays.cpp index 2e533077f6..1393374da2 100644 --- a/modules/gdnative/godot/pool_arrays.cpp +++ b/modules/gdnative/gdnative/pool_arrays.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/pool_arrays.h> +#include "gdnative/pool_arrays.h" #include "array.h" #include "core/variant.h" @@ -65,7 +65,7 @@ void GDAPI godot_pool_byte_array_new_with_array(godot_pool_byte_array *r_dest, c memnew_placement(dest, PoolVector<uint8_t>); dest->resize(a->size()); - for (size_t i = 0; i < a->size(); i++) { + for (int i = 0; i < a->size(); i++) { dest->set(i, (*a)[i]); } } @@ -144,7 +144,7 @@ void GDAPI godot_pool_int_array_new_with_array(godot_pool_int_array *r_dest, con memnew_placement(dest, PoolVector<godot_int>); dest->resize(a->size()); - for (size_t i = 0; i < a->size(); i++) { + for (int i = 0; i < a->size(); i++) { dest->set(i, (*a)[i]); } } @@ -223,7 +223,7 @@ void GDAPI godot_pool_real_array_new_with_array(godot_pool_real_array *r_dest, c memnew_placement(dest, PoolVector<godot_real>); dest->resize(a->size()); - for (size_t i = 0; i < a->size(); i++) { + for (int i = 0; i < a->size(); i++) { dest->set(i, (*a)[i]); } } @@ -302,7 +302,7 @@ void GDAPI godot_pool_string_array_new_with_array(godot_pool_string_array *r_des memnew_placement(dest, PoolVector<String>); dest->resize(a->size()); - for (size_t i = 0; i < a->size(); i++) { + for (int i = 0; i < a->size(); i++) { dest->set(i, (*a)[i]); } } @@ -389,7 +389,7 @@ void GDAPI godot_pool_vector2_array_new_with_array(godot_pool_vector2_array *r_d memnew_placement(dest, PoolVector<Vector2>); dest->resize(a->size()); - for (size_t i = 0; i < a->size(); i++) { + for (int i = 0; i < a->size(); i++) { dest->set(i, (*a)[i]); } } @@ -475,7 +475,7 @@ void GDAPI godot_pool_vector3_array_new_with_array(godot_pool_vector3_array *r_d memnew_placement(dest, PoolVector<Vector3>); dest->resize(a->size()); - for (size_t i = 0; i < a->size(); i++) { + for (int i = 0; i < a->size(); i++) { dest->set(i, (*a)[i]); } } @@ -561,7 +561,7 @@ void GDAPI godot_pool_color_array_new_with_array(godot_pool_color_array *r_dest, memnew_placement(dest, PoolVector<Color>); dest->resize(a->size()); - for (size_t i = 0; i < a->size(); i++) { + for (int i = 0; i < a->size(); i++) { dest->set(i, (*a)[i]); } } diff --git a/modules/gdnative/godot/quat.cpp b/modules/gdnative/gdnative/quat.cpp index e6bea78b60..7db7847da1 100644 --- a/modules/gdnative/godot/quat.cpp +++ b/modules/gdnative/gdnative/quat.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/quat.h> +#include "gdnative/quat.h" #include "core/math/quat.h" #include "core/variant.h" diff --git a/modules/gdnative/godot/rect2.cpp b/modules/gdnative/gdnative/rect2.cpp index 98e7855dc9..ecd8cce9ca 100644 --- a/modules/gdnative/godot/rect2.cpp +++ b/modules/gdnative/gdnative/rect2.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/rect2.h> +#include "gdnative/rect2.h" #include "core/math/math_2d.h" #include "core/variant.h" diff --git a/modules/gdnative/godot/rect3.cpp b/modules/gdnative/gdnative/rect3.cpp index 88952ab49c..d34d964db9 100644 --- a/modules/gdnative/godot/rect3.cpp +++ b/modules/gdnative/gdnative/rect3.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/rect3.h> +#include "gdnative/rect3.h" #include "core/math/rect3.h" #include "core/variant.h" diff --git a/modules/gdnative/godot/rid.cpp b/modules/gdnative/gdnative/rid.cpp index 51c8aaa1b3..f05c39906c 100644 --- a/modules/gdnative/godot/rid.cpp +++ b/modules/gdnative/gdnative/rid.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/rid.h> +#include "gdnative/rid.h" #include "core/resource.h" #include "core/rid.h" diff --git a/modules/gdnative/godot/string.cpp b/modules/gdnative/gdnative/string.cpp index 3790b6ea95..905c513d9d 100644 --- a/modules/gdnative/godot/string.cpp +++ b/modules/gdnative/gdnative/string.cpp @@ -27,11 +27,11 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/string.h> +#include "gdnative/string.h" +#include "core/string_db.h" +#include "core/ustring.h" #include "core/variant.h" -#include "string_db.h" -#include "ustring.h" #include <string.h> @@ -232,7 +232,7 @@ godot_int GDAPI godot_string_findn_from(const godot_string *p_self, godot_string return self->findn(*what, p_from); } -godot_int GDAPI find_last(const godot_string *p_self, godot_string p_what) { +godot_int GDAPI godot_string_find_last(const godot_string *p_self, godot_string p_what) { const String *self = (const String *)p_self; String *what = (String *)&p_what; diff --git a/modules/gdnative/gdnative/string_name.cpp b/modules/gdnative/gdnative/string_name.cpp new file mode 100644 index 0000000000..5c00fdfc2f --- /dev/null +++ b/modules/gdnative/gdnative/string_name.cpp @@ -0,0 +1,91 @@ +/*************************************************************************/ +/* string_name.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "gdnative/string_name.h" + +#include "core/string_db.h" +#include "core/ustring.h" + +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +void _string_name_api_anchor() { +} + +void GDAPI godot_string_name_new(godot_string_name *r_dest, const godot_string *p_name) { + StringName *dest = (StringName *)r_dest; + const String *name = (const String *)p_name; + memnew_placement(dest, StringName(*name)); +} + +void GDAPI godot_string_name_new_data(godot_string_name *r_dest, const char *p_name) { + StringName *dest = (StringName *)r_dest; + memnew_placement(dest, StringName(p_name)); +} + +godot_string GDAPI godot_string_name_get_name(const godot_string_name *p_self) { + godot_string ret; + const StringName *self = (const StringName *)p_self; + memnew_placement(&ret, String(*self)); + return ret; +} + +uint32_t GDAPI godot_string_name_get_hash(const godot_string_name *p_self) { + const StringName *self = (const StringName *)p_self; + return self->hash(); +} + +const void GDAPI *godot_string_name_get_data_unique_pointer(const godot_string_name *p_self) { + const StringName *self = (const StringName *)p_self; + return self->data_unique_pointer(); +} + +godot_bool GDAPI godot_string_name_operator_equal(const godot_string_name *p_self, const godot_string_name *p_other) { + const StringName *self = (const StringName *)p_self; + const StringName *other = (const StringName *)p_other; + return self == other; +} + +godot_bool GDAPI godot_string_name_operator_less(const godot_string_name *p_self, const godot_string_name *p_other) { + const StringName *self = (const StringName *)p_self; + const StringName *other = (const StringName *)p_other; + return self < other; +} + +void GDAPI godot_string_name_destroy(godot_string_name *p_self) { + StringName *self = (StringName *)p_self; + self->~StringName(); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/gdnative/godot/transform.cpp b/modules/gdnative/gdnative/transform.cpp index a965067b77..d7a3e78d3f 100644 --- a/modules/gdnative/godot/transform.cpp +++ b/modules/gdnative/gdnative/transform.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/transform.h> +#include "gdnative/transform.h" #include "core/math/transform.h" #include "core/variant.h" diff --git a/modules/gdnative/godot/transform2d.cpp b/modules/gdnative/gdnative/transform2d.cpp index 9fc44ecdfa..dcb54f7a53 100644 --- a/modules/gdnative/godot/transform2d.cpp +++ b/modules/gdnative/gdnative/transform2d.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/transform2d.h> +#include "gdnative/transform2d.h" #include "core/math/math_2d.h" #include "core/variant.h" diff --git a/modules/gdnative/godot/variant.cpp b/modules/gdnative/gdnative/variant.cpp index 582544b3a0..9ba4166c1d 100644 --- a/modules/gdnative/godot/variant.cpp +++ b/modules/gdnative/gdnative/variant.cpp @@ -27,8 +27,9 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/variant.h> +#include "gdnative/variant.h" +#include "core/reference.h" #include "core/variant.h" #ifdef __cplusplus @@ -158,7 +159,21 @@ void GDAPI godot_variant_new_rid(godot_variant *r_dest, const godot_rid *p_rid) void GDAPI godot_variant_new_object(godot_variant *r_dest, const godot_object *p_obj) { Variant *dest = (Variant *)r_dest; Object *obj = (Object *)p_obj; - memnew_placement_custom(dest, Variant, Variant(obj)); + Reference *reference = Object::cast_to<Reference>(obj); + REF ref; + if (reference) { + ref = REF(reference); + } + if (!ref.is_null()) { + memnew_placement_custom(dest, Variant, Variant(ref.get_ref_ptr())); + } else { +#if defined(DEBUG_METHODS_ENABLED) + if (reference) { + ERR_PRINT("Reference object has 0 refcount in godot_variant_new_object - you lost it somewhere."); + } +#endif + memnew_placement_custom(dest, Variant, Variant(obj)); + } } void GDAPI godot_variant_new_dictionary(godot_variant *r_dest, const godot_dictionary *p_dict) { @@ -465,10 +480,9 @@ godot_bool GDAPI godot_variant_hash_compare(const godot_variant *p_self, const g return self->hash_compare(*other); } -godot_bool GDAPI godot_variant_booleanize(const godot_variant *p_self, godot_bool *r_valid) { +godot_bool GDAPI godot_variant_booleanize(const godot_variant *p_self) { const Variant *self = (const Variant *)p_self; - bool &valid = *r_valid; - return self->booleanize(valid); + return self->booleanize(); } void GDAPI godot_variant_destroy(godot_variant *p_self) { diff --git a/modules/gdnative/godot/vector2.cpp b/modules/gdnative/gdnative/vector2.cpp index 78ed5f06a9..67f858997f 100644 --- a/modules/gdnative/godot/vector2.cpp +++ b/modules/gdnative/gdnative/vector2.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/vector2.h> +#include "gdnative/vector2.h" #include "core/math/math_2d.h" #include "core/variant.h" diff --git a/modules/gdnative/godot/vector3.cpp b/modules/gdnative/gdnative/vector3.cpp index 5faeac2864..c85a3f1c08 100644 --- a/modules/gdnative/godot/vector3.cpp +++ b/modules/gdnative/gdnative/vector3.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include <godot/vector3.h> +#include "gdnative/vector3.h" #include "core/variant.h" #include "core/vector.h" diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json new file mode 100644 index 0000000000..31b021b751 --- /dev/null +++ b/modules/gdnative/gdnative_api.json @@ -0,0 +1,5313 @@ +{ + "version": "1.0.0", + "api": [ + { + "name": "godot_color_new_rgba", + "return_type": "void", + "arguments": [ + ["godot_color *", "r_dest"], + ["const godot_real", "p_r"], + ["const godot_real", "p_g"], + ["const godot_real", "p_b"], + ["const godot_real", "p_a"] + ] + }, + { + "name": "godot_color_new_rgb", + "return_type": "void", + "arguments": [ + ["godot_color *", "r_dest"], + ["const godot_real", "p_r"], + ["const godot_real", "p_g"], + ["const godot_real", "p_b"] + ] + }, + { + "name": "godot_color_get_r", + "return_type": "godot_real", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_set_r", + "return_type": "void", + "arguments": [ + ["godot_color *", "p_self"], + ["const godot_real", "r"] + ] + }, + { + "name": "godot_color_get_g", + "return_type": "godot_real", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_set_g", + "return_type": "void", + "arguments": [ + ["godot_color *", "p_self"], + ["const godot_real", "g"] + ] + }, + { + "name": "godot_color_get_b", + "return_type": "godot_real", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_set_b", + "return_type": "void", + "arguments": [ + ["godot_color *", "p_self"], + ["const godot_real", "b"] + ] + }, + { + "name": "godot_color_get_a", + "return_type": "godot_real", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_set_a", + "return_type": "void", + "arguments": [ + ["godot_color *", "p_self"], + ["const godot_real", "a"] + ] + }, + { + "name": "godot_color_get_h", + "return_type": "godot_real", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_get_s", + "return_type": "godot_real", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_get_v", + "return_type": "godot_real", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_as_string", + "return_type": "godot_string", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_to_rgba32", + "return_type": "godot_int", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_to_argb32", + "return_type": "godot_int", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_gray", + "return_type": "godot_real", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_inverted", + "return_type": "godot_color", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_contrasted", + "return_type": "godot_color", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_linear_interpolate", + "return_type": "godot_color", + "arguments": [ + ["const godot_color *", "p_self"], + ["const godot_color *", "p_b"], + ["const godot_real", "p_t"] + ] + }, + { + "name": "godot_color_blend", + "return_type": "godot_color", + "arguments": [ + ["const godot_color *", "p_self"], + ["const godot_color *", "p_over"] + ] + }, + { + "name": "godot_color_to_html", + "return_type": "godot_string", + "arguments": [ + ["const godot_color *", "p_self"], + ["const godot_bool", "p_with_alpha"] + ] + }, + { + "name": "godot_color_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_color *", "p_self"], + ["const godot_color *", "p_b"] + ] + }, + { + "name": "godot_color_operator_less", + "return_type": "godot_bool", + "arguments": [ + ["const godot_color *", "p_self"], + ["const godot_color *", "p_b"] + ] + }, + { + "name": "godot_vector2_new", + "return_type": "void", + "arguments": [ + ["godot_vector2 *", "r_dest"], + ["const godot_real", "p_x"], + ["const godot_real", "p_y"] + ] + }, + { + "name": "godot_vector2_as_string", + "return_type": "godot_string", + "arguments": [ + ["const godot_vector2 *", "p_self"] + ] + }, + { + "name": "godot_vector2_normalized", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"] + ] + }, + { + "name": "godot_vector2_length", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector2 *", "p_self"] + ] + }, + { + "name": "godot_vector2_angle", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector2 *", "p_self"] + ] + }, + { + "name": "godot_vector2_length_squared", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector2 *", "p_self"] + ] + }, + { + "name": "godot_vector2_is_normalized", + "return_type": "godot_bool", + "arguments": [ + ["const godot_vector2 *", "p_self"] + ] + }, + { + "name": "godot_vector2_distance_to", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_to"] + ] + }, + { + "name": "godot_vector2_distance_squared_to", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_to"] + ] + }, + { + "name": "godot_vector2_angle_to", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_to"] + ] + }, + { + "name": "godot_vector2_angle_to_point", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_to"] + ] + }, + { + "name": "godot_vector2_linear_interpolate", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_b"], + ["const godot_real", "p_t"] + ] + }, + { + "name": "godot_vector2_cubic_interpolate", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_b"], + ["const godot_vector2 *", "p_pre_a"], + ["const godot_vector2 *", "p_post_b"], + ["const godot_real", "p_t"] + ] + }, + { + "name": "godot_vector2_rotated", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_real", "p_phi"] + ] + }, + { + "name": "godot_vector2_tangent", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"] + ] + }, + { + "name": "godot_vector2_floor", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"] + ] + }, + { + "name": "godot_vector2_snapped", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_by"] + ] + }, + { + "name": "godot_vector2_aspect", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector2 *", "p_self"] + ] + }, + { + "name": "godot_vector2_dot", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_with"] + ] + }, + { + "name": "godot_vector2_slide", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_n"] + ] + }, + { + "name": "godot_vector2_bounce", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_n"] + ] + }, + { + "name": "godot_vector2_reflect", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_n"] + ] + }, + { + "name": "godot_vector2_abs", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"] + ] + }, + { + "name": "godot_vector2_clamped", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_real", "p_length"] + ] + }, + { + "name": "godot_vector2_operator_add", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_b"] + ] + }, + { + "name": "godot_vector2_operator_substract", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_b"] + ] + }, + { + "name": "godot_vector2_operator_multiply_vector", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_b"] + ] + }, + { + "name": "godot_vector2_operator_multiply_scalar", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_real", "p_b"] + ] + }, + { + "name": "godot_vector2_operator_divide_vector", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_b"] + ] + }, + { + "name": "godot_vector2_operator_divide_scalar", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_real", "p_b"] + ] + }, + { + "name": "godot_vector2_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_b"] + ] + }, + { + "name": "godot_vector2_operator_less", + "return_type": "godot_bool", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_b"] + ] + }, + { + "name": "godot_vector2_operator_neg", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"] + ] + }, + { + "name": "godot_vector2_set_x", + "return_type": "void", + "arguments": [ + ["godot_vector2 *", "p_self"], + ["const godot_real", "p_x"] + ] + }, + { + "name": "godot_vector2_set_y", + "return_type": "void", + "arguments": [ + ["godot_vector2 *", "p_self"], + ["const godot_real", "p_y"] + ] + }, + { + "name": "godot_vector2_get_x", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector2 *", "p_self"] + ] + }, + { + "name": "godot_vector2_get_y", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector2 *", "p_self"] + ] + }, + { + "name": "godot_quat_new", + "return_type": "void", + "arguments": [ + ["godot_quat *", "r_dest"], + ["const godot_real", "p_x"], + ["const godot_real", "p_y"], + ["const godot_real", "p_z"], + ["const godot_real", "p_w"] + ] + }, + { + "name": "godot_quat_new_with_axis_angle", + "return_type": "void", + "arguments": [ + ["godot_quat *", "r_dest"], + ["const godot_vector3 *", "p_axis"], + ["const godot_real", "p_angle"] + ] + }, + { + "name": "godot_quat_get_x", + "return_type": "godot_real", + "arguments": [ + ["const godot_quat *", "p_self"] + ] + }, + { + "name": "godot_quat_set_x", + "return_type": "void", + "arguments": [ + ["godot_quat *", "p_self"], + ["const godot_real", "val"] + ] + }, + { + "name": "godot_quat_get_y", + "return_type": "godot_real", + "arguments": [ + ["const godot_quat *", "p_self"] + ] + }, + { + "name": "godot_quat_set_y", + "return_type": "void", + "arguments": [ + ["godot_quat *", "p_self"], + ["const godot_real", "val"] + ] + }, + { + "name": "godot_quat_get_z", + "return_type": "godot_real", + "arguments": [ + ["const godot_quat *", "p_self"] + ] + }, + { + "name": "godot_quat_set_z", + "return_type": "void", + "arguments": [ + ["godot_quat *", "p_self"], + ["const godot_real", "val"] + ] + }, + { + "name": "godot_quat_get_w", + "return_type": "godot_real", + "arguments": [ + ["const godot_quat *", "p_self"] + ] + }, + { + "name": "godot_quat_set_w", + "return_type": "void", + "arguments": [ + ["godot_quat *", "p_self"], + ["const godot_real", "val"] + ] + }, + { + "name": "godot_quat_as_string", + "return_type": "godot_string", + "arguments": [ + ["const godot_quat *", "p_self"] + ] + }, + { + "name": "godot_quat_length", + "return_type": "godot_real", + "arguments": [ + ["const godot_quat *", "p_self"] + ] + }, + { + "name": "godot_quat_length_squared", + "return_type": "godot_real", + "arguments": [ + ["const godot_quat *", "p_self"] + ] + }, + { + "name": "godot_quat_normalized", + "return_type": "godot_quat", + "arguments": [ + ["const godot_quat *", "p_self"] + ] + }, + { + "name": "godot_quat_is_normalized", + "return_type": "godot_bool", + "arguments": [ + ["const godot_quat *", "p_self"] + ] + }, + { + "name": "godot_quat_inverse", + "return_type": "godot_quat", + "arguments": [ + ["const godot_quat *", "p_self"] + ] + }, + { + "name": "godot_quat_dot", + "return_type": "godot_real", + "arguments": [ + ["const godot_quat *", "p_self"], + ["const godot_quat *", "p_b"] + ] + }, + { + "name": "godot_quat_xform", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_quat *", "p_self"], + ["const godot_vector3 *", "p_v"] + ] + }, + { + "name": "godot_quat_slerp", + "return_type": "godot_quat", + "arguments": [ + ["const godot_quat *", "p_self"], + ["const godot_quat *", "p_b"], + ["const godot_real", "p_t"] + ] + }, + { + "name": "godot_quat_slerpni", + "return_type": "godot_quat", + "arguments": [ + ["const godot_quat *", "p_self"], + ["const godot_quat *", "p_b"], + ["const godot_real", "p_t"] + ] + }, + { + "name": "godot_quat_cubic_slerp", + "return_type": "godot_quat", + "arguments": [ + ["const godot_quat *", "p_self"], + ["const godot_quat *", "p_b"], + ["const godot_quat *", "p_pre_a"], + ["const godot_quat *", "p_post_b"], + ["const godot_real", "p_t"] + ] + }, + { + "name": "godot_quat_operator_multiply", + "return_type": "godot_quat", + "arguments": [ + ["const godot_quat *", "p_self"], + ["const godot_real", "p_b"] + ] + }, + { + "name": "godot_quat_operator_add", + "return_type": "godot_quat", + "arguments": [ + ["const godot_quat *", "p_self"], + ["const godot_quat *", "p_b"] + ] + }, + { + "name": "godot_quat_operator_substract", + "return_type": "godot_quat", + "arguments": [ + ["const godot_quat *", "p_self"], + ["const godot_quat *", "p_b"] + ] + }, + { + "name": "godot_quat_operator_divide", + "return_type": "godot_quat", + "arguments": [ + ["const godot_quat *", "p_self"], + ["const godot_real", "p_b"] + ] + }, + { + "name": "godot_quat_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_quat *", "p_self"], + ["const godot_quat *", "p_b"] + ] + }, + { + "name": "godot_quat_operator_neg", + "return_type": "godot_quat", + "arguments": [ + ["const godot_quat *", "p_self"] + ] + }, + { + "name": "godot_basis_new_with_rows", + "return_type": "void", + "arguments": [ + ["godot_basis *", "r_dest"], + ["const godot_vector3 *", "p_x_axis"], + ["const godot_vector3 *", "p_y_axis"], + ["const godot_vector3 *", "p_z_axis"] + ] + }, + { + "name": "godot_basis_new_with_axis_and_angle", + "return_type": "void", + "arguments": [ + ["godot_basis *", "r_dest"], + ["const godot_vector3 *", "p_axis"], + ["const godot_real", "p_phi"] + ] + }, + { + "name": "godot_basis_new_with_euler", + "return_type": "void", + "arguments": [ + ["godot_basis *", "r_dest"], + ["const godot_vector3 *", "p_euler"] + ] + }, + { + "name": "godot_basis_as_string", + "return_type": "godot_string", + "arguments": [ + ["const godot_basis *", "p_self"] + ] + }, + { + "name": "godot_basis_inverse", + "return_type": "godot_basis", + "arguments": [ + ["const godot_basis *", "p_self"] + ] + }, + { + "name": "godot_basis_transposed", + "return_type": "godot_basis", + "arguments": [ + ["const godot_basis *", "p_self"] + ] + }, + { + "name": "godot_basis_orthonormalized", + "return_type": "godot_basis", + "arguments": [ + ["const godot_basis *", "p_self"] + ] + }, + { + "name": "godot_basis_determinant", + "return_type": "godot_real", + "arguments": [ + ["const godot_basis *", "p_self"] + ] + }, + { + "name": "godot_basis_rotated", + "return_type": "godot_basis", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_vector3 *", "p_axis"], + ["const godot_real", "p_phi"] + ] + }, + { + "name": "godot_basis_scaled", + "return_type": "godot_basis", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_vector3 *", "p_scale"] + ] + }, + { + "name": "godot_basis_get_scale", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_basis *", "p_self"] + ] + }, + { + "name": "godot_basis_get_euler", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_basis *", "p_self"] + ] + }, + { + "name": "godot_basis_tdotx", + "return_type": "godot_real", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_vector3 *", "p_with"] + ] + }, + { + "name": "godot_basis_tdoty", + "return_type": "godot_real", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_vector3 *", "p_with"] + ] + }, + { + "name": "godot_basis_tdotz", + "return_type": "godot_real", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_vector3 *", "p_with"] + ] + }, + { + "name": "godot_basis_xform", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_vector3 *", "p_v"] + ] + }, + { + "name": "godot_basis_xform_inv", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_vector3 *", "p_v"] + ] + }, + { + "name": "godot_basis_get_orthogonal_index", + "return_type": "godot_int", + "arguments": [ + ["const godot_basis *", "p_self"] + ] + }, + { + "name": "godot_basis_new", + "return_type": "void", + "arguments": [ + ["godot_basis *", "r_dest"] + ] + }, + { + "name": "godot_basis_new_with_euler_quat", + "return_type": "void", + "arguments": [ + ["godot_basis *", "r_dest"], + ["const godot_quat *", "p_euler"] + ] + }, + { + "name": "godot_basis_get_elements", + "return_type": "void", + "arguments": [ + ["godot_basis *", "p_self"], + ["godot_vector3 *", "p_elements"] + ] + }, + { + "name": "godot_basis_get_axis", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_int", "p_axis"] + ] + }, + { + "name": "godot_basis_set_axis", + "return_type": "void", + "arguments": [ + ["godot_basis *", "p_self"], + ["const godot_int", "p_axis"], + ["const godot_vector3 *", "p_value"] + ] + }, + { + "name": "godot_basis_get_row", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_int", "p_row"] + ] + }, + { + "name": "godot_basis_set_row", + "return_type": "void", + "arguments": [ + ["godot_basis *", "p_self"], + ["const godot_int", "p_row"], + ["const godot_vector3 *", "p_value"] + ] + }, + { + "name": "godot_basis_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_basis *", "p_b"] + ] + }, + { + "name": "godot_basis_operator_add", + "return_type": "godot_basis", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_basis *", "p_b"] + ] + }, + { + "name": "godot_basis_operator_substract", + "return_type": "godot_basis", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_basis *", "p_b"] + ] + }, + { + "name": "godot_basis_operator_multiply_vector", + "return_type": "godot_basis", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_basis *", "p_b"] + ] + }, + { + "name": "godot_basis_operator_multiply_scalar", + "return_type": "godot_basis", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_real", "p_b"] + ] + }, + { + "name": "godot_vector3_new", + "return_type": "void", + "arguments": [ + ["godot_vector3 *", "r_dest"], + ["const godot_real", "p_x"], + ["const godot_real", "p_y"], + ["const godot_real", "p_z"] + ] + }, + { + "name": "godot_vector3_as_string", + "return_type": "godot_string", + "arguments": [ + ["const godot_vector3 *", "p_self"] + ] + }, + { + "name": "godot_vector3_min_axis", + "return_type": "godot_int", + "arguments": [ + ["const godot_vector3 *", "p_self"] + ] + }, + { + "name": "godot_vector3_max_axis", + "return_type": "godot_int", + "arguments": [ + ["const godot_vector3 *", "p_self"] + ] + }, + { + "name": "godot_vector3_length", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector3 *", "p_self"] + ] + }, + { + "name": "godot_vector3_length_squared", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector3 *", "p_self"] + ] + }, + { + "name": "godot_vector3_is_normalized", + "return_type": "godot_bool", + "arguments": [ + ["const godot_vector3 *", "p_self"] + ] + }, + { + "name": "godot_vector3_normalized", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"] + ] + }, + { + "name": "godot_vector3_inverse", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"] + ] + }, + { + "name": "godot_vector3_snapped", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_by"] + ] + }, + { + "name": "godot_vector3_rotated", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_axis"], + ["const godot_real", "p_phi"] + ] + }, + { + "name": "godot_vector3_linear_interpolate", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_b"], + ["const godot_real", "p_t"] + ] + }, + { + "name": "godot_vector3_cubic_interpolate", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_b"], + ["const godot_vector3 *", "p_pre_a"], + ["const godot_vector3 *", "p_post_b"], + ["const godot_real", "p_t"] + ] + }, + { + "name": "godot_vector3_dot", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_b"] + ] + }, + { + "name": "godot_vector3_cross", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_b"] + ] + }, + { + "name": "godot_vector3_outer", + "return_type": "godot_basis", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_b"] + ] + }, + { + "name": "godot_vector3_to_diagonal_matrix", + "return_type": "godot_basis", + "arguments": [ + ["const godot_vector3 *", "p_self"] + ] + }, + { + "name": "godot_vector3_abs", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"] + ] + }, + { + "name": "godot_vector3_floor", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"] + ] + }, + { + "name": "godot_vector3_ceil", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"] + ] + }, + { + "name": "godot_vector3_distance_to", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_b"] + ] + }, + { + "name": "godot_vector3_distance_squared_to", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_b"] + ] + }, + { + "name": "godot_vector3_angle_to", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_to"] + ] + }, + { + "name": "godot_vector3_slide", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_n"] + ] + }, + { + "name": "godot_vector3_bounce", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_n"] + ] + }, + { + "name": "godot_vector3_reflect", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_n"] + ] + }, + { + "name": "godot_vector3_operator_add", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_b"] + ] + }, + { + "name": "godot_vector3_operator_substract", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_b"] + ] + }, + { + "name": "godot_vector3_operator_multiply_vector", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_b"] + ] + }, + { + "name": "godot_vector3_operator_multiply_scalar", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_real", "p_b"] + ] + }, + { + "name": "godot_vector3_operator_divide_vector", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_b"] + ] + }, + { + "name": "godot_vector3_operator_divide_scalar", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_real", "p_b"] + ] + }, + { + "name": "godot_vector3_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_b"] + ] + }, + { + "name": "godot_vector3_operator_less", + "return_type": "godot_bool", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_b"] + ] + }, + { + "name": "godot_vector3_operator_neg", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"] + ] + }, + { + "name": "godot_vector3_set_axis", + "return_type": "void", + "arguments": [ + ["godot_vector3 *", "p_self"], + ["const godot_vector3_axis", "p_axis"], + ["const godot_real", "p_val"] + ] + }, + { + "name": "godot_vector3_get_axis", + "return_type": "godot_real", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3_axis", "p_axis"] + ] + }, + { + "name": "godot_pool_byte_array_new", + "return_type": "void", + "arguments": [ + ["godot_pool_byte_array *", "r_dest"] + ] + }, + { + "name": "godot_pool_byte_array_new_copy", + "return_type": "void", + "arguments": [ + ["godot_pool_byte_array *", "r_dest"], + ["const godot_pool_byte_array *", "p_src"] + ] + }, + { + "name": "godot_pool_byte_array_new_with_array", + "return_type": "void", + "arguments": [ + ["godot_pool_byte_array *", "r_dest"], + ["const godot_array *", "p_a"] + ] + }, + { + "name": "godot_pool_byte_array_append", + "return_type": "void", + "arguments": [ + ["godot_pool_byte_array *", "p_self"], + ["const uint8_t", "p_data"] + ] + }, + { + "name": "godot_pool_byte_array_append_array", + "return_type": "void", + "arguments": [ + ["godot_pool_byte_array *", "p_self"], + ["const godot_pool_byte_array *", "p_array"] + ] + }, + { + "name": "godot_pool_byte_array_insert", + "return_type": "godot_error", + "arguments": [ + ["godot_pool_byte_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const uint8_t", "p_data"] + ] + }, + { + "name": "godot_pool_byte_array_invert", + "return_type": "void", + "arguments": [ + ["godot_pool_byte_array *", "p_self"] + ] + }, + { + "name": "godot_pool_byte_array_push_back", + "return_type": "void", + "arguments": [ + ["godot_pool_byte_array *", "p_self"], + ["const uint8_t", "p_data"] + ] + }, + { + "name": "godot_pool_byte_array_remove", + "return_type": "void", + "arguments": [ + ["godot_pool_byte_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_pool_byte_array_resize", + "return_type": "void", + "arguments": [ + ["godot_pool_byte_array *", "p_self"], + ["const godot_int", "p_size"] + ] + }, + { + "name": "godot_pool_byte_array_set", + "return_type": "void", + "arguments": [ + ["godot_pool_byte_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const uint8_t", "p_data"] + ] + }, + { + "name": "godot_pool_byte_array_get", + "return_type": "uint8_t", + "arguments": [ + ["const godot_pool_byte_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_pool_byte_array_size", + "return_type": "godot_int", + "arguments": [ + ["const godot_pool_byte_array *", "p_self"] + ] + }, + { + "name": "godot_pool_byte_array_destroy", + "return_type": "void", + "arguments": [ + ["godot_pool_byte_array *", "p_self"] + ] + }, + { + "name": "godot_pool_int_array_new", + "return_type": "void", + "arguments": [ + ["godot_pool_int_array *", "r_dest"] + ] + }, + { + "name": "godot_pool_int_array_new_copy", + "return_type": "void", + "arguments": [ + ["godot_pool_int_array *", "r_dest"], + ["const godot_pool_int_array *", "p_src"] + ] + }, + { + "name": "godot_pool_int_array_new_with_array", + "return_type": "void", + "arguments": [ + ["godot_pool_int_array *", "r_dest"], + ["const godot_array *", "p_a"] + ] + }, + { + "name": "godot_pool_int_array_append", + "return_type": "void", + "arguments": [ + ["godot_pool_int_array *", "p_self"], + ["const godot_int", "p_data"] + ] + }, + { + "name": "godot_pool_int_array_append_array", + "return_type": "void", + "arguments": [ + ["godot_pool_int_array *", "p_self"], + ["const godot_pool_int_array *", "p_array"] + ] + }, + { + "name": "godot_pool_int_array_insert", + "return_type": "godot_error", + "arguments": [ + ["godot_pool_int_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const godot_int", "p_data"] + ] + }, + { + "name": "godot_pool_int_array_invert", + "return_type": "void", + "arguments": [ + ["godot_pool_int_array *", "p_self"] + ] + }, + { + "name": "godot_pool_int_array_push_back", + "return_type": "void", + "arguments": [ + ["godot_pool_int_array *", "p_self"], + ["const godot_int", "p_data"] + ] + }, + { + "name": "godot_pool_int_array_remove", + "return_type": "void", + "arguments": [ + ["godot_pool_int_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_pool_int_array_resize", + "return_type": "void", + "arguments": [ + ["godot_pool_int_array *", "p_self"], + ["const godot_int", "p_size"] + ] + }, + { + "name": "godot_pool_int_array_set", + "return_type": "void", + "arguments": [ + ["godot_pool_int_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const godot_int", "p_data"] + ] + }, + { + "name": "godot_pool_int_array_get", + "return_type": "godot_int", + "arguments": [ + ["const godot_pool_int_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_pool_int_array_size", + "return_type": "godot_int", + "arguments": [ + ["const godot_pool_int_array *", "p_self"] + ] + }, + { + "name": "godot_pool_int_array_destroy", + "return_type": "void", + "arguments": [ + ["godot_pool_int_array *", "p_self"] + ] + }, + { + "name": "godot_pool_real_array_new", + "return_type": "void", + "arguments": [ + ["godot_pool_real_array *", "r_dest"] + ] + }, + { + "name": "godot_pool_real_array_new_copy", + "return_type": "void", + "arguments": [ + ["godot_pool_real_array *", "r_dest"], + ["const godot_pool_real_array *", "p_src"] + ] + }, + { + "name": "godot_pool_real_array_new_with_array", + "return_type": "void", + "arguments": [ + ["godot_pool_real_array *", "r_dest"], + ["const godot_array *", "p_a"] + ] + }, + { + "name": "godot_pool_real_array_append", + "return_type": "void", + "arguments": [ + ["godot_pool_real_array *", "p_self"], + ["const godot_real", "p_data"] + ] + }, + { + "name": "godot_pool_real_array_append_array", + "return_type": "void", + "arguments": [ + ["godot_pool_real_array *", "p_self"], + ["const godot_pool_real_array *", "p_array"] + ] + }, + { + "name": "godot_pool_real_array_insert", + "return_type": "godot_error", + "arguments": [ + ["godot_pool_real_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const godot_real", "p_data"] + ] + }, + { + "name": "godot_pool_real_array_invert", + "return_type": "void", + "arguments": [ + ["godot_pool_real_array *", "p_self"] + ] + }, + { + "name": "godot_pool_real_array_push_back", + "return_type": "void", + "arguments": [ + ["godot_pool_real_array *", "p_self"], + ["const godot_real", "p_data"] + ] + }, + { + "name": "godot_pool_real_array_remove", + "return_type": "void", + "arguments": [ + ["godot_pool_real_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_pool_real_array_resize", + "return_type": "void", + "arguments": [ + ["godot_pool_real_array *", "p_self"], + ["const godot_int", "p_size"] + ] + }, + { + "name": "godot_pool_real_array_set", + "return_type": "void", + "arguments": [ + ["godot_pool_real_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const godot_real", "p_data"] + ] + }, + { + "name": "godot_pool_real_array_get", + "return_type": "godot_real", + "arguments": [ + ["const godot_pool_real_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_pool_real_array_size", + "return_type": "godot_int", + "arguments": [ + ["const godot_pool_real_array *", "p_self"] + ] + }, + { + "name": "godot_pool_real_array_destroy", + "return_type": "void", + "arguments": [ + ["godot_pool_real_array *", "p_self"] + ] + }, + { + "name": "godot_pool_string_array_new", + "return_type": "void", + "arguments": [ + ["godot_pool_string_array *", "r_dest"] + ] + }, + { + "name": "godot_pool_string_array_new_copy", + "return_type": "void", + "arguments": [ + ["godot_pool_string_array *", "r_dest"], + ["const godot_pool_string_array *", "p_src"] + ] + }, + { + "name": "godot_pool_string_array_new_with_array", + "return_type": "void", + "arguments": [ + ["godot_pool_string_array *", "r_dest"], + ["const godot_array *", "p_a"] + ] + }, + { + "name": "godot_pool_string_array_append", + "return_type": "void", + "arguments": [ + ["godot_pool_string_array *", "p_self"], + ["const godot_string *", "p_data"] + ] + }, + { + "name": "godot_pool_string_array_append_array", + "return_type": "void", + "arguments": [ + ["godot_pool_string_array *", "p_self"], + ["const godot_pool_string_array *", "p_array"] + ] + }, + { + "name": "godot_pool_string_array_insert", + "return_type": "godot_error", + "arguments": [ + ["godot_pool_string_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const godot_string *", "p_data"] + ] + }, + { + "name": "godot_pool_string_array_invert", + "return_type": "void", + "arguments": [ + ["godot_pool_string_array *", "p_self"] + ] + }, + { + "name": "godot_pool_string_array_push_back", + "return_type": "void", + "arguments": [ + ["godot_pool_string_array *", "p_self"], + ["const godot_string *", "p_data"] + ] + }, + { + "name": "godot_pool_string_array_remove", + "return_type": "void", + "arguments": [ + ["godot_pool_string_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_pool_string_array_resize", + "return_type": "void", + "arguments": [ + ["godot_pool_string_array *", "p_self"], + ["const godot_int", "p_size"] + ] + }, + { + "name": "godot_pool_string_array_set", + "return_type": "void", + "arguments": [ + ["godot_pool_string_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const godot_string *", "p_data"] + ] + }, + { + "name": "godot_pool_string_array_get", + "return_type": "godot_string", + "arguments": [ + ["const godot_pool_string_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_pool_string_array_size", + "return_type": "godot_int", + "arguments": [ + ["const godot_pool_string_array *", "p_self"] + ] + }, + { + "name": "godot_pool_string_array_destroy", + "return_type": "void", + "arguments": [ + ["godot_pool_string_array *", "p_self"] + ] + }, + { + "name": "godot_pool_vector2_array_new", + "return_type": "void", + "arguments": [ + ["godot_pool_vector2_array *", "r_dest"] + ] + }, + { + "name": "godot_pool_vector2_array_new_copy", + "return_type": "void", + "arguments": [ + ["godot_pool_vector2_array *", "r_dest"], + ["const godot_pool_vector2_array *", "p_src"] + ] + }, + { + "name": "godot_pool_vector2_array_new_with_array", + "return_type": "void", + "arguments": [ + ["godot_pool_vector2_array *", "r_dest"], + ["const godot_array *", "p_a"] + ] + }, + { + "name": "godot_pool_vector2_array_append", + "return_type": "void", + "arguments": [ + ["godot_pool_vector2_array *", "p_self"], + ["const godot_vector2 *", "p_data"] + ] + }, + { + "name": "godot_pool_vector2_array_append_array", + "return_type": "void", + "arguments": [ + ["godot_pool_vector2_array *", "p_self"], + ["const godot_pool_vector2_array *", "p_array"] + ] + }, + { + "name": "godot_pool_vector2_array_insert", + "return_type": "godot_error", + "arguments": [ + ["godot_pool_vector2_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const godot_vector2 *", "p_data"] + ] + }, + { + "name": "godot_pool_vector2_array_invert", + "return_type": "void", + "arguments": [ + ["godot_pool_vector2_array *", "p_self"] + ] + }, + { + "name": "godot_pool_vector2_array_push_back", + "return_type": "void", + "arguments": [ + ["godot_pool_vector2_array *", "p_self"], + ["const godot_vector2 *", "p_data"] + ] + }, + { + "name": "godot_pool_vector2_array_remove", + "return_type": "void", + "arguments": [ + ["godot_pool_vector2_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_pool_vector2_array_resize", + "return_type": "void", + "arguments": [ + ["godot_pool_vector2_array *", "p_self"], + ["const godot_int", "p_size"] + ] + }, + { + "name": "godot_pool_vector2_array_set", + "return_type": "void", + "arguments": [ + ["godot_pool_vector2_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const godot_vector2 *", "p_data"] + ] + }, + { + "name": "godot_pool_vector2_array_get", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_pool_vector2_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_pool_vector2_array_size", + "return_type": "godot_int", + "arguments": [ + ["const godot_pool_vector2_array *", "p_self"] + ] + }, + { + "name": "godot_pool_vector2_array_destroy", + "return_type": "void", + "arguments": [ + ["godot_pool_vector2_array *", "p_self"] + ] + }, + { + "name": "godot_pool_vector3_array_new", + "return_type": "void", + "arguments": [ + ["godot_pool_vector3_array *", "r_dest"] + ] + }, + { + "name": "godot_pool_vector3_array_new_copy", + "return_type": "void", + "arguments": [ + ["godot_pool_vector3_array *", "r_dest"], + ["const godot_pool_vector3_array *", "p_src"] + ] + }, + { + "name": "godot_pool_vector3_array_new_with_array", + "return_type": "void", + "arguments": [ + ["godot_pool_vector3_array *", "r_dest"], + ["const godot_array *", "p_a"] + ] + }, + { + "name": "godot_pool_vector3_array_append", + "return_type": "void", + "arguments": [ + ["godot_pool_vector3_array *", "p_self"], + ["const godot_vector3 *", "p_data"] + ] + }, + { + "name": "godot_pool_vector3_array_append_array", + "return_type": "void", + "arguments": [ + ["godot_pool_vector3_array *", "p_self"], + ["const godot_pool_vector3_array *", "p_array"] + ] + }, + { + "name": "godot_pool_vector3_array_insert", + "return_type": "godot_error", + "arguments": [ + ["godot_pool_vector3_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const godot_vector3 *", "p_data"] + ] + }, + { + "name": "godot_pool_vector3_array_invert", + "return_type": "void", + "arguments": [ + ["godot_pool_vector3_array *", "p_self"] + ] + }, + { + "name": "godot_pool_vector3_array_push_back", + "return_type": "void", + "arguments": [ + ["godot_pool_vector3_array *", "p_self"], + ["const godot_vector3 *", "p_data"] + ] + }, + { + "name": "godot_pool_vector3_array_remove", + "return_type": "void", + "arguments": [ + ["godot_pool_vector3_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_pool_vector3_array_resize", + "return_type": "void", + "arguments": [ + ["godot_pool_vector3_array *", "p_self"], + ["const godot_int", "p_size"] + ] + }, + { + "name": "godot_pool_vector3_array_set", + "return_type": "void", + "arguments": [ + ["godot_pool_vector3_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const godot_vector3 *", "p_data"] + ] + }, + { + "name": "godot_pool_vector3_array_get", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_pool_vector3_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_pool_vector3_array_size", + "return_type": "godot_int", + "arguments": [ + ["const godot_pool_vector3_array *", "p_self"] + ] + }, + { + "name": "godot_pool_vector3_array_destroy", + "return_type": "void", + "arguments": [ + ["godot_pool_vector3_array *", "p_self"] + ] + }, + { + "name": "godot_pool_color_array_new", + "return_type": "void", + "arguments": [ + ["godot_pool_color_array *", "r_dest"] + ] + }, + { + "name": "godot_pool_color_array_new_copy", + "return_type": "void", + "arguments": [ + ["godot_pool_color_array *", "r_dest"], + ["const godot_pool_color_array *", "p_src"] + ] + }, + { + "name": "godot_pool_color_array_new_with_array", + "return_type": "void", + "arguments": [ + ["godot_pool_color_array *", "r_dest"], + ["const godot_array *", "p_a"] + ] + }, + { + "name": "godot_pool_color_array_append", + "return_type": "void", + "arguments": [ + ["godot_pool_color_array *", "p_self"], + ["const godot_color *", "p_data"] + ] + }, + { + "name": "godot_pool_color_array_append_array", + "return_type": "void", + "arguments": [ + ["godot_pool_color_array *", "p_self"], + ["const godot_pool_color_array *", "p_array"] + ] + }, + { + "name": "godot_pool_color_array_insert", + "return_type": "godot_error", + "arguments": [ + ["godot_pool_color_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const godot_color *", "p_data"] + ] + }, + { + "name": "godot_pool_color_array_invert", + "return_type": "void", + "arguments": [ + ["godot_pool_color_array *", "p_self"] + ] + }, + { + "name": "godot_pool_color_array_push_back", + "return_type": "void", + "arguments": [ + ["godot_pool_color_array *", "p_self"], + ["const godot_color *", "p_data"] + ] + }, + { + "name": "godot_pool_color_array_remove", + "return_type": "void", + "arguments": [ + ["godot_pool_color_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_pool_color_array_resize", + "return_type": "void", + "arguments": [ + ["godot_pool_color_array *", "p_self"], + ["const godot_int", "p_size"] + ] + }, + { + "name": "godot_pool_color_array_set", + "return_type": "void", + "arguments": [ + ["godot_pool_color_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const godot_color *", "p_data"] + ] + }, + { + "name": "godot_pool_color_array_get", + "return_type": "godot_color", + "arguments": [ + ["const godot_pool_color_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_pool_color_array_size", + "return_type": "godot_int", + "arguments": [ + ["const godot_pool_color_array *", "p_self"] + ] + }, + { + "name": "godot_pool_color_array_destroy", + "return_type": "void", + "arguments": [ + ["godot_pool_color_array *", "p_self"] + ] + }, + { + "name": "godot_array_new", + "return_type": "void", + "arguments": [ + ["godot_array *", "r_dest"] + ] + }, + { + "name": "godot_array_new_copy", + "return_type": "void", + "arguments": [ + ["godot_array *", "r_dest"], + ["const godot_array *", "p_src"] + ] + }, + { + "name": "godot_array_new_pool_color_array", + "return_type": "void", + "arguments": [ + ["godot_array *", "r_dest"], + ["const godot_pool_color_array *", "p_pca"] + ] + }, + { + "name": "godot_array_new_pool_vector3_array", + "return_type": "void", + "arguments": [ + ["godot_array *", "r_dest"], + ["const godot_pool_vector3_array *", "p_pv3a"] + ] + }, + { + "name": "godot_array_new_pool_vector2_array", + "return_type": "void", + "arguments": [ + ["godot_array *", "r_dest"], + ["const godot_pool_vector2_array *", "p_pv2a"] + ] + }, + { + "name": "godot_array_new_pool_string_array", + "return_type": "void", + "arguments": [ + ["godot_array *", "r_dest"], + ["const godot_pool_string_array *", "p_psa"] + ] + }, + { + "name": "godot_array_new_pool_real_array", + "return_type": "void", + "arguments": [ + ["godot_array *", "r_dest"], + ["const godot_pool_real_array *", "p_pra"] + ] + }, + { + "name": "godot_array_new_pool_int_array", + "return_type": "void", + "arguments": [ + ["godot_array *", "r_dest"], + ["const godot_pool_int_array *", "p_pia"] + ] + }, + { + "name": "godot_array_new_pool_byte_array", + "return_type": "void", + "arguments": [ + ["godot_array *", "r_dest"], + ["const godot_pool_byte_array *", "p_pba"] + ] + }, + { + "name": "godot_array_set", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"], + ["const godot_int", "p_idx"], + ["const godot_variant *", "p_value"] + ] + }, + { + "name": "godot_array_get", + "return_type": "godot_variant", + "arguments": [ + ["const godot_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_array_operator_index", + "return_type": "godot_variant *", + "arguments": [ + ["godot_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_array_append", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"], + ["const godot_variant *", "p_value"] + ] + }, + { + "name": "godot_array_clear", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"] + ] + }, + { + "name": "godot_array_count", + "return_type": "godot_int", + "arguments": [ + ["const godot_array *", "p_self"], + ["const godot_variant *", "p_value"] + ] + }, + { + "name": "godot_array_empty", + "return_type": "godot_bool", + "arguments": [ + ["const godot_array *", "p_self"] + ] + }, + { + "name": "godot_array_erase", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"], + ["const godot_variant *", "p_value"] + ] + }, + { + "name": "godot_array_front", + "return_type": "godot_variant", + "arguments": [ + ["const godot_array *", "p_self"] + ] + }, + { + "name": "godot_array_back", + "return_type": "godot_variant", + "arguments": [ + ["const godot_array *", "p_self"] + ] + }, + { + "name": "godot_array_find", + "return_type": "godot_int", + "arguments": [ + ["const godot_array *", "p_self"], + ["const godot_variant *", "p_what"], + ["const godot_int", "p_from"] + ] + }, + { + "name": "godot_array_find_last", + "return_type": "godot_int", + "arguments": [ + ["const godot_array *", "p_self"], + ["const godot_variant *", "p_what"] + ] + }, + { + "name": "godot_array_has", + "return_type": "godot_bool", + "arguments": [ + ["const godot_array *", "p_self"], + ["const godot_variant *", "p_value"] + ] + }, + { + "name": "godot_array_hash", + "return_type": "godot_int", + "arguments": [ + ["const godot_array *", "p_self"] + ] + }, + { + "name": "godot_array_insert", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"], + ["const godot_int", "p_pos"], + ["const godot_variant *", "p_value"] + ] + }, + { + "name": "godot_array_invert", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"] + ] + }, + { + "name": "godot_array_pop_back", + "return_type": "godot_variant", + "arguments": [ + ["godot_array *", "p_self"] + ] + }, + { + "name": "godot_array_pop_front", + "return_type": "godot_variant", + "arguments": [ + ["godot_array *", "p_self"] + ] + }, + { + "name": "godot_array_push_back", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"], + ["const godot_variant *", "p_value"] + ] + }, + { + "name": "godot_array_push_front", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"], + ["const godot_variant *", "p_value"] + ] + }, + { + "name": "godot_array_remove", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_array_resize", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"], + ["const godot_int", "p_size"] + ] + }, + { + "name": "godot_array_rfind", + "return_type": "godot_int", + "arguments": [ + ["const godot_array *", "p_self"], + ["const godot_variant *", "p_what"], + ["const godot_int", "p_from"] + ] + }, + { + "name": "godot_array_size", + "return_type": "godot_int", + "arguments": [ + ["const godot_array *", "p_self"] + ] + }, + { + "name": "godot_array_sort", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"] + ] + }, + { + "name": "godot_array_sort_custom", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"], + ["godot_object *", "p_obj"], + ["const godot_string *", "p_func"] + ] + }, + { + "name": "godot_array_destroy", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"] + ] + }, + { + "name": "godot_dictionary_new", + "return_type": "void", + "arguments": [ + ["godot_dictionary *", "r_dest"] + ] + }, + { + "name": "godot_dictionary_new_copy", + "return_type": "void", + "arguments": [ + ["godot_dictionary *", "r_dest"], + ["const godot_dictionary *", "p_src"] + ] + }, + { + "name": "godot_dictionary_destroy", + "return_type": "void", + "arguments": [ + ["godot_dictionary *", "p_self"] + ] + }, + { + "name": "godot_dictionary_size", + "return_type": "godot_int", + "arguments": [ + ["const godot_dictionary *", "p_self"] + ] + }, + { + "name": "godot_dictionary_empty", + "return_type": "godot_bool", + "arguments": [ + ["const godot_dictionary *", "p_self"] + ] + }, + { + "name": "godot_dictionary_clear", + "return_type": "void", + "arguments": [ + ["godot_dictionary *", "p_self"] + ] + }, + { + "name": "godot_dictionary_has", + "return_type": "godot_bool", + "arguments": [ + ["const godot_dictionary *", "p_self"], + ["const godot_variant *", "p_key"] + ] + }, + { + "name": "godot_dictionary_has_all", + "return_type": "godot_bool", + "arguments": [ + ["const godot_dictionary *", "p_self"], + ["const godot_array *", "p_keys"] + ] + }, + { + "name": "godot_dictionary_erase", + "return_type": "void", + "arguments": [ + ["godot_dictionary *", "p_self"], + ["const godot_variant *", "p_key"] + ] + }, + { + "name": "godot_dictionary_hash", + "return_type": "godot_int", + "arguments": [ + ["const godot_dictionary *", "p_self"] + ] + }, + { + "name": "godot_dictionary_keys", + "return_type": "godot_array", + "arguments": [ + ["const godot_dictionary *", "p_self"] + ] + }, + { + "name": "godot_dictionary_values", + "return_type": "godot_array", + "arguments": [ + ["const godot_dictionary *", "p_self"] + ] + }, + { + "name": "godot_dictionary_get", + "return_type": "godot_variant", + "arguments": [ + ["const godot_dictionary *", "p_self"], + ["const godot_variant *", "p_key"] + ] + }, + { + "name": "godot_dictionary_set", + "return_type": "void", + "arguments": [ + ["godot_dictionary *", "p_self"], + ["const godot_variant *", "p_key"], + ["const godot_variant *", "p_value"] + ] + }, + { + "name": "godot_dictionary_operator_index", + "return_type": "godot_variant *", + "arguments": [ + ["godot_dictionary *", "p_self"], + ["const godot_variant *", "p_key"] + ] + }, + { + "name": "godot_dictionary_next", + "return_type": "godot_variant *", + "arguments": [ + ["const godot_dictionary *", "p_self"], + ["const godot_variant *", "p_key"] + ] + }, + { + "name": "godot_dictionary_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_dictionary *", "p_self"], + ["const godot_dictionary *", "p_b"] + ] + }, + { + "name": "godot_dictionary_to_json", + "return_type": "godot_string", + "arguments": [ + ["const godot_dictionary *", "p_self"] + ] + }, + { + "name": "godot_node_path_new", + "return_type": "void", + "arguments": [ + ["godot_node_path *", "r_dest"], + ["const godot_string *", "p_from"] + ] + }, + { + "name": "godot_node_path_new_copy", + "return_type": "void", + "arguments": [ + ["godot_node_path *", "r_dest"], + ["const godot_node_path *", "p_src"] + ] + }, + { + "name": "godot_node_path_destroy", + "return_type": "void", + "arguments": [ + ["godot_node_path *", "p_self"] + ] + }, + { + "name": "godot_node_path_as_string", + "return_type": "godot_string", + "arguments": [ + ["const godot_node_path *", "p_self"] + ] + }, + { + "name": "godot_node_path_is_absolute", + "return_type": "godot_bool", + "arguments": [ + ["const godot_node_path *", "p_self"] + ] + }, + { + "name": "godot_node_path_get_name_count", + "return_type": "godot_int", + "arguments": [ + ["const godot_node_path *", "p_self"] + ] + }, + { + "name": "godot_node_path_get_name", + "return_type": "godot_string", + "arguments": [ + ["const godot_node_path *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_node_path_get_subname_count", + "return_type": "godot_int", + "arguments": [ + ["const godot_node_path *", "p_self"] + ] + }, + { + "name": "godot_node_path_get_subname", + "return_type": "godot_string", + "arguments": [ + ["const godot_node_path *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_node_path_get_property", + "return_type": "godot_string", + "arguments": [ + ["const godot_node_path *", "p_self"] + ] + }, + { + "name": "godot_node_path_is_empty", + "return_type": "godot_bool", + "arguments": [ + ["const godot_node_path *", "p_self"] + ] + }, + { + "name": "godot_node_path_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_node_path *", "p_self"], + ["const godot_node_path *", "p_b"] + ] + }, + { + "name": "godot_plane_new_with_reals", + "return_type": "void", + "arguments": [ + ["godot_plane *", "r_dest"], + ["const godot_real", "p_a"], + ["const godot_real", "p_b"], + ["const godot_real", "p_c"], + ["const godot_real", "p_d"] + ] + }, + { + "name": "godot_plane_new_with_vectors", + "return_type": "void", + "arguments": [ + ["godot_plane *", "r_dest"], + ["const godot_vector3 *", "p_v1"], + ["const godot_vector3 *", "p_v2"], + ["const godot_vector3 *", "p_v3"] + ] + }, + { + "name": "godot_plane_new_with_normal", + "return_type": "void", + "arguments": [ + ["godot_plane *", "r_dest"], + ["const godot_vector3 *", "p_normal"], + ["const godot_real", "p_d"] + ] + }, + { + "name": "godot_plane_as_string", + "return_type": "godot_string", + "arguments": [ + ["const godot_plane *", "p_self"] + ] + }, + { + "name": "godot_plane_normalized", + "return_type": "godot_plane", + "arguments": [ + ["const godot_plane *", "p_self"] + ] + }, + { + "name": "godot_plane_center", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_plane *", "p_self"] + ] + }, + { + "name": "godot_plane_get_any_point", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_plane *", "p_self"] + ] + }, + { + "name": "godot_plane_is_point_over", + "return_type": "godot_bool", + "arguments": [ + ["const godot_plane *", "p_self"], + ["const godot_vector3 *", "p_point"] + ] + }, + { + "name": "godot_plane_distance_to", + "return_type": "godot_real", + "arguments": [ + ["const godot_plane *", "p_self"], + ["const godot_vector3 *", "p_point"] + ] + }, + { + "name": "godot_plane_has_point", + "return_type": "godot_bool", + "arguments": [ + ["const godot_plane *", "p_self"], + ["const godot_vector3 *", "p_point"], + ["const godot_real", "p_epsilon"] + ] + }, + { + "name": "godot_plane_project", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_plane *", "p_self"], + ["const godot_vector3 *", "p_point"] + ] + }, + { + "name": "godot_plane_intersect_3", + "return_type": "godot_bool", + "arguments": [ + ["const godot_plane *", "p_self"], + ["godot_vector3 *", "r_dest"], + ["const godot_plane *", "p_b"], + ["const godot_plane *", "p_c"] + ] + }, + { + "name": "godot_plane_intersects_ray", + "return_type": "godot_bool", + "arguments": [ + ["const godot_plane *", "p_self"], + ["godot_vector3 *", "r_dest"], + ["const godot_vector3 *", "p_from"], + ["const godot_vector3 *", "p_dir"] + ] + }, + { + "name": "godot_plane_intersects_segment", + "return_type": "godot_bool", + "arguments": [ + ["const godot_plane *", "p_self"], + ["godot_vector3 *", "r_dest"], + ["const godot_vector3 *", "p_begin"], + ["const godot_vector3 *", "p_end"] + ] + }, + { + "name": "godot_plane_operator_neg", + "return_type": "godot_plane", + "arguments": [ + ["const godot_plane *", "p_self"] + ] + }, + { + "name": "godot_plane_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_plane *", "p_self"], + ["const godot_plane *", "p_b"] + ] + }, + { + "name": "godot_plane_set_normal", + "return_type": "void", + "arguments": [ + ["godot_plane *", "p_self"], + ["const godot_vector3 *", "p_normal"] + ] + }, + { + "name": "godot_plane_get_normal", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_plane *", "p_self"] + ] + }, + { + "name": "godot_plane_get_d", + "return_type": "godot_real", + "arguments": [ + ["const godot_plane *", "p_self"] + ] + }, + { + "name": "godot_plane_set_d", + "return_type": "void", + "arguments": [ + ["godot_plane *", "p_self"], + ["const godot_real", "p_d"] + ] + }, + { + "name": "godot_rect2_new_with_position_and_size", + "return_type": "void", + "arguments": [ + ["godot_rect2 *", "r_dest"], + ["const godot_vector2 *", "p_pos"], + ["const godot_vector2 *", "p_size"] + ] + }, + { + "name": "godot_rect2_new", + "return_type": "void", + "arguments": [ + ["godot_rect2 *", "r_dest"], + ["const godot_real", "p_x"], + ["const godot_real", "p_y"], + ["const godot_real", "p_width"], + ["const godot_real", "p_height"] + ] + }, + { + "name": "godot_rect2_as_string", + "return_type": "godot_string", + "arguments": [ + ["const godot_rect2 *", "p_self"] + ] + }, + { + "name": "godot_rect2_get_area", + "return_type": "godot_real", + "arguments": [ + ["const godot_rect2 *", "p_self"] + ] + }, + { + "name": "godot_rect2_intersects", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rect2 *", "p_self"], + ["const godot_rect2 *", "p_b"] + ] + }, + { + "name": "godot_rect2_encloses", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rect2 *", "p_self"], + ["const godot_rect2 *", "p_b"] + ] + }, + { + "name": "godot_rect2_has_no_area", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rect2 *", "p_self"] + ] + }, + { + "name": "godot_rect2_clip", + "return_type": "godot_rect2", + "arguments": [ + ["const godot_rect2 *", "p_self"], + ["const godot_rect2 *", "p_b"] + ] + }, + { + "name": "godot_rect2_merge", + "return_type": "godot_rect2", + "arguments": [ + ["const godot_rect2 *", "p_self"], + ["const godot_rect2 *", "p_b"] + ] + }, + { + "name": "godot_rect2_has_point", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rect2 *", "p_self"], + ["const godot_vector2 *", "p_point"] + ] + }, + { + "name": "godot_rect2_grow", + "return_type": "godot_rect2", + "arguments": [ + ["const godot_rect2 *", "p_self"], + ["const godot_real", "p_by"] + ] + }, + { + "name": "godot_rect2_expand", + "return_type": "godot_rect2", + "arguments": [ + ["const godot_rect2 *", "p_self"], + ["const godot_vector2 *", "p_to"] + ] + }, + { + "name": "godot_rect2_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rect2 *", "p_self"], + ["const godot_rect2 *", "p_b"] + ] + }, + { + "name": "godot_rect2_get_position", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_rect2 *", "p_self"] + ] + }, + { + "name": "godot_rect2_get_size", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_rect2 *", "p_self"] + ] + }, + { + "name": "godot_rect2_set_position", + "return_type": "void", + "arguments": [ + ["godot_rect2 *", "p_self"], + ["const godot_vector2 *", "p_pos"] + ] + }, + { + "name": "godot_rect2_set_size", + "return_type": "void", + "arguments": [ + ["godot_rect2 *", "p_self"], + ["const godot_vector2 *", "p_size"] + ] + }, + { + "name": "godot_rect3_new", + "return_type": "void", + "arguments": [ + ["godot_rect3 *", "r_dest"], + ["const godot_vector3 *", "p_pos"], + ["const godot_vector3 *", "p_size"] + ] + }, + { + "name": "godot_rect3_get_position", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_rect3 *", "p_self"] + ] + }, + { + "name": "godot_rect3_set_position", + "return_type": "void", + "arguments": [ + ["const godot_rect3 *", "p_self"], + ["const godot_vector3 *", "p_v"] + ] + }, + { + "name": "godot_rect3_get_size", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_rect3 *", "p_self"] + ] + }, + { + "name": "godot_rect3_set_size", + "return_type": "void", + "arguments": [ + ["const godot_rect3 *", "p_self"], + ["const godot_vector3 *", "p_v"] + ] + }, + { + "name": "godot_rect3_as_string", + "return_type": "godot_string", + "arguments": [ + ["const godot_rect3 *", "p_self"] + ] + }, + { + "name": "godot_rect3_get_area", + "return_type": "godot_real", + "arguments": [ + ["const godot_rect3 *", "p_self"] + ] + }, + { + "name": "godot_rect3_has_no_area", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rect3 *", "p_self"] + ] + }, + { + "name": "godot_rect3_has_no_surface", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rect3 *", "p_self"] + ] + }, + { + "name": "godot_rect3_intersects", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rect3 *", "p_self"], + ["const godot_rect3 *", "p_with"] + ] + }, + { + "name": "godot_rect3_encloses", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rect3 *", "p_self"], + ["const godot_rect3 *", "p_with"] + ] + }, + { + "name": "godot_rect3_merge", + "return_type": "godot_rect3", + "arguments": [ + ["const godot_rect3 *", "p_self"], + ["const godot_rect3 *", "p_with"] + ] + }, + { + "name": "godot_rect3_intersection", + "return_type": "godot_rect3", + "arguments": [ + ["const godot_rect3 *", "p_self"], + ["const godot_rect3 *", "p_with"] + ] + }, + { + "name": "godot_rect3_intersects_plane", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rect3 *", "p_self"], + ["const godot_plane *", "p_plane"] + ] + }, + { + "name": "godot_rect3_intersects_segment", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rect3 *", "p_self"], + ["const godot_vector3 *", "p_from"], + ["const godot_vector3 *", "p_to"] + ] + }, + { + "name": "godot_rect3_has_point", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rect3 *", "p_self"], + ["const godot_vector3 *", "p_point"] + ] + }, + { + "name": "godot_rect3_get_support", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_rect3 *", "p_self"], + ["const godot_vector3 *", "p_dir"] + ] + }, + { + "name": "godot_rect3_get_longest_axis", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_rect3 *", "p_self"] + ] + }, + { + "name": "godot_rect3_get_longest_axis_index", + "return_type": "godot_int", + "arguments": [ + ["const godot_rect3 *", "p_self"] + ] + }, + { + "name": "godot_rect3_get_longest_axis_size", + "return_type": "godot_real", + "arguments": [ + ["const godot_rect3 *", "p_self"] + ] + }, + { + "name": "godot_rect3_get_shortest_axis", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_rect3 *", "p_self"] + ] + }, + { + "name": "godot_rect3_get_shortest_axis_index", + "return_type": "godot_int", + "arguments": [ + ["const godot_rect3 *", "p_self"] + ] + }, + { + "name": "godot_rect3_get_shortest_axis_size", + "return_type": "godot_real", + "arguments": [ + ["const godot_rect3 *", "p_self"] + ] + }, + { + "name": "godot_rect3_expand", + "return_type": "godot_rect3", + "arguments": [ + ["const godot_rect3 *", "p_self"], + ["const godot_vector3 *", "p_to_point"] + ] + }, + { + "name": "godot_rect3_grow", + "return_type": "godot_rect3", + "arguments": [ + ["const godot_rect3 *", "p_self"], + ["const godot_real", "p_by"] + ] + }, + { + "name": "godot_rect3_get_endpoint", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_rect3 *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_rect3_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rect3 *", "p_self"], + ["const godot_rect3 *", "p_b"] + ] + }, + { + "name": "godot_rid_new", + "return_type": "void", + "arguments": [ + ["godot_rid *", "r_dest"] + ] + }, + { + "name": "godot_rid_get_id", + "return_type": "godot_int", + "arguments": [ + ["const godot_rid *", "p_self"] + ] + }, + { + "name": "godot_rid_new_with_resource", + "return_type": "void", + "arguments": [ + ["godot_rid *", "r_dest"], + ["const godot_object *", "p_from"] + ] + }, + { + "name": "godot_rid_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rid *", "p_self"], + ["const godot_rid *", "p_b"] + ] + }, + { + "name": "godot_rid_operator_less", + "return_type": "godot_bool", + "arguments": [ + ["const godot_rid *", "p_self"], + ["const godot_rid *", "p_b"] + ] + }, + { + "name": "godot_transform_new_with_axis_origin", + "return_type": "void", + "arguments": [ + ["godot_transform *", "r_dest"], + ["const godot_vector3 *", "p_x_axis"], + ["const godot_vector3 *", "p_y_axis"], + ["const godot_vector3 *", "p_z_axis"], + ["const godot_vector3 *", "p_origin"] + ] + }, + { + "name": "godot_transform_new", + "return_type": "void", + "arguments": [ + ["godot_transform *", "r_dest"], + ["const godot_basis *", "p_basis"], + ["const godot_vector3 *", "p_origin"] + ] + }, + { + "name": "godot_transform_get_basis", + "return_type": "godot_basis", + "arguments": [ + ["const godot_transform *", "p_self"] + ] + }, + { + "name": "godot_transform_set_basis", + "return_type": "void", + "arguments": [ + ["godot_transform *", "p_self"], + ["godot_basis *", "p_v"] + ] + }, + { + "name": "godot_transform_get_origin", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_transform *", "p_self"] + ] + }, + { + "name": "godot_transform_set_origin", + "return_type": "void", + "arguments": [ + ["godot_transform *", "p_self"], + ["godot_vector3 *", "p_v"] + ] + }, + { + "name": "godot_transform_as_string", + "return_type": "godot_string", + "arguments": [ + ["const godot_transform *", "p_self"] + ] + }, + { + "name": "godot_transform_inverse", + "return_type": "godot_transform", + "arguments": [ + ["const godot_transform *", "p_self"] + ] + }, + { + "name": "godot_transform_affine_inverse", + "return_type": "godot_transform", + "arguments": [ + ["const godot_transform *", "p_self"] + ] + }, + { + "name": "godot_transform_orthonormalized", + "return_type": "godot_transform", + "arguments": [ + ["const godot_transform *", "p_self"] + ] + }, + { + "name": "godot_transform_rotated", + "return_type": "godot_transform", + "arguments": [ + ["const godot_transform *", "p_self"], + ["const godot_vector3 *", "p_axis"], + ["const godot_real", "p_phi"] + ] + }, + { + "name": "godot_transform_scaled", + "return_type": "godot_transform", + "arguments": [ + ["const godot_transform *", "p_self"], + ["const godot_vector3 *", "p_scale"] + ] + }, + { + "name": "godot_transform_translated", + "return_type": "godot_transform", + "arguments": [ + ["const godot_transform *", "p_self"], + ["const godot_vector3 *", "p_ofs"] + ] + }, + { + "name": "godot_transform_looking_at", + "return_type": "godot_transform", + "arguments": [ + ["const godot_transform *", "p_self"], + ["const godot_vector3 *", "p_target"], + ["const godot_vector3 *", "p_up"] + ] + }, + { + "name": "godot_transform_xform_plane", + "return_type": "godot_plane", + "arguments": [ + ["const godot_transform *", "p_self"], + ["const godot_plane *", "p_v"] + ] + }, + { + "name": "godot_transform_xform_inv_plane", + "return_type": "godot_plane", + "arguments": [ + ["const godot_transform *", "p_self"], + ["const godot_plane *", "p_v"] + ] + }, + { + "name": "godot_transform_new_identity", + "return_type": "void", + "arguments": [ + ["godot_transform *", "r_dest"] + ] + }, + { + "name": "godot_transform_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_transform *", "p_self"], + ["const godot_transform *", "p_b"] + ] + }, + { + "name": "godot_transform_operator_multiply", + "return_type": "godot_transform", + "arguments": [ + ["const godot_transform *", "p_self"], + ["const godot_transform *", "p_b"] + ] + }, + { + "name": "godot_transform_xform_vector3", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_transform *", "p_self"], + ["const godot_vector3 *", "p_v"] + ] + }, + { + "name": "godot_transform_xform_inv_vector3", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_transform *", "p_self"], + ["const godot_vector3 *", "p_v"] + ] + }, + { + "name": "godot_transform_xform_rect3", + "return_type": "godot_rect3", + "arguments": [ + ["const godot_transform *", "p_self"], + ["const godot_rect3 *", "p_v"] + ] + }, + { + "name": "godot_transform_xform_inv_rect3", + "return_type": "godot_rect3", + "arguments": [ + ["const godot_transform *", "p_self"], + ["const godot_rect3 *", "p_v"] + ] + }, + { + "name": "godot_transform2d_new", + "return_type": "void", + "arguments": [ + ["godot_transform2d *", "r_dest"], + ["const godot_real", "p_rot"], + ["const godot_vector2 *", "p_pos"] + ] + }, + { + "name": "godot_transform2d_new_axis_origin", + "return_type": "void", + "arguments": [ + ["godot_transform2d *", "r_dest"], + ["const godot_vector2 *", "p_x_axis"], + ["const godot_vector2 *", "p_y_axis"], + ["const godot_vector2 *", "p_origin"] + ] + }, + { + "name": "godot_transform2d_as_string", + "return_type": "godot_string", + "arguments": [ + ["const godot_transform2d *", "p_self"] + ] + }, + { + "name": "godot_transform2d_inverse", + "return_type": "godot_transform2d", + "arguments": [ + ["const godot_transform2d *", "p_self"] + ] + }, + { + "name": "godot_transform2d_affine_inverse", + "return_type": "godot_transform2d", + "arguments": [ + ["const godot_transform2d *", "p_self"] + ] + }, + { + "name": "godot_transform2d_get_rotation", + "return_type": "godot_real", + "arguments": [ + ["const godot_transform2d *", "p_self"] + ] + }, + { + "name": "godot_transform2d_get_origin", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_transform2d *", "p_self"] + ] + }, + { + "name": "godot_transform2d_get_scale", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_transform2d *", "p_self"] + ] + }, + { + "name": "godot_transform2d_orthonormalized", + "return_type": "godot_transform2d", + "arguments": [ + ["const godot_transform2d *", "p_self"] + ] + }, + { + "name": "godot_transform2d_rotated", + "return_type": "godot_transform2d", + "arguments": [ + ["const godot_transform2d *", "p_self"], + ["const godot_real", "p_phi"] + ] + }, + { + "name": "godot_transform2d_scaled", + "return_type": "godot_transform2d", + "arguments": [ + ["const godot_transform2d *", "p_self"], + ["const godot_vector2 *", "p_scale"] + ] + }, + { + "name": "godot_transform2d_translated", + "return_type": "godot_transform2d", + "arguments": [ + ["const godot_transform2d *", "p_self"], + ["const godot_vector2 *", "p_offset"] + ] + }, + { + "name": "godot_transform2d_xform_vector2", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_transform2d *", "p_self"], + ["const godot_vector2 *", "p_v"] + ] + }, + { + "name": "godot_transform2d_xform_inv_vector2", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_transform2d *", "p_self"], + ["const godot_vector2 *", "p_v"] + ] + }, + { + "name": "godot_transform2d_basis_xform_vector2", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_transform2d *", "p_self"], + ["const godot_vector2 *", "p_v"] + ] + }, + { + "name": "godot_transform2d_basis_xform_inv_vector2", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_transform2d *", "p_self"], + ["const godot_vector2 *", "p_v"] + ] + }, + { + "name": "godot_transform2d_interpolate_with", + "return_type": "godot_transform2d", + "arguments": [ + ["const godot_transform2d *", "p_self"], + ["const godot_transform2d *", "p_m"], + ["const godot_real", "p_c"] + ] + }, + { + "name": "godot_transform2d_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_transform2d *", "p_self"], + ["const godot_transform2d *", "p_b"] + ] + }, + { + "name": "godot_transform2d_operator_multiply", + "return_type": "godot_transform2d", + "arguments": [ + ["const godot_transform2d *", "p_self"], + ["const godot_transform2d *", "p_b"] + ] + }, + { + "name": "godot_transform2d_new_identity", + "return_type": "void", + "arguments": [ + ["godot_transform2d *", "r_dest"] + ] + }, + { + "name": "godot_transform2d_xform_rect2", + "return_type": "godot_rect2", + "arguments": [ + ["const godot_transform2d *", "p_self"], + ["const godot_rect2 *", "p_v"] + ] + }, + { + "name": "godot_transform2d_xform_inv_rect2", + "return_type": "godot_rect2", + "arguments": [ + ["const godot_transform2d *", "p_self"], + ["const godot_rect2 *", "p_v"] + ] + }, + { + "name": "godot_variant_get_type", + "return_type": "godot_variant_type", + "arguments": [ + ["const godot_variant *", "p_v"] + ] + }, + { + "name": "godot_variant_new_copy", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_variant *", "p_src"] + ] + }, + { + "name": "godot_variant_new_nil", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"] + ] + }, + { + "name": "godot_variant_new_bool", + "return_type": "void", + "arguments": [ + ["godot_variant *", "p_v"], + ["const godot_bool", "p_b"] + ] + }, + { + "name": "godot_variant_new_uint", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const uint64_t", "p_i"] + ] + }, + { + "name": "godot_variant_new_int", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const int64_t", "p_i"] + ] + }, + { + "name": "godot_variant_new_real", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const double", "p_r"] + ] + }, + { + "name": "godot_variant_new_string", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_string *", "p_s"] + ] + }, + { + "name": "godot_variant_new_vector2", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_vector2 *", "p_v2"] + ] + }, + { + "name": "godot_variant_new_rect2", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_rect2 *", "p_rect2"] + ] + }, + { + "name": "godot_variant_new_vector3", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_vector3 *", "p_v3"] + ] + }, + { + "name": "godot_variant_new_transform2d", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_transform2d *", "p_t2d"] + ] + }, + { + "name": "godot_variant_new_plane", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_plane *", "p_plane"] + ] + }, + { + "name": "godot_variant_new_quat", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_quat *", "p_quat"] + ] + }, + { + "name": "godot_variant_new_rect3", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_rect3 *", "p_rect3"] + ] + }, + { + "name": "godot_variant_new_basis", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_basis *", "p_basis"] + ] + }, + { + "name": "godot_variant_new_transform", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_transform *", "p_trans"] + ] + }, + { + "name": "godot_variant_new_color", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_color *", "p_color"] + ] + }, + { + "name": "godot_variant_new_node_path", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_node_path *", "p_np"] + ] + }, + { + "name": "godot_variant_new_rid", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_rid *", "p_rid"] + ] + }, + { + "name": "godot_variant_new_object", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_object *", "p_obj"] + ] + }, + { + "name": "godot_variant_new_dictionary", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_dictionary *", "p_dict"] + ] + }, + { + "name": "godot_variant_new_array", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_array *", "p_arr"] + ] + }, + { + "name": "godot_variant_new_pool_byte_array", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_pool_byte_array *", "p_pba"] + ] + }, + { + "name": "godot_variant_new_pool_int_array", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_pool_int_array *", "p_pia"] + ] + }, + { + "name": "godot_variant_new_pool_real_array", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_pool_real_array *", "p_pra"] + ] + }, + { + "name": "godot_variant_new_pool_string_array", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_pool_string_array *", "p_psa"] + ] + }, + { + "name": "godot_variant_new_pool_vector2_array", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_pool_vector2_array *", "p_pv2a"] + ] + }, + { + "name": "godot_variant_new_pool_vector3_array", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_pool_vector3_array *", "p_pv3a"] + ] + }, + { + "name": "godot_variant_new_pool_color_array", + "return_type": "void", + "arguments": [ + ["godot_variant *", "r_dest"], + ["const godot_pool_color_array *", "p_pca"] + ] + }, + { + "name": "godot_variant_as_bool", + "return_type": "godot_bool", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_uint", + "return_type": "uint64_t", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_int", + "return_type": "int64_t", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_real", + "return_type": "double", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_string", + "return_type": "godot_string", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_vector2", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_rect2", + "return_type": "godot_rect2", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_vector3", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_transform2d", + "return_type": "godot_transform2d", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_plane", + "return_type": "godot_plane", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_quat", + "return_type": "godot_quat", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_rect3", + "return_type": "godot_rect3", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_basis", + "return_type": "godot_basis", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_transform", + "return_type": "godot_transform", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_color", + "return_type": "godot_color", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_node_path", + "return_type": "godot_node_path", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_rid", + "return_type": "godot_rid", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_object", + "return_type": "godot_object *", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_dictionary", + "return_type": "godot_dictionary", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_array", + "return_type": "godot_array", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_pool_byte_array", + "return_type": "godot_pool_byte_array", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_pool_int_array", + "return_type": "godot_pool_int_array", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_pool_real_array", + "return_type": "godot_pool_real_array", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_pool_string_array", + "return_type": "godot_pool_string_array", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_pool_vector2_array", + "return_type": "godot_pool_vector2_array", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_pool_vector3_array", + "return_type": "godot_pool_vector3_array", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_as_pool_color_array", + "return_type": "godot_pool_color_array", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_call", + "return_type": "godot_variant", + "arguments": [ + ["godot_variant *", "p_self"], + ["const godot_string *", "p_method"], + ["const godot_variant **", "p_args"], + ["const godot_int", "p_argcount"], + ["godot_variant_call_error *", "r_error"] + ] + }, + { + "name": "godot_variant_has_method", + "return_type": "godot_bool", + "arguments": [ + ["const godot_variant *", "p_self"], + ["const godot_string *", "p_method"] + ] + }, + { + "name": "godot_variant_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_variant *", "p_self"], + ["const godot_variant *", "p_other"] + ] + }, + { + "name": "godot_variant_operator_less", + "return_type": "godot_bool", + "arguments": [ + ["const godot_variant *", "p_self"], + ["const godot_variant *", "p_other"] + ] + }, + { + "name": "godot_variant_hash_compare", + "return_type": "godot_bool", + "arguments": [ + ["const godot_variant *", "p_self"], + ["const godot_variant *", "p_other"] + ] + }, + { + "name": "godot_variant_booleanize", + "return_type": "godot_bool", + "arguments": [ + ["const godot_variant *", "p_self"] + ] + }, + { + "name": "godot_variant_destroy", + "return_type": "void", + "arguments": [ + ["godot_variant *", "p_self"] + ] + }, + { + "name": "godot_string_new", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"] + ] + }, + { + "name": "godot_string_new_copy", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const godot_string *", "p_src"] + ] + }, + { + "name": "godot_string_new_data", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char *", "p_contents"], + ["const int", "p_size"] + ] + }, + { + "name": "godot_string_new_unicode_data", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const wchar_t *", "p_contents"], + ["const int", "p_size"] + ] + }, + { + "name": "godot_string_get_data", + "return_type": "void", + "arguments": [ + ["const godot_string *", "p_self"], + ["char *", "p_dest"], + ["int *", "p_size"] + ] + }, + { + "name": "godot_string_operator_index", + "return_type": "wchar_t *", + "arguments": [ + ["godot_string *", "p_self"], + ["const godot_int", "p_idx"] + ] + }, + { + "name": "godot_string_c_str", + "return_type": "const char *", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_unicode_str", + "return_type": "const wchar_t *", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_b"] + ] + }, + { + "name": "godot_string_operator_less", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_b"] + ] + }, + { + "name": "godot_string_operator_plus", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_b"] + ] + }, + { + "name": "godot_string_length", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_begins_with", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_string"] + ] + }, + { + "name": "godot_string_begins_with_char_array", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"], + ["const char *", "p_char_array"] + ] + }, + { + "name": "godot_string_bigrams", + "return_type": "godot_array", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_chr", + "return_type": "godot_string", + "arguments": [ + ["wchar_t", "p_character"] + ] + }, + { + "name": "godot_string_ends_with", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_string"] + ] + }, + { + "name": "godot_string_find", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_string", "p_what"] + ] + }, + { + "name": "godot_string_find_from", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_string", "p_what"], + ["godot_int", "p_from"] + ] + }, + { + "name": "godot_string_findmk", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_array *", "p_keys"] + ] + }, + { + "name": "godot_string_findmk_from", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_array *", "p_keys"], + ["godot_int", "p_from"] + ] + }, + { + "name": "godot_string_findmk_from_in_place", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_array *", "p_keys"], + ["godot_int", "p_from"], + ["godot_int *", "r_key"] + ] + }, + { + "name": "godot_string_findn", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_string", "p_what"] + ] + }, + { + "name": "godot_string_findn_from", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_string", "p_what"], + ["godot_int", "p_from"] + ] + }, + { + "name": "godot_string_find_last", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_string", "p_what"] + ] + }, + { + "name": "godot_string_format", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_variant *", "p_values"] + ] + }, + { + "name": "godot_string_format_with_custom_placeholder", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_variant *", "p_values"], + ["const char *", "p_placeholder"] + ] + }, + { + "name": "godot_string_hex_encode_buffer", + "return_type": "godot_string", + "arguments": [ + ["const uint8_t *", "p_buffer"], + ["godot_int", "p_len"] + ] + }, + { + "name": "godot_string_hex_to_int", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_hex_to_int_without_prefix", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_insert", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_int", "p_at_pos"], + ["godot_string", "p_string"] + ] + }, + { + "name": "godot_string_is_numeric", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_is_subsequence_of", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_string"] + ] + }, + { + "name": "godot_string_is_subsequence_ofi", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_string"] + ] + }, + { + "name": "godot_string_lpad", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_int", "p_min_length"] + ] + }, + { + "name": "godot_string_lpad_with_custom_character", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_int", "p_min_length"], + ["const godot_string *", "p_character"] + ] + }, + { + "name": "godot_string_match", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_wildcard"] + ] + }, + { + "name": "godot_string_matchn", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_wildcard"] + ] + }, + { + "name": "godot_string_md5", + "return_type": "godot_string", + "arguments": [ + ["const uint8_t *", "p_md5"] + ] + }, + { + "name": "godot_string_num", + "return_type": "godot_string", + "arguments": [ + ["double", "p_num"] + ] + }, + { + "name": "godot_string_num_int64", + "return_type": "godot_string", + "arguments": [ + ["int64_t", "p_num"], + ["godot_int", "p_base"] + ] + }, + { + "name": "godot_string_num_int64_capitalized", + "return_type": "godot_string", + "arguments": [ + ["int64_t", "p_num"], + ["godot_int", "p_base"], + ["godot_bool", "p_capitalize_hex"] + ] + }, + { + "name": "godot_string_num_real", + "return_type": "godot_string", + "arguments": [ + ["double", "p_num"] + ] + }, + { + "name": "godot_string_num_scientific", + "return_type": "godot_string", + "arguments": [ + ["double", "p_num"] + ] + }, + { + "name": "godot_string_num_with_decimals", + "return_type": "godot_string", + "arguments": [ + ["double", "p_num"], + ["godot_int", "p_decimals"] + ] + }, + { + "name": "godot_string_pad_decimals", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_int", "p_digits"] + ] + }, + { + "name": "godot_string_pad_zeros", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_int", "p_digits"] + ] + }, + { + "name": "godot_string_replace_first", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_string", "p_key"], + ["godot_string", "p_with"] + ] + }, + { + "name": "godot_string_replace", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_string", "p_key"], + ["godot_string", "p_with"] + ] + }, + { + "name": "godot_string_replacen", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_string", "p_key"], + ["godot_string", "p_with"] + ] + }, + { + "name": "godot_string_rfind", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_string", "p_what"] + ] + }, + { + "name": "godot_string_rfindn", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_string", "p_what"] + ] + }, + { + "name": "godot_string_rfind_from", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_string", "p_what"], + ["godot_int", "p_from"] + ] + }, + { + "name": "godot_string_rfindn_from", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_string", "p_what"], + ["godot_int", "p_from"] + ] + }, + { + "name": "godot_string_rpad", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_int", "p_min_length"] + ] + }, + { + "name": "godot_string_rpad_with_custom_character", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_int", "p_min_length"], + ["const godot_string *", "p_character"] + ] + }, + { + "name": "godot_string_similarity", + "return_type": "godot_real", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_string"] + ] + }, + { + "name": "godot_string_sprintf", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_array *", "p_values"], + ["godot_bool *", "p_error"] + ] + }, + { + "name": "godot_string_substr", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_int", "p_from"], + ["godot_int", "p_chars"] + ] + }, + { + "name": "godot_string_to_double", + "return_type": "double", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_to_float", + "return_type": "godot_real", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_to_int", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_camelcase_to_underscore", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_camelcase_to_underscore_lowercased", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_capitalize", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_char_to_double", + "return_type": "double", + "arguments": [ + ["const char *", "p_what"] + ] + }, + { + "name": "godot_string_char_to_int", + "return_type": "godot_int", + "arguments": [ + ["const char *", "p_what"] + ] + }, + { + "name": "godot_string_wchar_to_int", + "return_type": "int64_t", + "arguments": [ + ["const wchar_t *", "p_str"] + ] + }, + { + "name": "godot_string_char_to_int_with_len", + "return_type": "godot_int", + "arguments": [ + ["const char *", "p_what"], + ["godot_int", "p_len"] + ] + }, + { + "name": "godot_string_char_to_int64_with_len", + "return_type": "int64_t", + "arguments": [ + ["const wchar_t *", "p_str"], + ["int", "p_len"] + ] + }, + { + "name": "godot_string_hex_to_int64", + "return_type": "int64_t", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_hex_to_int64_with_prefix", + "return_type": "int64_t", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_to_int64", + "return_type": "int64_t", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_unicode_char_to_double", + "return_type": "double", + "arguments": [ + ["const wchar_t *", "p_str"], + ["const wchar_t **", "r_end"] + ] + }, + { + "name": "godot_string_get_slice_count", + "return_type": "godot_int", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_string", "p_splitter"] + ] + }, + { + "name": "godot_string_get_slice", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_string", "p_splitter"], + ["godot_int", "p_slice"] + ] + }, + { + "name": "godot_string_get_slicec", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["wchar_t", "p_splitter"], + ["godot_int", "p_slice"] + ] + }, + { + "name": "godot_string_split", + "return_type": "godot_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_splitter"] + ] + }, + { + "name": "godot_string_split_allow_empty", + "return_type": "godot_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_splitter"] + ] + }, + { + "name": "godot_string_split_floats", + "return_type": "godot_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_splitter"] + ] + }, + { + "name": "godot_string_split_floats_allows_empty", + "return_type": "godot_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_splitter"] + ] + }, + { + "name": "godot_string_split_floats_mk", + "return_type": "godot_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_array *", "p_splitters"] + ] + }, + { + "name": "godot_string_split_floats_mk_allows_empty", + "return_type": "godot_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_array *", "p_splitters"] + ] + }, + { + "name": "godot_string_split_ints", + "return_type": "godot_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_splitter"] + ] + }, + { + "name": "godot_string_split_ints_allows_empty", + "return_type": "godot_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_splitter"] + ] + }, + { + "name": "godot_string_split_ints_mk", + "return_type": "godot_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_array *", "p_splitters"] + ] + }, + { + "name": "godot_string_split_ints_mk_allows_empty", + "return_type": "godot_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_array *", "p_splitters"] + ] + }, + { + "name": "godot_string_split_spaces", + "return_type": "godot_array", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_char_lowercase", + "return_type": "wchar_t", + "arguments": [ + ["wchar_t", "p_char"] + ] + }, + { + "name": "godot_string_char_uppercase", + "return_type": "wchar_t", + "arguments": [ + ["wchar_t", "p_char"] + ] + }, + { + "name": "godot_string_to_lower", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_to_upper", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_get_basename", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_get_extension", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_left", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_int", "p_pos"] + ] + }, + { + "name": "godot_string_ord_at", + "return_type": "wchar_t", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_int", "p_idx"] + ] + }, + { + "name": "godot_string_plus_file", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_file"] + ] + }, + { + "name": "godot_string_right", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_int", "p_pos"] + ] + }, + { + "name": "godot_string_strip_edges", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_bool", "p_left"], + ["godot_bool", "p_right"] + ] + }, + { + "name": "godot_string_strip_escapes", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_erase", + "return_type": "void", + "arguments": [ + ["godot_string *", "p_self"], + ["godot_int", "p_pos"], + ["godot_int", "p_chars"] + ] + }, + { + "name": "godot_string_ascii", + "return_type": "void", + "arguments": [ + ["godot_string *", "p_self"], + ["char *", "result"] + ] + }, + { + "name": "godot_string_ascii_extended", + "return_type": "void", + "arguments": [ + ["godot_string *", "p_self"], + ["char *", "result"] + ] + }, + { + "name": "godot_string_utf8", + "return_type": "void", + "arguments": [ + ["godot_string *", "p_self"], + ["char *", "result"] + ] + }, + { + "name": "godot_string_parse_utf8", + "return_type": "godot_bool", + "arguments": [ + ["godot_string *", "p_self"], + ["const char *", "p_utf8"] + ] + }, + { + "name": "godot_string_parse_utf8_with_len", + "return_type": "godot_bool", + "arguments": [ + ["godot_string *", "p_self"], + ["const char *", "p_utf8"], + ["godot_int", "p_len"] + ] + }, + { + "name": "godot_string_chars_to_utf8", + "return_type": "godot_string", + "arguments": [ + ["const char *", "p_utf8"] + ] + }, + { + "name": "godot_string_chars_to_utf8_with_len", + "return_type": "godot_string", + "arguments": [ + ["const char *", "p_utf8"], + ["godot_int", "p_len"] + ] + }, + { + "name": "godot_string_hash", + "return_type": "uint32_t", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_hash64", + "return_type": "uint64_t", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_hash_chars", + "return_type": "uint32_t", + "arguments": [ + ["const char *", "p_cstr"] + ] + }, + { + "name": "godot_string_hash_chars_with_len", + "return_type": "uint32_t", + "arguments": [ + ["const char *", "p_cstr"], + ["godot_int", "p_len"] + ] + }, + { + "name": "godot_string_hash_utf8_chars", + "return_type": "uint32_t", + "arguments": [ + ["const wchar_t *", "p_str"] + ] + }, + { + "name": "godot_string_hash_utf8_chars_with_len", + "return_type": "uint32_t", + "arguments": [ + ["const wchar_t *", "p_str"], + ["godot_int", "p_len"] + ] + }, + { + "name": "godot_string_md5_buffer", + "return_type": "godot_pool_byte_array", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_md5_text", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_sha256_buffer", + "return_type": "godot_pool_byte_array", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_sha256_text", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_empty", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_get_base_dir", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_get_file", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_humanize_size", + "return_type": "godot_string", + "arguments": [ + ["size_t", "p_size"] + ] + }, + { + "name": "godot_string_is_abs_path", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_is_rel_path", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_is_resource_file", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_path_to", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_path"] + ] + }, + { + "name": "godot_string_path_to_file", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_path"] + ] + }, + { + "name": "godot_string_simplify_path", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_c_escape", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_c_escape_multiline", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_c_unescape", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_http_escape", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_http_unescape", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_json_escape", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_word_wrap", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_int", "p_chars_per_line"] + ] + }, + { + "name": "godot_string_xml_escape", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_xml_escape_with_quotes", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_xml_unescape", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_percent_decode", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_percent_encode", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_is_valid_float", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_is_valid_hex_number", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_bool", "p_with_prefix"] + ] + }, + { + "name": "godot_string_is_valid_html_color", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_is_valid_identifier", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_is_valid_integer", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_is_valid_ip_address", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_destroy", + "return_type": "void", + "arguments": [ + ["godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_name_new", + "return_type": "void", + "arguments": [ + ["godot_string_name *", "r_dest"], + ["const godot_string *", "p_name"] + ] + }, + { + "name": "godot_string_name_new_data", + "return_type": "void", + "arguments": [ + ["godot_string_name *", "r_dest"], + ["const char *", "p_name"] + ] + }, + { + "name": "godot_string_name_get_name", + "return_type": "godot_string", + "arguments": [ + ["const godot_string_name *", "p_self"] + ] + }, + { + "name": "godot_string_name_get_hash", + "return_type": "uint32_t", + "arguments": [ + ["const godot_string_name *", "p_self"] + ] + }, + { + "name": "godot_string_name_get_data_unique_pointer", + "return_type": "const void *", + "arguments": [ + ["const godot_string_name *", "p_self"] + ] + }, + { + "name": "godot_string_name_operator_equal", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string_name *", "p_self"], + ["const godot_string_name *", "p_other"] + ] + }, + { + "name": "godot_string_name_operator_less", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string_name *", "p_self"], + ["const godot_string_name *", "p_other"] + ] + }, + { + "name": "godot_string_name_destroy", + "return_type": "void", + "arguments": [ + ["godot_string_name *", "p_self"] + ] + }, + { + "name": "godot_object_destroy", + "return_type": "void", + "arguments": [ + ["godot_object *", "p_o"] + ] + }, + { + "name": "godot_global_get_singleton", + "return_type": "godot_object *", + "arguments": [ + ["char *", "p_name"] + ] + }, + { + "name": "godot_method_bind_get_method", + "return_type": "godot_method_bind *", + "arguments": [ + ["const char *", "p_classname"], + ["const char *", "p_methodname"] + ] + }, + { + "name": "godot_method_bind_ptrcall", + "return_type": "void", + "arguments": [ + ["godot_method_bind *", "p_method_bind"], + ["godot_object *", "p_instance"], + ["const void **", "p_args"], + ["void *", "p_ret"] + ] + }, + { + "name": "godot_method_bind_call", + "return_type": "godot_variant", + "arguments": [ + ["godot_method_bind *", "p_method_bind"], + ["godot_object *", "p_instance"], + ["const godot_variant **", "p_args"], + ["const int", "p_arg_count"], + ["godot_variant_call_error *", "p_call_error"] + ] + }, + { + "name": "godot_get_class_constructor", + "return_type": "godot_class_constructor", + "arguments": [ + ["const char *", "p_classname"] + ] + }, + { + "name": "godot_alloc", + "return_type": "void *", + "arguments": [ + ["int", "p_bytes"] + ] + }, + { + "name": "godot_realloc", + "return_type": "void *", + "arguments": [ + ["void *", "p_ptr"], + ["int", "p_bytes"] + ] + }, + { + "name": "godot_free", + "return_type": "void", + "arguments": [ + ["void *", "p_ptr"] + ] + }, + { + "name": "godot_print_error", + "return_type": "void", + "arguments": [ + ["const char *", "p_description"], + ["const char *", "p_function"], + ["const char *", "p_file"], + ["int", "p_line"] + ] + }, + { + "name": "godot_print_warning", + "return_type": "void", + "arguments": [ + ["const char *", "p_description"], + ["const char *", "p_function"], + ["const char *", "p_file"], + ["int", "p_line"] + ] + }, + { + "name": "godot_print", + "return_type": "void", + "arguments": [ + ["const godot_string *", "p_message"] + ] + }, + { + "name": "godot_nativescript_register_class", + "return_type": "void", + "arguments": [ + ["void *", "p_gdnative_handle"], + ["const char *", "p_name"], + ["const char *", "p_base"], + ["godot_instance_create_func", "p_create_func"], + ["godot_instance_destroy_func", "p_destroy_func"] + ] + }, + { + "name": "godot_nativescript_register_tool_class", + "return_type": "void", + "arguments": [ + ["void *", "p_gdnative_handle"], + ["const char *", "p_name"], + ["const char *", "p_base"], + ["godot_instance_create_func", "p_create_func"], + ["godot_instance_destroy_func", "p_destroy_func"] + ] + }, + { + "name": "godot_nativescript_register_method", + "return_type": "void", + "arguments": [ + ["void *", "p_gdnative_handle"], + ["const char *", "p_name"], + ["const char *", "p_function_name"], + ["godot_method_attributes", "p_attr"], + ["godot_instance_method", "p_method"] + ] + }, + { + "name": "godot_nativescript_register_property", + "return_type": "void", + "arguments": [ + ["void *", "p_gdnative_handle"], + ["const char *", "p_name"], + ["const char *", "p_path"], + ["godot_property_attributes *", "p_attr"], + ["godot_property_set_func", "p_set_func"], + ["godot_property_get_func", "p_get_func"] + ] + }, + { + "name": "godot_nativescript_register_signal", + "return_type": "void", + "arguments": [ + ["void *", "p_gdnative_handle"], + ["const char *", "p_name"], + ["const godot_signal *", "p_signal"] + ] + }, + { + "name": "godot_nativescript_get_userdata", + "return_type": "void *", + "arguments": [ + ["godot_object *", "p_instance"] + ] + }, + { + "name": "godot_arvr_register_interface", + "return_type": "void", + "arguments": [ + ["const godot_arvr_interface_gdnative *", "p_interface"] + ] + }, + { + "name": "godot_arvr_get_worldscale", + "return_type": "godot_real", + "arguments": [] + }, + { + "name": "godot_arvr_get_reference_frame", + "return_type": "godot_transform", + "arguments": [] + }, + { + "name": "godot_arvr_blit", + "return_type": "void", + "arguments": [ + ["int", "p_eye"], + ["godot_rid *", "p_render_target"], + ["godot_rect2 *", "p_screen_rect"] + ] + }, + { + "name": "godot_arvr_get_texid", + "return_type": "godot_int", + "arguments": [ + ["godot_rid *", "p_render_target"] + ] + }, + { + "name": "godot_arvr_add_controller", + "return_type": "godot_int", + "arguments": [ + ["char *", "p_device_name"], + ["godot_int", "p_hand"], + ["godot_bool", "p_tracks_orientation"], + ["godot_bool", "p_tracks_position"] + ] + }, + { + "name": "godot_arvr_remove_controller", + "return_type": "void", + "arguments": [ + ["godot_int", "p_controller_id"] + ] + }, + { + "name": "godot_arvr_set_controller_transform", + "return_type": "void", + "arguments": [ + ["godot_int", "p_controller_id"], + ["godot_transform *", "p_transform"], + ["godot_bool", "p_tracks_orientation"], + ["godot_bool", "p_tracks_position"] + ] + }, + { + "name": "godot_arvr_set_controller_button", + "return_type": "void", + "arguments": [ + ["godot_int", "p_controller_id"], + ["godot_int", "p_button"], + ["godot_bool", "p_is_pressed"] + ] + }, + { + "name": "godot_arvr_set_controller_axis", + "return_type": "void", + "arguments": [ + ["godot_int", "p_controller_id"], + ["godot_int", "p_exis"], + ["godot_real", "p_value"], + ["godot_bool", "p_can_be_negative"] + ] + } + ] +} diff --git a/modules/gdnative/godot/icon.png.import b/modules/gdnative/godot/icon.png.import deleted file mode 100644 index 27920124f9..0000000000 --- a/modules/gdnative/godot/icon.png.import +++ /dev/null @@ -1,23 +0,0 @@ -[remap] - -importer="texture" -type="StreamTexture" -path="res://.import/icon.png-aa47d037a37fb38b3b7e7828e4eec407.stex" - -[params] - -compress/mode=0 -compress/lossy_quality=0.7 -compress/hdr_mode=0 -compress/normal_map=0 -flags/repeat=0 -flags/filter=true -flags/mipmaps=false -flags/anisotropic=false -flags/srgb=2 -process/fix_alpha_border=true -process/premult_alpha=false -process/HDR_as_SRGB=false -stream=false -size_limit=0 -detect_3d=true diff --git a/modules/gdnative/godot/array.h b/modules/gdnative/include/gdnative/array.h index 434ce958c9..d0639589b7 100644 --- a/modules/gdnative/godot/array.h +++ b/modules/gdnative/include/gdnative/array.h @@ -37,7 +37,7 @@ extern "C" { #include <stdint.h> -#define GODOT_ARRAY_SIZE 8 +#define GODOT_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_ARRAY_TYPE_DEFINED @@ -46,10 +46,19 @@ typedef struct { } godot_array; #endif -#include <godot/pool_arrays.h> -#include <godot/variant.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/pool_arrays.h> +#include <gdnative/variant.h> -#include <godot/gdnative.h> +#include <gdnative/gdnative.h> + +#ifdef __cplusplus +extern "C" { +#endif void GDAPI godot_array_new(godot_array *r_dest); void GDAPI godot_array_new_copy(godot_array *r_dest, const godot_array *p_src); diff --git a/modules/gdnative/godot/basis.h b/modules/gdnative/include/gdnative/basis.h index d336bb9bc1..b86b1c17d8 100644 --- a/modules/gdnative/godot/basis.h +++ b/modules/gdnative/include/gdnative/basis.h @@ -45,9 +45,18 @@ typedef struct { } godot_basis; #endif -#include <godot/gdnative.h> -#include <godot/quat.h> -#include <godot/vector3.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/gdnative.h> +#include <gdnative/quat.h> +#include <gdnative/vector3.h> + +#ifdef __cplusplus +extern "C" { +#endif void GDAPI godot_basis_new_with_rows(godot_basis *r_dest, const godot_vector3 *p_x_axis, const godot_vector3 *p_y_axis, const godot_vector3 *p_z_axis); void GDAPI godot_basis_new_with_axis_and_angle(godot_basis *r_dest, const godot_vector3 *p_axis, const godot_real p_phi); @@ -67,12 +76,6 @@ godot_basis GDAPI godot_basis_rotated(const godot_basis *p_self, const godot_vec godot_basis GDAPI godot_basis_scaled(const godot_basis *p_self, const godot_vector3 *p_scale); -void GDAPI godot_basis_set_scale(godot_basis *p_self, const godot_vector3 *p_scale); - -void GDAPI godot_basis_set_rotation_euler(godot_basis *p_self, const godot_vector3 *p_euler); - -void GDAPI godot_basis_set_rotation_axis_angle(godot_basis *p_self, const godot_vector3 *p_axis, const godot_real p_angle); - godot_vector3 GDAPI godot_basis_get_scale(const godot_basis *p_self); godot_vector3 GDAPI godot_basis_get_euler(const godot_basis *p_self); diff --git a/modules/gdnative/godot/color.h b/modules/gdnative/include/gdnative/color.h index 5d550e40b3..857e86a738 100644 --- a/modules/gdnative/godot/color.h +++ b/modules/gdnative/include/gdnative/color.h @@ -45,8 +45,17 @@ typedef struct { } godot_color; #endif -#include <godot/gdnative.h> -#include <godot/string.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/gdnative.h> +#include <gdnative/string.h> + +#ifdef __cplusplus +extern "C" { +#endif void GDAPI godot_color_new_rgba(godot_color *r_dest, const godot_real p_r, const godot_real p_g, const godot_real p_b, const godot_real p_a); void GDAPI godot_color_new_rgb(godot_color *r_dest, const godot_real p_r, const godot_real p_g, const godot_real p_b); @@ -69,9 +78,9 @@ godot_real godot_color_get_v(const godot_color *p_self); godot_string GDAPI godot_color_as_string(const godot_color *p_self); -godot_int GDAPI godot_color_to_32(const godot_color *p_self); +godot_int GDAPI godot_color_to_rgba32(const godot_color *p_self); -godot_int GDAPI godot_color_to_ARGB32(const godot_color *p_self); +godot_int GDAPI godot_color_to_argb32(const godot_color *p_self); godot_real GDAPI godot_color_gray(const godot_color *p_self); diff --git a/modules/gdnative/godot/dictionary.h b/modules/gdnative/include/gdnative/dictionary.h index bbe40f23c3..e68d0fdc29 100644 --- a/modules/gdnative/godot/dictionary.h +++ b/modules/gdnative/include/gdnative/dictionary.h @@ -36,7 +36,7 @@ extern "C" { #include <stdint.h> -#define GODOT_DICTIONARY_SIZE 8 +#define GODOT_DICTIONARY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_DICTIONARY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_DICTIONARY_TYPE_DEFINED @@ -45,9 +45,18 @@ typedef struct { } godot_dictionary; #endif -#include <godot/array.h> -#include <godot/gdnative.h> -#include <godot/variant.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/array.h> +#include <gdnative/gdnative.h> +#include <gdnative/variant.h> + +#ifdef __cplusplus +extern "C" { +#endif void GDAPI godot_dictionary_new(godot_dictionary *r_dest); void GDAPI godot_dictionary_new_copy(godot_dictionary *r_dest, const godot_dictionary *p_src); diff --git a/modules/gdnative/godot/gdnative.h b/modules/gdnative/include/gdnative/gdnative.h index 8b289da1f5..25d45db306 100644 --- a/modules/gdnative/godot/gdnative.h +++ b/modules/gdnative/include/gdnative/gdnative.h @@ -34,23 +34,14 @@ extern "C" { #endif -#ifdef GDAPI_BUILT_IN -#define GDAPI_EXPORT -#endif - #ifdef _WIN32 -#if defined(GDAPI_EXPORT) -#define GDCALLINGCONV -#define GDAPI __declspec(dllexport) GDCALLINGCONV -#else #define GDCALLINGCONV -#define GDAPI __declspec(dllimport) GDCALLINGCONV -#endif +#define GDAPI GDCALLINGCONV #elif defined(__APPLE__) #include "TargetConditionals.h" #if TARGET_OS_IPHONE -#define GDCALLINGCONV -#define GDAPI +#define GDCALLINGCONV __attribute__((visibility("default"))) +#define GDAPI GDCALLINGCONV #elif TARGET_OS_MAC #define GDCALLINGCONV __attribute__((sysv_abi)) #define GDAPI GDCALLINGCONV @@ -103,7 +94,7 @@ typedef enum { GODOT_ERR_CANT_CONNECT, // (25) GODOT_ERR_CANT_RESOLVE, GODOT_ERR_CONNECTION_ERROR, - GODOT_ERR_CANT_AQUIRE_RESOURCE, + GODOT_ERR_CANT_ACQUIRE_RESOURCE, GODOT_ERR_CANT_FORK, GODOT_ERR_INVALID_DATA, ///< Data passed is invalid (30) GODOT_ERR_INVALID_PARAMETER, ///< Parameter passed is invalid @@ -146,100 +137,78 @@ typedef float godot_real; /////// Object (forward declared) typedef void godot_object; -/////// Brute force forward declarations for the rest -/* -typedef struct godot_variant godot_variant; -typedef struct godot_string godot_string; -typedef struct godot_vector2 godot_vector2; -typedef struct godot_rect2 godot_rect2; -typedef struct godot_vector3 godot_vector3; -typedef struct godot_transform2d godot_transform2d; -typedef struct godot_plane godot_plane; -typedef struct godot_quat godot_quat; -typedef struct godot_rect3 godot_rect3; -typedef struct godot_basis godot_basis; -typedef struct godot_transform godot_transform; -typedef struct godot_color godot_color; -typedef struct godot_node_path godot_node_path; -typedef struct godot_rid godot_rid; -typedef struct godot_dictionary godot_dictionary; -typedef struct godot_array godot_array; -typedef struct godot_pool_byte_array godot_pool_byte_array; -typedef struct godot_pool_int_array godot_pool_int_array; -typedef struct godot_pool_real_array godot_pool_real_array; -typedef struct godot_pool_string_array godot_pool_string_array; -typedef struct godot_pool_vector2_array godot_pool_vector2_array; -typedef struct godot_pool_vector3_array godot_pool_vector3_array; -typedef struct godot_pool_color_array godot_pool_color_array; -*/ /////// String -#include <godot/string.h> +#include <gdnative/string.h> + +/////// String name + +#include <gdnative/string_name.h> ////// Vector2 -#include <godot/vector2.h> +#include <gdnative/vector2.h> ////// Rect2 -#include <godot/rect2.h> +#include <gdnative/rect2.h> ////// Vector3 -#include <godot/vector3.h> +#include <gdnative/vector3.h> ////// Transform2D -#include <godot/transform2d.h> +#include <gdnative/transform2d.h> /////// Plane -#include <godot/plane.h> +#include <gdnative/plane.h> /////// Quat -#include <godot/quat.h> +#include <gdnative/quat.h> /////// Rect3 -#include <godot/rect3.h> +#include <gdnative/rect3.h> /////// Basis -#include <godot/basis.h> +#include <gdnative/basis.h> /////// Transform -#include <godot/transform.h> +#include <gdnative/transform.h> /////// Color -#include <godot/color.h> +#include <gdnative/color.h> /////// NodePath -#include <godot/node_path.h> +#include <gdnative/node_path.h> /////// RID -#include <godot/rid.h> +#include <gdnative/rid.h> /////// Dictionary -#include <godot/dictionary.h> +#include <gdnative/dictionary.h> /////// Array -#include <godot/array.h> +#include <gdnative/array.h> // single API file for Pool*Array -#include <godot/pool_arrays.h> +#include <gdnative/pool_arrays.h> void GDAPI godot_object_destroy(godot_object *p_o); ////// Variant -#include <godot/variant.h> +#include <gdnative/variant.h> ////// Singleton API @@ -260,12 +229,16 @@ void GDAPI godot_method_bind_ptrcall(godot_method_bind *p_method_bind, godot_obj godot_variant GDAPI godot_method_bind_call(godot_method_bind *p_method_bind, godot_object *p_instance, const godot_variant **p_args, const int p_arg_count, godot_variant_call_error *p_call_error); ////// Script API +struct godot_gdnative_api_struct; // Forward declaration + typedef struct { godot_bool in_editor; uint64_t core_api_hash; uint64_t editor_api_hash; uint64_t no_api_hash; godot_object *gd_native_library; // pointer to GDNativeLibrary that is being initialized + const struct godot_gdnative_api_struct *api_struct; + const godot_string *active_library_path; } godot_gdnative_init_options; typedef struct { @@ -282,7 +255,7 @@ godot_dictionary GDAPI godot_get_global_constants(); ////// GDNative procedure types typedef void (*godot_gdnative_init_fn)(godot_gdnative_init_options *); typedef void (*godot_gdnative_terminate_fn)(godot_gdnative_terminate_options *); -typedef godot_variant (*godot_gdnative_procedure_fn)(void *, godot_array *); +typedef godot_variant (*godot_gdnative_procedure_fn)(godot_array *); ////// System Functions diff --git a/modules/gdnative/godot/node_path.h b/modules/gdnative/include/gdnative/node_path.h index 3e2a99e461..42446175d8 100644 --- a/modules/gdnative/godot/node_path.h +++ b/modules/gdnative/include/gdnative/node_path.h @@ -36,7 +36,7 @@ extern "C" { #include <stdint.h> -#define GODOT_NODE_PATH_SIZE 8 +#define GODOT_NODE_PATH_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_NODE_PATH_TYPE_DEFINED #define GODOT_CORE_API_GODOT_NODE_PATH_TYPE_DEFINED @@ -45,8 +45,17 @@ typedef struct { } godot_node_path; #endif -#include <godot/gdnative.h> -#include <godot/string.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/gdnative.h> +#include <gdnative/string.h> + +#ifdef __cplusplus +extern "C" { +#endif void GDAPI godot_node_path_new(godot_node_path *r_dest, const godot_string *p_from); void GDAPI godot_node_path_new_copy(godot_node_path *r_dest, const godot_node_path *p_src); diff --git a/modules/gdnative/godot/plane.h b/modules/gdnative/include/gdnative/plane.h index 27548c8b0c..dddd172122 100644 --- a/modules/gdnative/godot/plane.h +++ b/modules/gdnative/include/gdnative/plane.h @@ -45,8 +45,17 @@ typedef struct { } godot_plane; #endif -#include <godot/gdnative.h> -#include <godot/vector3.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/gdnative.h> +#include <gdnative/vector3.h> + +#ifdef __cplusplus +extern "C" { +#endif void GDAPI godot_plane_new_with_reals(godot_plane *r_dest, const godot_real p_a, const godot_real p_b, const godot_real p_c, const godot_real p_d); void GDAPI godot_plane_new_with_vectors(godot_plane *r_dest, const godot_vector3 *p_v1, const godot_vector3 *p_v2, const godot_vector3 *p_v3); diff --git a/modules/gdnative/godot/pool_arrays.h b/modules/gdnative/include/gdnative/pool_arrays.h index ecd85ddfe8..93181f2a6b 100644 --- a/modules/gdnative/godot/pool_arrays.h +++ b/modules/gdnative/include/gdnative/pool_arrays.h @@ -38,7 +38,7 @@ extern "C" { /////// PoolByteArray -#define GODOT_POOL_BYTE_ARRAY_SIZE 8 +#define GODOT_POOL_BYTE_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_POOL_BYTE_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_POOL_BYTE_ARRAY_TYPE_DEFINED @@ -49,7 +49,7 @@ typedef struct { /////// PoolIntArray -#define GODOT_POOL_INT_ARRAY_SIZE 8 +#define GODOT_POOL_INT_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_POOL_INT_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_POOL_INT_ARRAY_TYPE_DEFINED @@ -60,7 +60,7 @@ typedef struct { /////// PoolRealArray -#define GODOT_POOL_REAL_ARRAY_SIZE 8 +#define GODOT_POOL_REAL_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_POOL_REAL_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_POOL_REAL_ARRAY_TYPE_DEFINED @@ -71,7 +71,7 @@ typedef struct { /////// PoolStringArray -#define GODOT_POOL_STRING_ARRAY_SIZE 8 +#define GODOT_POOL_STRING_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_POOL_STRING_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_POOL_STRING_ARRAY_TYPE_DEFINED @@ -82,7 +82,7 @@ typedef struct { /////// PoolVector2Array -#define GODOT_POOL_VECTOR2_ARRAY_SIZE 8 +#define GODOT_POOL_VECTOR2_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_POOL_VECTOR2_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_POOL_VECTOR2_ARRAY_TYPE_DEFINED @@ -93,7 +93,7 @@ typedef struct { /////// PoolVector3Array -#define GODOT_POOL_VECTOR3_ARRAY_SIZE 8 +#define GODOT_POOL_VECTOR3_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_POOL_VECTOR3_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_POOL_VECTOR3_ARRAY_TYPE_DEFINED @@ -104,7 +104,7 @@ typedef struct { /////// PoolColorArray -#define GODOT_POOL_COLOR_ARRAY_SIZE 8 +#define GODOT_POOL_COLOR_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_POOL_COLOR_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_POOL_COLOR_ARRAY_TYPE_DEFINED @@ -113,12 +113,21 @@ typedef struct { } godot_pool_color_array; #endif -#include <godot/array.h> -#include <godot/color.h> -#include <godot/vector2.h> -#include <godot/vector3.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/array.h> +#include <gdnative/color.h> +#include <gdnative/vector2.h> +#include <gdnative/vector3.h> -#include <godot/gdnative.h> +#include <gdnative/gdnative.h> + +#ifdef __cplusplus +extern "C" { +#endif // byte diff --git a/modules/gdnative/godot/quat.h b/modules/gdnative/include/gdnative/quat.h index 9a3238a337..acae6e3e90 100644 --- a/modules/gdnative/godot/quat.h +++ b/modules/gdnative/include/gdnative/quat.h @@ -45,8 +45,17 @@ typedef struct { } godot_quat; #endif -#include <godot/gdnative.h> -#include <godot/vector3.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/gdnative.h> +#include <gdnative/vector3.h> + +#ifdef __cplusplus +extern "C" { +#endif void GDAPI godot_quat_new(godot_quat *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_z, const godot_real p_w); void GDAPI godot_quat_new_with_axis_angle(godot_quat *r_dest, const godot_vector3 *p_axis, const godot_real p_angle); diff --git a/modules/gdnative/godot/rect2.h b/modules/gdnative/include/gdnative/rect2.h index 8ceeddf1b4..1c66443d4f 100644 --- a/modules/gdnative/godot/rect2.h +++ b/modules/gdnative/include/gdnative/rect2.h @@ -43,8 +43,17 @@ typedef struct godot_rect2 { } godot_rect2; #endif -#include <godot/gdnative.h> -#include <godot/vector2.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/gdnative.h> +#include <gdnative/vector2.h> + +#ifdef __cplusplus +extern "C" { +#endif void GDAPI godot_rect2_new_with_position_and_size(godot_rect2 *r_dest, const godot_vector2 *p_pos, const godot_vector2 *p_size); void GDAPI godot_rect2_new(godot_rect2 *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_width, const godot_real p_height); diff --git a/modules/gdnative/godot/rect3.h b/modules/gdnative/include/gdnative/rect3.h index ca96aadd5c..f603a9268a 100644 --- a/modules/gdnative/godot/rect3.h +++ b/modules/gdnative/include/gdnative/rect3.h @@ -45,9 +45,18 @@ typedef struct { } godot_rect3; #endif -#include <godot/gdnative.h> -#include <godot/plane.h> -#include <godot/vector3.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/gdnative.h> +#include <gdnative/plane.h> +#include <gdnative/vector3.h> + +#ifdef __cplusplus +extern "C" { +#endif void GDAPI godot_rect3_new(godot_rect3 *r_dest, const godot_vector3 *p_pos, const godot_vector3 *p_size); diff --git a/modules/gdnative/godot/rid.h b/modules/gdnative/include/gdnative/rid.h index b685157cec..caa1bb967e 100644 --- a/modules/gdnative/godot/rid.h +++ b/modules/gdnative/include/gdnative/rid.h @@ -36,7 +36,7 @@ extern "C" { #include <stdint.h> -#define GODOT_RID_SIZE 8 +#define GODOT_RID_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_RID_TYPE_DEFINED #define GODOT_CORE_API_GODOT_RID_TYPE_DEFINED @@ -45,7 +45,16 @@ typedef struct { } godot_rid; #endif -#include <godot/gdnative.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/gdnative.h> + +#ifdef __cplusplus +extern "C" { +#endif void GDAPI godot_rid_new(godot_rid *r_dest); diff --git a/modules/gdnative/godot/string.h b/modules/gdnative/include/gdnative/string.h index f41626faa1..f30fdb8dc7 100644 --- a/modules/gdnative/godot/string.h +++ b/modules/gdnative/include/gdnative/string.h @@ -37,7 +37,7 @@ extern "C" { #include <stdint.h> #include <wchar.h> -#define GODOT_STRING_SIZE 8 +#define GODOT_STRING_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED #define GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED @@ -46,8 +46,17 @@ typedef struct { } godot_string; #endif -#include <godot/gdnative.h> -#include <godot/variant.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/gdnative.h> +#include <gdnative/variant.h> + +#ifdef __cplusplus +extern "C" { +#endif void GDAPI godot_string_new(godot_string *r_dest); void GDAPI godot_string_new_copy(godot_string *r_dest, const godot_string *p_src); @@ -82,7 +91,7 @@ godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_array *p_keys, godot_int p_from, godot_int *r_key); godot_int GDAPI godot_string_findn(const godot_string *p_self, godot_string p_what); godot_int GDAPI godot_string_findn_from(const godot_string *p_self, godot_string p_what, godot_int p_from); -godot_int GDAPI find_last(const godot_string *p_self, godot_string p_what); +godot_int GDAPI godot_string_find_last(const godot_string *p_self, godot_string p_what); godot_string GDAPI godot_string_format(const godot_string *p_self, const godot_variant *p_values); godot_string GDAPI godot_string_format_with_custom_placeholder(const godot_string *p_self, const godot_variant *p_values, const char *p_placeholder); godot_string GDAPI godot_string_hex_encode_buffer(const uint8_t *p_buffer, godot_int p_len); diff --git a/modules/gdnative/include/gdnative/string_name.h b/modules/gdnative/include/gdnative/string_name.h new file mode 100644 index 0000000000..ee9f603d20 --- /dev/null +++ b/modules/gdnative/include/gdnative/string_name.h @@ -0,0 +1,77 @@ +/*************************************************************************/ +/* string_name.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GODOT_STRING_NAME_H +#define GODOT_STRING_NAME_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <wchar.h> + +#define GODOT_STRING_NAME_SIZE sizeof(void *) + +#ifndef GODOT_CORE_API_GODOT_STRING_NAME_TYPE_DEFINED +#define GODOT_CORE_API_GODOT_STRING_NAME_TYPE_DEFINED +typedef struct { + uint8_t _dont_touch_that[GODOT_STRING_NAME_SIZE]; +} godot_string_name; +#endif + +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/gdnative.h> + +#ifdef __cplusplus +extern "C" { +#endif + +void GDAPI godot_string_name_new(godot_string_name *r_dest, const godot_string *p_name); +void GDAPI godot_string_name_new_data(godot_string_name *r_dest, const char *p_name); + +godot_string GDAPI godot_string_name_get_name(const godot_string_name *p_self); + +uint32_t GDAPI godot_string_name_get_hash(const godot_string_name *p_self); +const void GDAPI *godot_string_name_get_data_unique_pointer(const godot_string_name *p_self); + +godot_bool GDAPI godot_string_name_operator_equal(const godot_string_name *p_self, const godot_string_name *p_other); +godot_bool GDAPI godot_string_name_operator_less(const godot_string_name *p_self, const godot_string_name *p_other); + +void GDAPI godot_string_name_destroy(godot_string_name *p_self); + +#ifdef __cplusplus +} +#endif + +#endif // GODOT_STRING_NAME_H diff --git a/modules/gdnative/godot/transform.h b/modules/gdnative/include/gdnative/transform.h index 60788e3d57..8f50b01fb5 100644 --- a/modules/gdnative/godot/transform.h +++ b/modules/gdnative/include/gdnative/transform.h @@ -45,10 +45,19 @@ typedef struct { } godot_transform; #endif -#include <godot/basis.h> -#include <godot/gdnative.h> -#include <godot/variant.h> -#include <godot/vector3.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/basis.h> +#include <gdnative/gdnative.h> +#include <gdnative/variant.h> +#include <gdnative/vector3.h> + +#ifdef __cplusplus +extern "C" { +#endif void GDAPI godot_transform_new_with_axis_origin(godot_transform *r_dest, const godot_vector3 *p_x_axis, const godot_vector3 *p_y_axis, const godot_vector3 *p_z_axis, const godot_vector3 *p_origin); void GDAPI godot_transform_new(godot_transform *r_dest, const godot_basis *p_basis, const godot_vector3 *p_origin); diff --git a/modules/gdnative/godot/transform2d.h b/modules/gdnative/include/gdnative/transform2d.h index c0f5725eed..c68bd2963f 100644 --- a/modules/gdnative/godot/transform2d.h +++ b/modules/gdnative/include/gdnative/transform2d.h @@ -45,9 +45,18 @@ typedef struct { } godot_transform2d; #endif -#include <godot/gdnative.h> -#include <godot/variant.h> -#include <godot/vector2.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/gdnative.h> +#include <gdnative/variant.h> +#include <gdnative/vector2.h> + +#ifdef __cplusplus +extern "C" { +#endif void GDAPI godot_transform2d_new(godot_transform2d *r_dest, const godot_real p_rot, const godot_vector2 *p_pos); void GDAPI godot_transform2d_new_axis_origin(godot_transform2d *r_dest, const godot_vector2 *p_x_axis, const godot_vector2 *p_y_axis, const godot_vector2 *p_origin); diff --git a/modules/gdnative/godot/variant.h b/modules/gdnative/include/gdnative/variant.h index fda24db8d4..3d744ef1f2 100644 --- a/modules/gdnative/godot/variant.h +++ b/modules/gdnative/include/gdnative/variant.h @@ -36,7 +36,7 @@ extern "C" { #include <stdint.h> -#define GODOT_VARIANT_SIZE 24 +#define GODOT_VARIANT_SIZE (16 + sizeof(void *)) #ifndef GODOT_CORE_API_GODOT_VARIANT_TYPE_DEFINED #define GODOT_CORE_API_GODOT_VARIANT_TYPE_DEFINED @@ -99,25 +99,34 @@ typedef struct godot_variant_call_error { godot_variant_type expected; } godot_variant_call_error; -#include <godot/array.h> -#include <godot/basis.h> -#include <godot/color.h> -#include <godot/dictionary.h> -#include <godot/node_path.h> -#include <godot/plane.h> -#include <godot/pool_arrays.h> -#include <godot/quat.h> -#include <godot/rect2.h> -#include <godot/rect3.h> -#include <godot/rid.h> -#include <godot/string.h> -#include <godot/transform.h> -#include <godot/transform2d.h> -#include <godot/variant.h> -#include <godot/vector2.h> -#include <godot/vector3.h> - -#include <godot/gdnative.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/array.h> +#include <gdnative/basis.h> +#include <gdnative/color.h> +#include <gdnative/dictionary.h> +#include <gdnative/node_path.h> +#include <gdnative/plane.h> +#include <gdnative/pool_arrays.h> +#include <gdnative/quat.h> +#include <gdnative/rect2.h> +#include <gdnative/rect3.h> +#include <gdnative/rid.h> +#include <gdnative/string.h> +#include <gdnative/transform.h> +#include <gdnative/transform2d.h> +#include <gdnative/variant.h> +#include <gdnative/vector2.h> +#include <gdnative/vector3.h> + +#include <gdnative/gdnative.h> + +#ifdef __cplusplus +extern "C" { +#endif godot_variant_type GDAPI godot_variant_get_type(const godot_variant *p_v); @@ -190,7 +199,7 @@ godot_bool GDAPI godot_variant_operator_less(const godot_variant *p_self, const godot_bool GDAPI godot_variant_hash_compare(const godot_variant *p_self, const godot_variant *p_other); -godot_bool GDAPI godot_variant_booleanize(const godot_variant *p_self, godot_bool *r_valid); +godot_bool GDAPI godot_variant_booleanize(const godot_variant *p_self); void GDAPI godot_variant_destroy(godot_variant *p_self); diff --git a/modules/gdnative/godot/vector2.h b/modules/gdnative/include/gdnative/vector2.h index 98e9700e32..07105abaf2 100644 --- a/modules/gdnative/godot/vector2.h +++ b/modules/gdnative/include/gdnative/vector2.h @@ -45,7 +45,16 @@ typedef struct { } godot_vector2; #endif -#include <godot/gdnative.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/gdnative.h> + +#ifdef __cplusplus +extern "C" { +#endif void GDAPI godot_vector2_new(godot_vector2 *r_dest, const godot_real p_x, const godot_real p_y); diff --git a/modules/gdnative/godot/vector3.h b/modules/gdnative/include/gdnative/vector3.h index b76ca11a9c..3ed23778ec 100644 --- a/modules/gdnative/godot/vector3.h +++ b/modules/gdnative/include/gdnative/vector3.h @@ -45,8 +45,17 @@ typedef struct { } godot_vector3; #endif -#include <godot/basis.h> -#include <godot/gdnative.h> +// reduce extern "C" nesting for VS2013 +#ifdef __cplusplus +} +#endif + +#include <gdnative/basis.h> +#include <gdnative/gdnative.h> + +#ifdef __cplusplus +extern "C" { +#endif typedef enum { GODOT_VECTOR3_AXIS_X, diff --git a/modules/gdnative/include/nativearvr/godot_nativearvr.h b/modules/gdnative/include/nativearvr/godot_nativearvr.h new file mode 100644 index 0000000000..1a8970d396 --- /dev/null +++ b/modules/gdnative/include/nativearvr/godot_nativearvr.h @@ -0,0 +1,78 @@ +/*************************************************************************/ +/* godot_nativearvr.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GODOT_NATIVEARVR_H +#define GODOT_NATIVEARVR_H + +#include <gdnative/gdnative.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void *(*constructor)(godot_object *); + void (*destructor)(void *); + godot_string (*get_name)(const void *); + godot_int (*get_capabilities)(const void *); + godot_bool (*get_anchor_detection_is_enabled)(const void *); + void (*set_anchor_detection_is_enabled)(void *, godot_bool); + godot_bool (*is_stereo)(const void *); + godot_bool (*is_initialized)(const void *); + godot_bool (*initialize)(void *); + void (*uninitialize)(void *); + godot_vector2 (*get_recommended_render_targetsize)(const void *); + godot_transform (*get_transform_for_eye)(void *, godot_int, godot_transform *); + void (*fill_projection_for_eye)(void *, godot_real *, godot_int, godot_real, godot_real, godot_real); + void (*commit_for_eye)(void *, godot_int, godot_rid *, godot_rect2 *); + void (*process)(void *); +} godot_arvr_interface_gdnative; + +void GDAPI godot_arvr_register_interface(const godot_arvr_interface_gdnative *p_interface); + +// helper functions to access ARVRServer data +godot_real GDAPI godot_arvr_get_worldscale(); +godot_transform GDAPI godot_arvr_get_reference_frame(); + +// helper functions for rendering +void GDAPI godot_arvr_blit(godot_int p_eye, godot_rid *p_render_target, godot_rect2 *p_rect); +godot_int GDAPI godot_arvr_get_texid(godot_rid *p_render_target); + +// helper functions for updating ARVR controllers +godot_int GDAPI godot_arvr_add_controller(char *p_device_name, godot_int p_hand, godot_bool p_tracks_orientation, godot_bool p_tracks_position); +void GDAPI godot_arvr_remove_controller(godot_int p_controller_id); +void GDAPI godot_arvr_set_controller_transform(godot_int p_controller_id, godot_transform *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position); +void GDAPI godot_arvr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed); +void GDAPI godot_arvr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real p_value, godot_bool p_can_be_negative); + +#ifdef __cplusplus +} +#endif + +#endif /* !GODOT_NATIVEARVR_H */ diff --git a/modules/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h index 1eaf459570..8baff0fff9 100644 --- a/modules/nativescript/godot_nativescript.h +++ b/modules/gdnative/include/nativescript/godot_nativescript.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* godot.h */ +/* godot_nativescript.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -30,48 +30,12 @@ #ifndef GODOT_NATIVESCRIPT_H #define GODOT_NATIVESCRIPT_H -#include <godot/gdnative.h> +#include <gdnative/gdnative.h> #ifdef __cplusplus extern "C" { #endif -#ifdef GDAPI_BUILT_IN -#define GDAPI_EXPORT -#endif - -#ifdef _WIN32 -#if defined(GDAPI_EXPORT) -#define GDCALLINGCONV -#define GDAPI __declspec(dllexport) GDCALLINGCONV -#else -#define GDCALLINGCONV -#define GDAPI __declspec(dllimport) GDCALLINGCONV -#endif -#elif defined(__APPLE__) -#include "TargetConditionals.h" -#if TARGET_OS_IPHONE -#define GDCALLINGCONV -#define GDAPI -#elif TARGET_OS_MAC -#define GDCALLINGCONV __attribute__((sysv_abi)) -#define GDAPI GDCALLINGCONV -#endif -#else -#define GDCALLINGCONV __attribute__((sysv_abi)) -#define GDAPI GDCALLINGCONV -#endif - -// This is for libraries *using* the header, NOT GODOT EXPOSING STUFF!! -#ifdef _WIN32 -#define GDN_EXPORT __declspec(dllexport) -#else -#define GDN_EXPORT -#endif - -#include <stdbool.h> -#include <stdint.h> - typedef enum { GODOT_METHOD_RPC_MODE_DISABLED, GODOT_METHOD_RPC_MODE_REMOTE, diff --git a/modules/gdnative/include/pluginscript/godot_pluginscript.h b/modules/gdnative/include/pluginscript/godot_pluginscript.h new file mode 100644 index 0000000000..d1c210529c --- /dev/null +++ b/modules/gdnative/include/pluginscript/godot_pluginscript.h @@ -0,0 +1,171 @@ +/*************************************************************************/ +/* godot_nativescript.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GODOT_PLUGINSCRIPT_H +#define GODOT_PLUGINSCRIPT_H + +#include <gdnative/gdnative.h> +#include <nativescript/godot_nativescript.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void godot_pluginscript_instance_data; +typedef void godot_pluginscript_script_data; +typedef void godot_pluginscript_language_data; + +// --- Instance --- + +// TODO: use godot_string_name for faster lookup ? +typedef struct { + godot_pluginscript_instance_data *(*init)(godot_pluginscript_script_data *p_data, godot_object *p_owner); + void (*finish)(godot_pluginscript_instance_data *p_data); + + godot_bool (*set_prop)(godot_pluginscript_instance_data *p_data, const godot_string *p_name, const godot_variant *p_value); + godot_bool (*get_prop)(godot_pluginscript_instance_data *p_data, const godot_string *p_name, godot_variant *r_ret); + + godot_variant (*call_method)(godot_pluginscript_instance_data *p_data, + const godot_string_name *p_method, const godot_variant **p_args, + int p_argcount, godot_variant_call_error *r_error); + + void (*notification)(godot_pluginscript_instance_data *p_data, int p_notification); + // TODO: could this rpc mode stuff be moved to the godot_pluginscript_script_manifest ? + godot_method_rpc_mode (*get_rpc_mode)(godot_pluginscript_instance_data *p_data, const godot_string *p_method); + godot_method_rpc_mode (*get_rset_mode)(godot_pluginscript_instance_data *p_data, const godot_string *p_variable); + + //this is used by script languages that keep a reference counter of their own + //you can make make Ref<> not die when it reaches zero, so deleting the reference + //depends entirely from the script. + // Note: You can set thoses function pointer to NULL if not needed. + void (*refcount_incremented)(godot_pluginscript_instance_data *p_data); + bool (*refcount_decremented)(godot_pluginscript_instance_data *p_data); // return true if it can die +} godot_pluginscript_instance_desc; + +// --- Script --- + +typedef struct { + godot_pluginscript_script_data *data; + godot_string_name name; + godot_bool is_tool; + godot_string_name base; + + // Member lines format: {<string>: <int>} + godot_dictionary member_lines; + // Method info dictionary format + // { + // name: <string> + // args: [<dict:property>] + // default_args: [<variant>] + // return: <dict:property> + // flags: <int> + // rpc_mode: <int:godot_method_rpc_mode> + // } + godot_array methods; + // Same format than for methods + godot_array signals; + // Property info dictionary format + // { + // name: <string> + // type: <int:godot_variant_type> + // hint: <int:godot_property_hint> + // hint_string: <string> + // usage: <int:godot_property_usage_flags> + // default_value: <variant> + // rset_mode: <int:godot_method_rpc_mode> + // } + godot_array properties; +} godot_pluginscript_script_manifest; + +typedef struct { + godot_pluginscript_script_manifest (*init)(godot_pluginscript_language_data *p_data, const godot_string *p_path, const godot_string *p_source, godot_error *r_error); + void (*finish)(godot_pluginscript_script_data *p_data); + godot_pluginscript_instance_desc instance_desc; +} godot_pluginscript_script_desc; + +// --- Language --- + +typedef struct { + godot_string_name signature; + godot_int call_count; + godot_int total_time; // In microseconds + godot_int self_time; // In microseconds +} godot_pluginscript_profiling_data; + +typedef struct { + const char *name; + const char *type; + const char *extension; + const char **recognized_extensions; // NULL terminated array + godot_pluginscript_language_data *(*init)(); + void (*finish)(godot_pluginscript_language_data *p_data); + const char **reserved_words; // NULL terminated array + const char **comment_delimiters; // NULL terminated array + const char **string_delimiters; // NULL terminated array + godot_bool has_named_classes; + godot_bool supports_builtin_mode; + + godot_string (*get_template_source_code)(godot_pluginscript_language_data *p_data, const godot_string *p_class_name, const godot_string *p_base_class_name); + godot_bool (*validate)(godot_pluginscript_language_data *p_data, const godot_string *p_script, int *r_line_error, int *r_col_error, godot_string *r_test_error, const godot_string *p_path, godot_pool_string_array *r_functions); + int (*find_function)(godot_pluginscript_language_data *p_data, const godot_string *p_function, const godot_string *p_code); // Can be NULL + godot_string (*make_function)(godot_pluginscript_language_data *p_data, const godot_string *p_class, const godot_string *p_name, const godot_pool_string_array *p_args); + godot_error (*complete_code)(godot_pluginscript_language_data *p_data, const godot_string *p_code, const godot_string *p_base_path, godot_object *p_owner, godot_array *r_options, godot_bool *r_force, godot_string *r_call_hint); + void (*auto_indent_code)(godot_pluginscript_language_data *p_data, godot_string *p_code, int p_from_line, int p_to_line); + + void (*add_global_constant)(godot_pluginscript_language_data *p_data, const godot_string *p_variable, const godot_variant *p_value); + godot_string (*debug_get_error)(godot_pluginscript_language_data *p_data); + int (*debug_get_stack_level_count)(godot_pluginscript_language_data *p_data); + int (*debug_get_stack_level_line)(godot_pluginscript_language_data *p_data, int p_level); + godot_string (*debug_get_stack_level_function)(godot_pluginscript_language_data *p_data, int p_level); + godot_string (*debug_get_stack_level_source)(godot_pluginscript_language_data *p_data, int p_level); + void (*debug_get_stack_level_locals)(godot_pluginscript_language_data *p_data, int p_level, godot_pool_string_array *p_locals, godot_array *p_values, int p_max_subitems, int p_max_depth); + void (*debug_get_stack_level_members)(godot_pluginscript_language_data *p_data, int p_level, godot_pool_string_array *p_members, godot_array *p_values, int p_max_subitems, int p_max_depth); + void (*debug_get_globals)(godot_pluginscript_language_data *p_data, godot_pool_string_array *p_locals, godot_array *p_values, int p_max_subitems, int p_max_depth); + godot_string (*debug_parse_stack_level_expression)(godot_pluginscript_language_data *p_data, int p_level, const godot_string *p_expression, int p_max_subitems, int p_max_depth); + + // TODO: could this stuff be moved to the godot_pluginscript_language_desc ? + void (*get_public_functions)(godot_pluginscript_language_data *p_data, godot_array *r_functions); + void (*get_public_constants)(godot_pluginscript_language_data *p_data, godot_dictionary *r_constants); + + void (*profiling_start)(godot_pluginscript_language_data *p_data); + void (*profiling_stop)(godot_pluginscript_language_data *p_data); + int (*profiling_get_accumulated_data)(godot_pluginscript_language_data *p_data, godot_pluginscript_profiling_data *r_info, int p_info_max); + int (*profiling_get_frame_data)(godot_pluginscript_language_data *p_data, godot_pluginscript_profiling_data *r_info, int p_info_max); + void (*profiling_frame)(godot_pluginscript_language_data *p_data); + + godot_pluginscript_script_desc script_desc; +} godot_pluginscript_language_desc; + +void GDAPI godot_pluginscript_register_language(const godot_pluginscript_language_desc *language_desc); + +#ifdef __cplusplus +} +#endif + +#endif // GODOT_PLUGINSCRIPT_H diff --git a/modules/gdnative/nativearvr/SCsub b/modules/gdnative/nativearvr/SCsub new file mode 100644 index 0000000000..ecc5996108 --- /dev/null +++ b/modules/gdnative/nativearvr/SCsub @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +import os +import methods + +Import('env') +Import('env_modules') + +env_arvr_gdnative = env_modules.Clone() + +env_arvr_gdnative.Append(CPPPATH=['#modules/gdnative/include/']) +env_arvr_gdnative.add_source_files(env.modules_sources, '*.cpp') + diff --git a/modules/gdnative/nativearvr/arvr_interface_gdnative.cpp b/modules/gdnative/nativearvr/arvr_interface_gdnative.cpp new file mode 100644 index 0000000000..ff8bda162f --- /dev/null +++ b/modules/gdnative/nativearvr/arvr_interface_gdnative.cpp @@ -0,0 +1,386 @@ +/*************************************************************************/ +/* arvr_interface_gdnative.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "arvr_interface_gdnative.h" +#include "main/input_default.h" +#include "servers/arvr/arvr_positional_tracker.h" +#include "servers/visual/visual_server_global.h" + +ARVRInterfaceGDNative::ARVRInterfaceGDNative() { + // testing + printf("Construct gdnative interface\n"); + + // we won't have our data pointer until our library gets set + data = NULL; + + interface = NULL; +} + +ARVRInterfaceGDNative::~ARVRInterfaceGDNative() { + printf("Destruct gdnative interface\n"); + + if (is_initialized()) { + uninitialize(); + }; + + // cleanup after ourselves + cleanup(); +} + +void ARVRInterfaceGDNative::cleanup() { + if (interface != NULL) { + interface->destructor(data); + data = NULL; + interface = NULL; + } +} + +void ARVRInterfaceGDNative::set_interface(const godot_arvr_interface_gdnative *p_interface) { + // this should only be called once, just being paranoid.. + if (interface) { + cleanup(); + } + + // bind to our interface + interface = p_interface; + + // Now we do our constructing... + data = interface->constructor((godot_object *)this); +} + +StringName ARVRInterfaceGDNative::get_name() const { + + ERR_FAIL_COND_V(interface == NULL, StringName()); + + godot_string result = interface->get_name(data); + + StringName name = *(String *)&result; + + godot_string_destroy(&result); + + return name; +} + +int ARVRInterfaceGDNative::get_capabilities() const { + int capabilities; + + ERR_FAIL_COND_V(interface == NULL, 0); // 0 = None + + capabilities = interface->get_capabilities(data); + + return capabilities; +} + +bool ARVRInterfaceGDNative::get_anchor_detection_is_enabled() const { + bool enabled; + + ERR_FAIL_COND_V(interface == NULL, false); + + enabled = interface->get_anchor_detection_is_enabled(data); + + return enabled; +} + +void ARVRInterfaceGDNative::set_anchor_detection_is_enabled(bool p_enable) { + + ERR_FAIL_COND(interface == NULL); + + interface->set_anchor_detection_is_enabled(data, p_enable); +} + +bool ARVRInterfaceGDNative::is_stereo() { + bool stereo; + + ERR_FAIL_COND_V(interface == NULL, false); + + stereo = interface->is_stereo(data); + + return stereo; +} + +bool ARVRInterfaceGDNative::is_initialized() { + bool initialized; + + ERR_FAIL_COND_V(interface == NULL, false); + + initialized = interface->is_initialized(data); + + return initialized; +} + +bool ARVRInterfaceGDNative::initialize() { + bool initialized; + + ERR_FAIL_COND_V(interface == NULL, false); + + initialized = interface->initialize(data); + + if (initialized) { + // if we successfully initialize our interface and we don't have a primary interface yet, this becomes our primary interface + + ARVRServer *arvr_server = ARVRServer::get_singleton(); + if ((arvr_server != NULL) && (arvr_server->get_primary_interface() == NULL)) { + arvr_server->set_primary_interface(this); + }; + }; + + return initialized; +} + +void ARVRInterfaceGDNative::uninitialize() { + ERR_FAIL_COND(interface == NULL); + + ARVRServer *arvr_server = ARVRServer::get_singleton(); + if (arvr_server != NULL) { + // Whatever happens, make sure this is no longer our primary interface + arvr_server->clear_primary_interface_if(this); + } + + interface->uninitialize(data); +} + +Size2 ARVRInterfaceGDNative::get_recommended_render_targetsize() { + + ERR_FAIL_COND_V(interface == NULL, Size2()); + + godot_vector2 result = interface->get_recommended_render_targetsize(data); + Vector2 *vec = (Vector2 *)&result; + + return *vec; +} + +Transform ARVRInterfaceGDNative::get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform) { + Transform *ret; + + ERR_FAIL_COND_V(interface == NULL, Transform()); + + godot_transform t = interface->get_transform_for_eye(data, (int)p_eye, (godot_transform *)&p_cam_transform); + + ret = (Transform *)&t; + + return *ret; +} + +CameraMatrix ARVRInterfaceGDNative::get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { + CameraMatrix cm; + + ERR_FAIL_COND_V(interface == NULL, CameraMatrix()); + + interface->fill_projection_for_eye(data, (godot_real *)cm.matrix, (godot_int)p_eye, p_aspect, p_z_near, p_z_far); + + return cm; +} + +void ARVRInterfaceGDNative::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { + + ERR_FAIL_COND(interface == NULL); + + interface->commit_for_eye(data, (godot_int)p_eye, (godot_rid *)&p_render_target, (godot_rect2 *)&p_screen_rect); +} + +void ARVRInterfaceGDNative::process() { + ERR_FAIL_COND(interface == NULL); + + interface->process(data); +} + +///////////////////////////////////////////////////////////////////////////////////// +// some helper callbacks + +extern "C" { + +void GDAPI godot_arvr_register_interface(const godot_arvr_interface_gdnative *p_interface) { + Ref<ARVRInterfaceGDNative> new_interface; + new_interface.instance(); + new_interface->set_interface((godot_arvr_interface_gdnative * const)p_interface); + ARVRServer::get_singleton()->add_interface(new_interface); +} + +godot_real GDAPI godot_arvr_get_worldscale() { + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL_V(arvr_server, 1.0); + + return arvr_server->get_world_scale(); +} + +godot_transform GDAPI godot_arvr_get_reference_frame() { + godot_transform reference_frame; + Transform *reference_frame_ptr = (Transform *)&reference_frame; + + ARVRServer *arvr_server = ARVRServer::get_singleton(); + if (arvr_server != NULL) { + *reference_frame_ptr = arvr_server->get_reference_frame(); + } else { + godot_transform_new_identity(&reference_frame); + } + + return reference_frame; +} + +void GDAPI godot_arvr_blit(godot_int p_eye, godot_rid *p_render_target, godot_rect2 *p_rect) { + // blits out our texture as is, handy for preview display of one of the eyes that is already rendered with lens distortion on an external HMD + ARVRInterface::Eyes eye = (ARVRInterface::Eyes)p_eye; + RID *render_target = (RID *)p_render_target; + Rect2 screen_rect = *(Rect2 *)p_rect; + + if (eye == ARVRInterface::EYE_LEFT) { + screen_rect.size.x /= 2.0; + } else if (p_eye == ARVRInterface::EYE_RIGHT) { + screen_rect.size.x /= 2.0; + screen_rect.position.x += screen_rect.size.x; + } + + VSG::rasterizer->set_current_render_target(RID()); + VSG::rasterizer->blit_render_target_to_screen(*render_target, screen_rect, 0); +} + +godot_int GDAPI godot_arvr_get_texid(godot_rid *p_render_target) { + // In order to send off our textures to display on our hardware we need the opengl texture ID instead of the render target RID + // This is a handy function to expose that. + RID *render_target = (RID *)p_render_target; + + RID eye_texture = VSG::storage->render_target_get_texture(*render_target); + uint32_t texid = VS::get_singleton()->texture_get_texid(eye_texture); + + return texid; +} + +godot_int GDAPI godot_arvr_add_controller(char *p_device_name, godot_int p_hand, godot_bool p_tracks_orientation, godot_bool p_tracks_position) { + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL_V(arvr_server, 0); + + InputDefault *input = (InputDefault *)Input::get_singleton(); + ERR_FAIL_NULL_V(input, 0); + + ARVRPositionalTracker *new_tracker = memnew(ARVRPositionalTracker); + new_tracker->set_name(p_device_name); + new_tracker->set_type(ARVRServer::TRACKER_CONTROLLER); + if (p_hand == 1) { + new_tracker->set_hand(ARVRPositionalTracker::TRACKER_LEFT_HAND); + } else if (p_hand == 2) { + new_tracker->set_hand(ARVRPositionalTracker::TRACKER_RIGHT_HAND); + } + + // also register as joystick... + int joyid = input->get_unused_joy_id(); + if (joyid != -1) { + new_tracker->set_joy_id(joyid); + input->joy_connection_changed(joyid, true, p_device_name, ""); + } + + if (p_tracks_orientation) { + Basis orientation; + new_tracker->set_orientation(orientation); + } + if (p_tracks_position) { + Vector3 position; + new_tracker->set_position(position); + } + + // add our tracker to our server and remember its pointer + arvr_server->add_tracker(new_tracker); + + // note, this ID is only unique within controllers! + return new_tracker->get_tracker_id(); +} + +void GDAPI godot_arvr_remove_controller(godot_int p_controller_id) { + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL(arvr_server); + + InputDefault *input = (InputDefault *)Input::get_singleton(); + ERR_FAIL_NULL(input); + + ARVRPositionalTracker *remove_tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, p_controller_id); + if (remove_tracker != NULL) { + // unset our joystick if applicable + int joyid = remove_tracker->get_joy_id(); + if (joyid != -1) { + input->joy_connection_changed(joyid, false, "", ""); + remove_tracker->set_joy_id(-1); + } + + // remove our tracker from our server + arvr_server->remove_tracker(remove_tracker); + memdelete(remove_tracker); + } +} + +void GDAPI godot_arvr_set_controller_transform(godot_int p_controller_id, godot_transform *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position) { + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL(arvr_server); + + ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, p_controller_id); + if (tracker != NULL) { + Transform *transform = (Transform *)p_transform; + if (p_tracks_orientation) { + tracker->set_orientation(transform->basis); + } + if (p_tracks_position) { + tracker->set_position(transform->origin); + } + } +} + +void GDAPI godot_arvr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed) { + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL(arvr_server); + + InputDefault *input = (InputDefault *)Input::get_singleton(); + ERR_FAIL_NULL(input); + + ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, p_controller_id); + if (tracker != NULL) { + int joyid = tracker->get_joy_id(); + if (joyid != -1) { + input->joy_button(joyid, p_button, p_is_pressed); + } + } +} + +void GDAPI godot_arvr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real p_value, godot_bool p_can_be_negative) { + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL(arvr_server); + + InputDefault *input = (InputDefault *)Input::get_singleton(); + ERR_FAIL_NULL(input); + + ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, p_controller_id); + if (tracker != NULL) { + int joyid = tracker->get_joy_id(); + if (joyid != -1) { + InputDefault::JoyAxis jx; + jx.min = p_can_be_negative ? -1 : 0; + jx.value = p_value; + input->joy_axis(joyid, p_axis, jx); + } + } +} +} diff --git a/modules/gdnative/nativearvr/arvr_interface_gdnative.h b/modules/gdnative/nativearvr/arvr_interface_gdnative.h new file mode 100644 index 0000000000..e45b51e070 --- /dev/null +++ b/modules/gdnative/nativearvr/arvr_interface_gdnative.h @@ -0,0 +1,86 @@ +/*************************************************************************/ +/* arvr_interface_gdnative.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 ARVR_INTERFACE_GDNATIVE_H +#define ARVR_INTERFACE_GDNATIVE_H + +#include "modules/gdnative/gdnative.h" +#include "servers/arvr/arvr_interface.h" + +/** + @authors Hinsbart & Karroffel & Mux213 + + This subclass of our AR/VR interface forms a bridge to GDNative. +*/ + +class ARVRInterfaceGDNative : public ARVRInterface { + GDCLASS(ARVRInterfaceGDNative, ARVRInterface) + + void cleanup(); + +protected: + const godot_arvr_interface_gdnative *interface; + void *data; + +public: + /** general interface information **/ + ARVRInterfaceGDNative(); + ~ARVRInterfaceGDNative(); + + void set_interface(const godot_arvr_interface_gdnative *p_interface); + + virtual StringName get_name() const; + virtual int get_capabilities() const; + + virtual bool is_initialized(); + virtual bool initialize(); + virtual void uninitialize(); + + /** specific to AR **/ + virtual bool get_anchor_detection_is_enabled() const; + virtual void set_anchor_detection_is_enabled(bool p_enable); + + /** rendering and internal **/ + virtual Size2 get_recommended_render_targetsize(); + virtual bool is_stereo(); + virtual Transform get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform); + + // we expose a PoolVector<float> version of this function to GDNative + PoolVector<float> _get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); + + // and a CameraMatrix version to ARVRServer + virtual CameraMatrix get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); + + virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); + + virtual void process(); +}; + +#endif // ARVR_INTERFACE_GDNATIVE_H diff --git a/modules/gdnative/nativearvr/config.py b/modules/gdnative/nativearvr/config.py new file mode 100644 index 0000000000..4d1bdfe4d1 --- /dev/null +++ b/modules/gdnative/nativearvr/config.py @@ -0,0 +1,5 @@ +def can_build(platform): + return True + +def configure(env): + pass diff --git a/modules/gdnative/nativearvr/register_types.cpp b/modules/gdnative/nativearvr/register_types.cpp new file mode 100644 index 0000000000..c7d7847a21 --- /dev/null +++ b/modules/gdnative/nativearvr/register_types.cpp @@ -0,0 +1,39 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "register_types.h" +#include "arvr_interface_gdnative.h" + +void register_nativearvr_types() { + ClassDB::register_class<ARVRInterfaceGDNative>(); +} + +void unregister_nativearvr_types() { +} diff --git a/modules/gdnative/nativearvr/register_types.h b/modules/gdnative/nativearvr/register_types.h new file mode 100644 index 0000000000..5e7557c7e9 --- /dev/null +++ b/modules/gdnative/nativearvr/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +void register_nativearvr_types(); +void unregister_nativearvr_types(); diff --git a/modules/nativescript/SCsub b/modules/gdnative/nativescript/SCsub index e980e40e8e..ee3b9c351d 100644 --- a/modules/nativescript/SCsub +++ b/modules/gdnative/nativescript/SCsub @@ -4,7 +4,9 @@ Import('env') mod_env = env.Clone() mod_env.add_source_files(env.modules_sources, "*.cpp") -mod_env.Append(CPPPATH='#modules/gdnative') mod_env.Append(CPPFLAGS=['-DGDAPI_BUILT_IN']) +if "platform" in env and env["platform"] in ["x11", "iphone"]: + env.Append(LINKFLAGS=["-rdynamic"]) + Export('mod_env') diff --git a/modules/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index 4490197bdb..63fb71feb6 100644 --- a/modules/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -31,8 +31,9 @@ #ifdef TOOLS_ENABLED -#include "class_db.h" +#include "core/class_db.h" #include "core/global_constants.h" +#include "core/pair.h" #include "core/project_settings.h" #include "os/file_access.h" @@ -79,6 +80,7 @@ struct PropertyAPI { String getter; String setter; String type; + int index; }; struct ConstantAPI { @@ -93,6 +95,11 @@ struct SignalAPI { Map<int, Variant> default_arguments; }; +struct EnumAPI { + String name; + List<Pair<int, String> > values; +}; + struct ClassAPI { String class_name; String super_class_name; @@ -109,8 +116,28 @@ struct ClassAPI { List<PropertyAPI> properties; List<ConstantAPI> constants; List<SignalAPI> signals_; + List<EnumAPI> enums; }; +static String get_type_name(const PropertyInfo &info) { + if (info.type == Variant::INT && (info.usage & PROPERTY_USAGE_CLASS_IS_ENUM)) { + return String("enum.") + String(info.class_name).replace(".", "::"); + } + if (info.class_name != StringName()) { + return info.class_name; + } + if (info.hint == PROPERTY_HINT_RESOURCE_TYPE) { + return info.hint_string; + } + if (info.type == Variant::NIL && (info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) { + return "Variant"; + } + if (info.type == Variant::NIL) { + return "void"; + } + return Variant::get_type_name(info.type); +} + /* * Reads the entire Godot API to a list */ @@ -194,12 +221,8 @@ List<ClassAPI> generate_c_api_classes() { if (argument.name.find(":") != -1) { type = argument.name.get_slice(":", 1); name = argument.name.get_slice(":", 0); - } else if (argument.hint == PROPERTY_HINT_RESOURCE_TYPE) { - type = argument.hint_string; - } else if (argument.type == Variant::NIL) { - type = "Variant"; } else { - type = Variant::get_type_name(argument.type); + type = get_type_name(argument); } signal.argument_names.push_back(name); @@ -233,14 +256,12 @@ List<ClassAPI> generate_c_api_classes() { if (p->get().name.find(":") != -1) { property_api.type = p->get().name.get_slice(":", 1); property_api.name = p->get().name.get_slice(":", 0); - } else if (p->get().hint == PROPERTY_HINT_RESOURCE_TYPE) { - property_api.type = p->get().hint_string; - } else if (p->get().type == Variant::NIL) { - property_api.type = "Variant"; } else { - property_api.type = Variant::get_type_name(p->get().type); + property_api.type = get_type_name(p->get()); } + property_api.index = ClassDB::get_property_index(class_name, p->get().name); + if (!property_api.setter.empty() || !property_api.getter.empty()) { class_api.properties.push_back(property_api); } @@ -260,17 +281,11 @@ List<ClassAPI> generate_c_api_classes() { //method name method_api.method_name = m->get().name; //method return type - if (method_bind && method_bind->get_return_type() != StringName()) { - method_api.return_type = method_bind->get_return_type(); - } else if (method_api.method_name.find(":") != -1) { + if (method_api.method_name.find(":") != -1) { method_api.return_type = method_api.method_name.get_slice(":", 1); method_api.method_name = method_api.method_name.get_slice(":", 0); - } else if (m->get().return_val.type != Variant::NIL) { - method_api.return_type = m->get().return_val.hint == PROPERTY_HINT_RESOURCE_TYPE ? m->get().return_val.hint_string : Variant::get_type_name(m->get().return_val.type); - } else if (m->get().return_val.name != "") { - method_api.return_type = m->get().return_val.name; } else { - method_api.return_type = "void"; + method_api.return_type = get_type_name(m->get().return_val); } method_api.argument_count = method_info.arguments.size(); @@ -321,6 +336,25 @@ List<ClassAPI> generate_c_api_classes() { } } + // enums + { + List<EnumAPI> enums; + List<StringName> enum_names; + ClassDB::get_enum_list(class_name, &enum_names, true); + for (List<StringName>::Element *E = enum_names.front(); E; E = E->next()) { + List<StringName> value_names; + EnumAPI enum_api; + enum_api.name = E->get(); + ClassDB::get_enum_constants(class_name, E->get(), &value_names, true); + for (List<StringName>::Element *val_e = value_names.front(); val_e; val_e = val_e->next()) { + int int_val = ClassDB::get_integer_constant(class_name, val_e->get(), NULL); + enum_api.values.push_back(Pair<int, String>(int_val, val_e->get())); + } + enum_api.values.sort_custom<PairSort<int, String> >(); + class_api.enums.push_back(enum_api); + } + } + api.push_back(class_api); } @@ -364,7 +398,8 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) { source.push_back("\t\t\t\t\"name\": \"" + e->get().name + "\",\n"); source.push_back("\t\t\t\t\"type\": \"" + e->get().type + "\",\n"); source.push_back("\t\t\t\t\"getter\": \"" + e->get().getter + "\",\n"); - source.push_back("\t\t\t\t\"setter\": \"" + e->get().setter + "\"\n"); + source.push_back("\t\t\t\t\"setter\": \"" + e->get().setter + "\",\n"); + source.push_back(String("\t\t\t\t\"index\": ") + itos(e->get().index) + "\n"); source.push_back(String("\t\t\t}") + (e->next() ? "," : "") + "\n"); } source.push_back("\t\t],\n"); @@ -410,11 +445,24 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) { source.push_back("\t\t\t\t]\n"); source.push_back(String("\t\t\t}") + (e->next() ? "," : "") + "\n"); } + source.push_back("\t\t],\n"); + + source.push_back("\t\t\"enums\": [\n"); + for (List<EnumAPI>::Element *e = api.enums.front(); e; e = e->next()) { + source.push_back("\t\t\t{\n"); + source.push_back("\t\t\t\t\"name\": \"" + e->get().name + "\",\n"); + source.push_back("\t\t\t\t\"values\": {\n"); + for (List<Pair<int, String> >::Element *val_e = e->get().values.front(); val_e; val_e = val_e->next()) { + source.push_back("\t\t\t\t\t\"" + val_e->get().second + "\": " + itos(val_e->get().first)); + source.push_back(String((val_e->next() ? "," : "")) + "\n"); + } + source.push_back("\t\t\t\t}\n"); + source.push_back(String("\t\t\t}") + (e->next() ? "," : "") + "\n"); + } source.push_back("\t\t]\n"); source.push_back(String("\t}") + (c->next() ? "," : "") + "\n"); } - source.push_back("]"); return source; diff --git a/modules/nativescript/api_generator.h b/modules/gdnative/nativescript/api_generator.h index 56c2d786e6..a8e2eaf0bf 100644 --- a/modules/nativescript/api_generator.h +++ b/modules/gdnative/nativescript/api_generator.h @@ -30,8 +30,8 @@ #ifndef API_GENERATOR_H #define API_GENERATOR_H +#include "core/typedefs.h" #include "core/ustring.h" -#include "typedefs.h" Error generate_c_api(const String &p_path); diff --git a/modules/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp index 926b3261b2..61ac13b796 100644 --- a/modules/nativescript/godot_nativescript.cpp +++ b/modules/gdnative/nativescript/godot_nativescript.cpp @@ -27,17 +27,17 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "godot_nativescript.h" - -#include "nativescript.h" +#include "nativescript/godot_nativescript.h" #include "class_db.h" #include "error_macros.h" -#include "gdnative.h" +#include "gdnative/gdnative.h" #include "global_constants.h" #include "project_settings.h" #include "variant.h" +#include "nativescript.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/modules/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index 3799ce31f8..c1df7def2e 100644 --- a/modules/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -29,13 +29,13 @@ /*************************************************************************/ #include "nativescript.h" -#include "modules/gdnative/godot/gdnative.h" +#include "gdnative/gdnative.h" -#include "global_constants.h" +#include "core/global_constants.h" +#include "core/project_settings.h" #include "io/file_access_encrypted.h" #include "os/file_access.h" #include "os/os.h" -#include "project_settings.h" #include "scene/main/scene_tree.h" #include "scene/resources/scene_format_text.h" @@ -137,7 +137,6 @@ bool NativeScript::can_instance() const { #endif } -// TODO(karroffel): implement this Ref<Script> NativeScript::get_base_script() const { NativeScriptDesc *script_data = get_script_desc(); @@ -271,10 +270,6 @@ bool NativeScript::is_tool() const { return false; } -String NativeScript::get_node_type() const { - return ""; // NOTE(karroffel): uhm? -} - ScriptLanguage *NativeScript::get_language() const { return NativeScriptLanguage::get_singleton(); } @@ -315,7 +310,7 @@ void NativeScript::get_script_signal_list(List<MethodInfo> *r_signals) const { bool NativeScript::get_property_default_value(const StringName &p_property, Variant &r_value) const { NativeScriptDesc *script_data = get_script_desc(); - Map<StringName, NativeScriptDesc::Property>::Element *P = NULL; + OrderedHashMap<StringName, NativeScriptDesc::Property>::Element P; while (!P && script_data) { P = script_data->properties.find(p_property); script_data = script_data->base_data; @@ -323,7 +318,7 @@ bool NativeScript::get_property_default_value(const StringName &p_property, Vari if (!P) return false; - r_value = P->get().default_value; + r_value = P.get().default_value; return true; } @@ -355,23 +350,20 @@ void NativeScript::get_script_method_list(List<MethodInfo> *p_list) const { void NativeScript::get_script_property_list(List<PropertyInfo> *p_list) const { NativeScriptDesc *script_data = get_script_desc(); - if (!script_data) - return; - - Set<PropertyInfo> properties; - + Set<StringName> existing_properties; while (script_data) { - - for (Map<StringName, NativeScriptDesc::Property>::Element *E = script_data->properties.front(); E; E = E->next()) { - properties.insert(E->get().info); + List<PropertyInfo>::Element *insert_position = p_list->front(); + bool insert_before = true; + + for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.front(); E; E = E.next()) { + if (!existing_properties.has(E.key())) { + insert_position = insert_before ? p_list->insert_before(insert_position, E.get().info) : p_list->insert_after(insert_position, E.get().info); + insert_before = false; + existing_properties.insert(E.key()); + } } - script_data = script_data->base_data; } - - for (Set<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { - p_list->push_back(E->get()); - } } Variant NativeScript::_new(const Variant **p_args, int p_argcount, Variant::CallError &r_error) { @@ -461,10 +453,10 @@ bool NativeScriptInstance::set(const StringName &p_name, const Variant &p_value) NativeScriptDesc *script_data = GET_SCRIPT_DESC(); while (script_data) { - Map<StringName, NativeScriptDesc::Property>::Element *P = script_data->properties.find(p_name); + OrderedHashMap<StringName, NativeScriptDesc::Property>::Element P = script_data->properties.find(p_name); if (P) { - P->get().setter.set_func((godot_object *)owner, - P->get().setter.method_data, + P.get().setter.set_func((godot_object *)owner, + P.get().setter.method_data, userdata, (godot_variant *)&p_value); return true; @@ -491,11 +483,11 @@ bool NativeScriptInstance::get(const StringName &p_name, Variant &r_ret) const { NativeScriptDesc *script_data = GET_SCRIPT_DESC(); while (script_data) { - Map<StringName, NativeScriptDesc::Property>::Element *P = script_data->properties.find(p_name); + OrderedHashMap<StringName, NativeScriptDesc::Property>::Element P = script_data->properties.find(p_name); if (P) { godot_variant value; - value = P->get().getter.get_func((godot_object *)owner, - P->get().getter.method_data, + value = P.get().getter.get_func((godot_object *)owner, + P.get().getter.method_data, userdata); r_ret = *(Variant *)&value; godot_variant_destroy(&value); @@ -592,10 +584,10 @@ Variant::Type NativeScriptInstance::get_property_type(const StringName &p_name, while (script_data) { - Map<StringName, NativeScriptDesc::Property>::Element *P = script_data->properties.find(p_name); + OrderedHashMap<StringName, NativeScriptDesc::Property>::Element P = script_data->properties.find(p_name); if (P) { *r_is_valid = true; - return P->get().info.type; + return P.get().info.type; } script_data = script_data->base_data; @@ -706,9 +698,9 @@ NativeScriptInstance::RPCMode NativeScriptInstance::get_rset_mode(const StringNa while (script_data) { - Map<StringName, NativeScriptDesc::Property>::Element *E = script_data->properties.find(p_variable); + OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.find(p_variable); if (E) { - switch (E->get().rset_mode) { + switch (E.get().rset_mode) { case GODOT_METHOD_RPC_MODE_DISABLED: return RPC_MODE_DISABLED; case GODOT_METHOD_RPC_MODE_REMOTE: @@ -796,12 +788,12 @@ void NativeScriptLanguage::_unload_stuff() { for (Map<StringName, NativeScriptDesc>::Element *C = L->get().front(); C; C = C->next()) { // free property stuff first - for (Map<StringName, NativeScriptDesc::Property>::Element *P = C->get().properties.front(); P; P = P->next()) { - if (P->get().getter.free_func) - P->get().getter.free_func(P->get().getter.method_data); + for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element P = C->get().properties.front(); P; P = P.next()) { + if (P.get().getter.free_func) + P.get().getter.free_func(P.get().getter.method_data); - if (P->get().setter.free_func) - P->get().setter.free_func(P->get().setter.method_data); + if (P.get().setter.free_func) + P.get().setter.free_func(P.get().setter.method_data); } // free method stuff @@ -912,6 +904,9 @@ Script *NativeScriptLanguage::create_script() const { bool NativeScriptLanguage::has_named_classes() const { return true; } +bool NativeScriptLanguage::supports_builtin_mode() const { + return true; +} int NativeScriptLanguage::find_function(const String &p_function, const String &p_code) const { return -1; } @@ -994,6 +989,8 @@ void NativeScriptLanguage::init_library(const Ref<GDNativeLibrary> &lib) { #endif // See if this library was "registered" already. const String &lib_path = lib->get_active_library_path(); + ERR_EXPLAIN(lib->get_name() + " does not have a library for the current platform"); + ERR_FAIL_COND(lib_path.length() == 0); Map<String, Ref<GDNative> >::Element *E = library_gdnatives.find(lib_path); if (!E) { @@ -1011,17 +1008,15 @@ void NativeScriptLanguage::init_library(const Ref<GDNativeLibrary> &lib) { if (!library_script_users.has(lib_path)) library_script_users.insert(lib_path, Set<NativeScript *>()); - void *args[1] = { - (void *)&lib_path - }; + void *proc_ptr; - // here the library registers all the classes and stuff. - gdn->call_native_raw(_init_call_type, - _init_call_name, - NULL, - 1, - args, - NULL); + Error err = gdn->get_symbol(_init_call_name, proc_ptr); + + if (err != OK) { + ERR_PRINT(String("No " + _init_call_name + " in \"" + lib_path + "\" found").utf8().get_data()); + } else { + ((void (*)(godot_string *))proc_ptr)((godot_string *)&lib_path); + } } else { // already initialized. Nice. } @@ -1054,13 +1049,13 @@ void NativeScriptLanguage::call_libraries_cb(const StringName &name) { // library_gdnatives is modified only from the main thread, so it's safe not to use mutex here for (Map<String, Ref<GDNative> >::Element *L = library_gdnatives.front(); L; L = L->next()) { if (L->get()->is_initialized()) { - L->get()->call_native_raw( - _noarg_call_type, - name, - NULL, - 0, - NULL, - NULL); + + void *proc_ptr; + Error err = L->get()->get_symbol(name, proc_ptr); + + if (!err) { + ((void (*)())proc_ptr)(); + } } } } @@ -1143,12 +1138,14 @@ void NativeReloadNode::_notification(int p_what) { }; // here the library registers all the classes and stuff. - L->get()->call_native_raw(NSL->_init_call_type, - NSL->_init_call_name, - NULL, - 1, - args, - NULL); + + void *proc_ptr; + Error err = L->get()->get_symbol("godot_nativescript_init", proc_ptr); + if (err != OK) { + ERR_PRINT(String("No godot_nativescript_init in \"" + L->key() + "\" found").utf8().get_data()); + } else { + ((void (*)(void *))proc_ptr)((void *)&L->key()); + } for (Map<String, Set<NativeScript *> >::Element *U = NSL->library_script_users.front(); U; U = U->next()) { for (Set<NativeScript *>::Element *S = U->get().front(); S; S = S->next()) { diff --git a/modules/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index 571a3c9cc7..e8fc9e6880 100644 --- a/modules/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -30,16 +30,17 @@ #ifndef NATIVE_SCRIPT_H #define NATIVE_SCRIPT_H +#include "core/resource.h" +#include "core/script_language.h" +#include "core/self_list.h" #include "io/resource_loader.h" #include "io/resource_saver.h" +#include "ordered_hash_map.h" #include "os/thread_safe.h" -#include "resource.h" #include "scene/main/node.h" -#include "script_language.h" -#include "self_list.h" -#include "godot_nativescript.h" #include "modules/gdnative/gdnative.h" +#include <nativescript/godot_nativescript.h> #ifndef NO_THREADS #include "os/mutex.h" @@ -65,7 +66,7 @@ struct NativeScriptDesc { }; Map<StringName, Method> methods; - Map<StringName, Property> properties; + OrderedHashMap<StringName, Property> properties; Map<StringName, Signal> signals_; // QtCreator doesn't like the name signals StringName base; StringName base_native_type; @@ -141,8 +142,6 @@ public: virtual bool is_tool() const; - virtual String get_node_type() const; - virtual ScriptLanguage *get_language() const; virtual bool has_script_signal(const StringName &p_signal) const; @@ -270,6 +269,7 @@ public: virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const; virtual Script *create_script() const; virtual bool has_named_classes() const; + virtual bool supports_builtin_mode() const; virtual int find_function(const String &p_function, const String &p_code) const; virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const; virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const; diff --git a/modules/nativescript/register_types.cpp b/modules/gdnative/nativescript/register_types.cpp index b846710ab8..d734bba810 100644 --- a/modules/nativescript/register_types.cpp +++ b/modules/gdnative/nativescript/register_types.cpp @@ -38,53 +38,6 @@ NativeScriptLanguage *native_script_language; -typedef void (*native_script_init_fn)(void *); - -void init_call_cb(void *p_handle, godot_string *p_proc_name, void *p_data, int p_num_args, void **args, void *r_ret) { - if (p_handle == NULL) { - ERR_PRINT("No valid library handle, can't call nativescript init procedure"); - return; - } - - void *library_proc; - Error err = OS::get_singleton()->get_dynamic_library_symbol_handle( - p_handle, - *(String *)p_proc_name, - library_proc, - true); // we print our own message - if (err != OK) { - ERR_PRINT((String("GDNative procedure \"" + *(String *)p_proc_name) + "\" does not exists and can't be called").utf8().get_data()); - return; - } - - native_script_init_fn fn = (native_script_init_fn)library_proc; - - fn(args[0]); -} - -typedef void (*native_script_empty_callback)(); - -void noarg_call_cb(void *p_handle, godot_string *p_proc_name, void *p_data, int p_num_args, void **args, void *r_ret) { - if (p_handle == NULL) { - ERR_PRINT("No valid library handle, can't call nativescript callback"); - return; - } - - void *library_proc; - Error err = OS::get_singleton()->get_dynamic_library_symbol_handle( - p_handle, - *(String *)p_proc_name, - library_proc, - true); - if (err != OK) { - // it's fine if thread callbacks are not present in the library. - return; - } - - native_script_empty_callback fn = (native_script_empty_callback)library_proc; - fn(); -} - ResourceFormatLoaderNativeScript *resource_loader_gdns = NULL; ResourceFormatSaverNativeScript *resource_saver_gdns = NULL; @@ -95,9 +48,6 @@ void register_nativescript_types() { ScriptServer::register_language(native_script_language); - GDNativeCallRegistry::singleton->register_native_raw_call_type(native_script_language->_init_call_type, init_call_cb); - GDNativeCallRegistry::singleton->register_native_raw_call_type(native_script_language->_noarg_call_type, noarg_call_cb); - resource_saver_gdns = memnew(ResourceFormatSaverNativeScript); ResourceSaver::add_resource_format_saver(resource_saver_gdns); diff --git a/modules/nativescript/register_types.h b/modules/gdnative/nativescript/register_types.h index 7ac558f68f..7ac558f68f 100644 --- a/modules/nativescript/register_types.h +++ b/modules/gdnative/nativescript/register_types.h diff --git a/modules/gdnative/pluginscript/SCsub b/modules/gdnative/pluginscript/SCsub new file mode 100644 index 0000000000..2031a4236b --- /dev/null +++ b/modules/gdnative/pluginscript/SCsub @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +env_pluginscript = env_modules.Clone() + +env_pluginscript.Append(CPPPATH=['#modules/gdnative/include/']) +env_pluginscript.add_source_files(env.modules_sources, '*.cpp') diff --git a/modules/gdnative/pluginscript/pluginscript_instance.cpp b/modules/gdnative/pluginscript/pluginscript_instance.cpp new file mode 100644 index 0000000000..8f01350826 --- /dev/null +++ b/modules/gdnative/pluginscript/pluginscript_instance.cpp @@ -0,0 +1,181 @@ +/*************************************************************************/ +/* pluginscript_instance.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +// Godot imports +#include "core/os/os.h" +#include "core/variant.h" +// PluginScript imports +#include "pluginscript_instance.h" +#include "pluginscript_language.h" +#include "pluginscript_script.h" + +bool PluginScriptInstance::set(const StringName &p_name, const Variant &p_value) { + String name = String(p_name); + return _desc->set_prop(_data, (const godot_string *)&name, (const godot_variant *)&p_value); +} + +bool PluginScriptInstance::get(const StringName &p_name, Variant &r_ret) const { + String name = String(p_name); + return _desc->get_prop(_data, (const godot_string *)&name, (godot_variant *)&r_ret); +} + +Ref<Script> PluginScriptInstance::get_script() const { + return _script; +} + +ScriptLanguage *PluginScriptInstance::get_language() { + return _script->get_language(); +} + +Variant::Type PluginScriptInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const { + if (!_script->has_property(p_name)) { + if (r_is_valid) { + *r_is_valid = false; + } + return Variant::NIL; + } + if (r_is_valid) { + *r_is_valid = true; + } + return _script->get_property_info(p_name).type; +} + +void PluginScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const { + _script->get_script_property_list(p_properties); +} + +void PluginScriptInstance::get_method_list(List<MethodInfo> *p_list) const { + _script->get_script_method_list(p_list); +} + +bool PluginScriptInstance::has_method(const StringName &p_method) const { + return _script->has_method(p_method); +} + +Variant PluginScriptInstance::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) { + // TODO: optimize when calling a Godot method from Godot to avoid param conversion ? + godot_variant ret = _desc->call_method( + _data, (godot_string_name *)&p_method, (const godot_variant **)p_args, + p_argcount, (godot_variant_call_error *)&r_error); + Variant *var_ret = (Variant *)&ret; + return *var_ret; +} + +#if 0 // TODO: Don't rely on default implementations provided by ScriptInstance ? +void PluginScriptInstance::call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount) { + +#if 0 + PluginScript *sptr=script.ptr(); + Variant::CallError ce; + + while(sptr) { + Map<StringName,GDFunction*>::Element *E = sptr->member_functions.find(p_method); + if (E) { + E->get()->call(this,p_args,p_argcount,ce); + } + sptr = sptr->_base; + } +#endif + +} + +#if 0 +void PluginScriptInstance::_ml_call_reversed(PluginScript *sptr,const StringName& p_method,const Variant** p_args,int p_argcount) { + + if (sptr->_base) + _ml_call_reversed(sptr->_base,p_method,p_args,p_argcount); + + Variant::CallError ce; + + Map<StringName,GDFunction*>::Element *E = sptr->member_functions.find(p_method); + if (E) { + E->get()->call(this,p_args,p_argcount,ce); + } + +} +#endif + + +void PluginScriptInstance::call_multilevel_reversed(const StringName& p_method,const Variant** p_args,int p_argcount) { + +#if 0 + if (script.ptr()) { + _ml_call_reversed(script.ptr(),p_method,p_args,p_argcount); + } +#endif +} +#endif // Multilevel stuff + +void PluginScriptInstance::notification(int p_notification) { + _desc->notification(_data, p_notification); +} + +ScriptInstance::RPCMode PluginScriptInstance::get_rpc_mode(const StringName &p_method) const { + return _script->get_rpc_mode(p_method); +} + +ScriptInstance::RPCMode PluginScriptInstance::get_rset_mode(const StringName &p_variable) const { + return _script->get_rset_mode(p_variable); +} + +void PluginScriptInstance::refcount_incremented() { + if (_desc->refcount_decremented) { + _desc->refcount_incremented(_data); + } +} + +bool PluginScriptInstance::refcount_decremented() { + // Return true if it can die + if (_desc->refcount_decremented) { + return _desc->refcount_decremented(_data); + } + return true; +} + +PluginScriptInstance::PluginScriptInstance() { +} + +bool PluginScriptInstance::init(PluginScript *p_script, Object *p_owner) { + _owner = p_owner; + _owner_variant = Variant(p_owner); + _script = Ref<PluginScript>(p_script); + _desc = &p_script->_desc->instance_desc; + _data = _desc->init(p_script->_data, (godot_object *)p_owner); + ERR_FAIL_COND_V(_data == NULL, false); + p_owner->set_script_instance(this); + return true; +} + +PluginScriptInstance::~PluginScriptInstance() { + _desc->finish(_data); + _script->_language->lock(); + _script->_instances.erase(_owner); + _script->_language->unlock(); +} diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h new file mode 100644 index 0000000000..68696b4417 --- /dev/null +++ b/modules/gdnative/pluginscript/pluginscript_instance.h @@ -0,0 +1,90 @@ +/*************************************************************************/ +/* pluginscript_instance.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 PLUGINSCRIPT_INSTANCE_H +#define PLUGINSCRIPT_INSTANCE_H + +// Godot imports +#include "core/script_language.h" +// PluginScript imports +#include <pluginscript/godot_pluginscript.h> + +class PluginScript; + +class PluginScriptInstance : public ScriptInstance { + friend class PluginScript; + +private: + Ref<PluginScript> _script; + Object *_owner; + Variant _owner_variant; + godot_pluginscript_instance_data *_data; + const godot_pluginscript_instance_desc *_desc; + +public: + _FORCE_INLINE_ Object *get_owner() { return _owner; } + + virtual bool set(const StringName &p_name, const Variant &p_value); + virtual bool get(const StringName &p_name, Variant &r_ret) const; + virtual void get_property_list(List<PropertyInfo> *p_properties) const; + virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = NULL) const; + + virtual void get_method_list(List<MethodInfo> *p_list) const; + virtual bool has_method(const StringName &p_method) const; + + virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error); +#if 0 + // Rely on default implementations provided by ScriptInstance for the moment. + // Note that multilevel call could be removed in 3.0 release, so stay tunned + // (see https://godotengine.org/qa/9244/can-override-the-_ready-and-_process-functions-child-classes) + virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount); + virtual void call_multilevel_reversed(const StringName& p_method,const Variant** p_args,int p_argcount); +#endif + + virtual void notification(int p_notification); + + virtual Ref<Script> get_script() const; + + virtual ScriptLanguage *get_language(); + + void set_path(const String &p_path); + + virtual RPCMode get_rpc_mode(const StringName &p_method) const; + virtual RPCMode get_rset_mode(const StringName &p_variable) const; + + virtual void refcount_incremented(); + virtual bool refcount_decremented(); + + PluginScriptInstance(); + bool init(PluginScript *p_script, Object *p_owner); + virtual ~PluginScriptInstance(); +}; + +#endif // PLUGINSCRIPT_INSTANCE_H diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp new file mode 100644 index 0000000000..40feb5ae43 --- /dev/null +++ b/modules/gdnative/pluginscript/pluginscript_language.cpp @@ -0,0 +1,435 @@ +/*************************************************************************/ +/* pluginscript_language.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +// Godot imports +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" +// PluginScript imports +#include "pluginscript_language.h" +#include "pluginscript_script.h" + +String PluginScriptLanguage::get_name() const { + return String(_desc.name); +} + +void PluginScriptLanguage::init() { + _data = _desc.init(); +} + +String PluginScriptLanguage::get_type() const { + return String(_desc.type); +} + +String PluginScriptLanguage::get_extension() const { + return String(_desc.extension); +} + +Error PluginScriptLanguage::execute_file(const String &p_path) { + // TODO: pretty sure this method is totally deprecated and should be removed... + return OK; +} + +void PluginScriptLanguage::finish() { + _desc.finish(_data); +} + +/* EDITOR FUNCTIONS */ + +void PluginScriptLanguage::get_reserved_words(List<String> *p_words) const { + if (_desc.reserved_words) { + const char **w = _desc.reserved_words; + while (*w) { + p_words->push_back(*w); + w++; + } + } +} + +void PluginScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const { + if (_desc.comment_delimiters) { + const char **w = _desc.comment_delimiters; + while (*w) { + p_delimiters->push_back(*w); + w++; + } + } +} + +void PluginScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { + if (_desc.string_delimiters) { + const char **w = _desc.string_delimiters; + while (*w) { + p_delimiters->push_back(*w); + w++; + } + } +} + +Ref<Script> PluginScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { + Script *ns = create_script(); + Ref<Script> script = Ref<Script>(ns); + if (_desc.get_template_source_code) { + godot_string src = _desc.get_template_source_code(_data, (godot_string *)&p_class_name, (godot_string *)&p_base_class_name); + script->set_source_code(*(String *)&src); + } + return script; +} + +bool PluginScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const { + PoolStringArray functions; + if (_desc.validate) { + bool ret = _desc.validate( + _data, + (godot_string *)&p_script, + &r_line_error, + &r_col_error, + (godot_string *)&r_test_error, + (godot_string *)&p_path, + (godot_pool_string_array *)&functions); + for (int i = 0; i < functions.size(); i++) { + r_functions->push_back(functions[i]); + } + return ret; + } + return true; +} + +Script *PluginScriptLanguage::create_script() const { + PluginScript *script = memnew(PluginScript()); + // I'm hurting kittens doing this I guess... + script->init(const_cast<PluginScriptLanguage *>(this)); + return script; +} + +bool PluginScriptLanguage::has_named_classes() const { + return _desc.has_named_classes; +} + +bool PluginScriptLanguage::supports_builtin_mode() const { + return _desc.supports_builtin_mode; +} + +int PluginScriptLanguage::find_function(const String &p_function, const String &p_code) const { + if (_desc.find_function) { + return _desc.find_function(_data, (godot_string *)&p_function, (godot_string *)&p_code); + } + return -1; +} + +String PluginScriptLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const { + if (_desc.make_function) { + godot_string tmp = _desc.make_function(_data, (godot_string *)&p_class, (godot_string *)&p_name, (godot_pool_string_array *)&p_args); + String ret = *(String *)&tmp; + godot_string_destroy(&tmp); + return ret; + } + return String(); +} + +Error PluginScriptLanguage::complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint) { + if (_desc.complete_code) { + Array options; + godot_error tmp = _desc.complete_code( + _data, + (godot_string *)&p_code, + (godot_string *)&p_base_path, + (godot_object *)p_owner, + (godot_array *)&options, + &r_force, + (godot_string *)&r_call_hint); + for (int i = 0; i < options.size(); i++) { + r_options->push_back(String(options[i])); + } + Error err = *(Error *)tmp; + return err; + } + return ERR_UNAVAILABLE; +} + +void PluginScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_to_line) const { + if (_desc.auto_indent_code) { + _desc.auto_indent_code(_data, (godot_string *)&p_code, p_from_line, p_to_line); + } + return; +} + +void PluginScriptLanguage::add_global_constant(const StringName &p_variable, const Variant &p_value) { + const String variable = String(p_variable); + _desc.add_global_constant(_data, (godot_string *)&variable, (godot_variant *)&p_value); +} + +/* LOADER FUNCTIONS */ + +void PluginScriptLanguage::get_recognized_extensions(List<String> *p_extensions) const { + for (int i = 0; _desc.recognized_extensions[i]; ++i) { + p_extensions->push_back(String(_desc.recognized_extensions[i])); + } +} + +void PluginScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const { + // TODO: provid this statically in `godot_pluginscript_language_desc` ? + if (_desc.get_public_functions) { + Array functions; + _desc.get_public_functions(_data, (godot_array *)&functions); + for (int i = 0; i < functions.size(); i++) { + MethodInfo mi = MethodInfo::from_dict(functions[i]); + p_functions->push_back(mi); + } + } +} + +void PluginScriptLanguage::get_public_constants(List<Pair<String, Variant> > *p_constants) const { + // TODO: provid this statically in `godot_pluginscript_language_desc` ? + if (_desc.get_public_constants) { + Dictionary constants; + _desc.get_public_constants(_data, (godot_dictionary *)&constants); + for (const Variant *key = constants.next(); key; key = constants.next(key)) { + Variant value = constants[key]; + p_constants->push_back(Pair<String, Variant>(*key, value)); + } + } +} + +void PluginScriptLanguage::profiling_start() { +#ifdef DEBUG_ENABLED + if (_desc.profiling_start) { + lock(); + _desc.profiling_start(_data); + unlock(); + } +#endif +} + +void PluginScriptLanguage::profiling_stop() { +#ifdef DEBUG_ENABLED + if (_desc.profiling_stop) { + lock(); + _desc.profiling_stop(_data); + unlock(); + } +#endif +} + +int PluginScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) { + int info_count = 0; +#ifdef DEBUG_ENABLED + if (_desc.profiling_get_accumulated_data) { + godot_pluginscript_profiling_data *info = (godot_pluginscript_profiling_data *)memalloc( + sizeof(godot_pluginscript_profiling_data) * p_info_max); + info_count = _desc.profiling_get_accumulated_data(_data, info, p_info_max); + for (int i = 0; i < info_count; ++i) { + p_info_arr[i].signature = *(StringName *)&info[i].signature; + p_info_arr[i].call_count = info[i].call_count; + p_info_arr[i].total_time = info[i].total_time; + p_info_arr[i].self_time = info[i].self_time; + godot_string_name_destroy(&info[i].signature); + } + } +#endif + return info_count; +} + +int PluginScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) { + int info_count = 0; +#ifdef DEBUG_ENABLED + if (_desc.profiling_get_frame_data) { + godot_pluginscript_profiling_data *info = (godot_pluginscript_profiling_data *)memalloc( + sizeof(godot_pluginscript_profiling_data) * p_info_max); + info_count = _desc.profiling_get_frame_data(_data, info, p_info_max); + for (int i = 0; i < info_count; ++i) { + p_info_arr[i].signature = *(StringName *)&info[i].signature; + p_info_arr[i].call_count = info[i].call_count; + p_info_arr[i].total_time = info[i].total_time; + p_info_arr[i].self_time = info[i].self_time; + godot_string_name_destroy(&info[i].signature); + } + } +#endif + return info_count; +} + +void PluginScriptLanguage::frame() { +#ifdef DEBUG_ENABLED + if (_desc.profiling_frame) { + _desc.profiling_frame(_data); + } +#endif +} + +/* DEBUGGER FUNCTIONS */ + +String PluginScriptLanguage::debug_get_error() const { + if (_desc.debug_get_error) { + godot_string tmp = _desc.debug_get_error(_data); + String ret = *(String *)&tmp; + godot_string_destroy(&tmp); + return ret; + } + return String("Nothing"); +} + +int PluginScriptLanguage::debug_get_stack_level_count() const { + if (_desc.debug_get_stack_level_count) { + return _desc.debug_get_stack_level_count(_data); + } + return 1; +} + +int PluginScriptLanguage::debug_get_stack_level_line(int p_level) const { + if (_desc.debug_get_stack_level_line) { + return _desc.debug_get_stack_level_line(_data, p_level); + } + return 1; +} + +String PluginScriptLanguage::debug_get_stack_level_function(int p_level) const { + if (_desc.debug_get_stack_level_function) { + godot_string tmp = _desc.debug_get_stack_level_function(_data, p_level); + String ret = *(String *)&tmp; + godot_string_destroy(&tmp); + return ret; + } + return String("Nothing"); +} + +String PluginScriptLanguage::debug_get_stack_level_source(int p_level) const { + if (_desc.debug_get_stack_level_source) { + godot_string tmp = _desc.debug_get_stack_level_source(_data, p_level); + String ret = *(String *)&tmp; + godot_string_destroy(&tmp); + return ret; + } + return String("Nothing"); +} + +void PluginScriptLanguage::debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) { + if (_desc.debug_get_stack_level_locals) { + PoolStringArray locals; + Array values; + _desc.debug_get_stack_level_locals(_data, p_level, (godot_pool_string_array *)&locals, (godot_array *)&values, p_max_subitems, p_max_depth); + for (int i = 0; i < locals.size(); i++) { + p_locals->push_back(locals[i]); + } + for (int i = 0; i < values.size(); i++) { + p_values->push_back(values[i]); + } + } +} + +void PluginScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) { + if (_desc.debug_get_stack_level_members) { + PoolStringArray members; + Array values; + _desc.debug_get_stack_level_members(_data, p_level, (godot_pool_string_array *)&members, (godot_array *)&values, p_max_subitems, p_max_depth); + for (int i = 0; i < members.size(); i++) { + p_members->push_back(members[i]); + } + for (int i = 0; i < values.size(); i++) { + p_values->push_back(values[i]); + } + } +} + +void PluginScriptLanguage::debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) { + if (_desc.debug_get_globals) { + PoolStringArray locals; + Array values; + _desc.debug_get_globals(_data, (godot_pool_string_array *)&locals, (godot_array *)&values, p_max_subitems, p_max_depth); + for (int i = 0; i < locals.size(); i++) { + p_locals->push_back(locals[i]); + } + for (int i = 0; i < values.size(); i++) { + p_values->push_back(values[i]); + } + } +} + +String PluginScriptLanguage::debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) { + if (_desc.debug_parse_stack_level_expression) { + godot_string tmp = _desc.debug_parse_stack_level_expression(_data, p_level, (godot_string *)&p_expression, p_max_subitems, p_max_depth); + String ret = *(String *)&tmp; + godot_string_destroy(&tmp); + return ret; + } + return String("Nothing"); +} + +void PluginScriptLanguage::reload_all_scripts() { + // TODO +} + +void PluginScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) { +#ifdef DEBUG_ENABLED + lock(); + // TODO + unlock(); +#endif +} + +void PluginScriptLanguage::lock() { +#ifndef NO_THREADS + if (_lock) { + _lock->lock(); + } +#endif +} + +void PluginScriptLanguage::unlock() { +#ifndef NO_THREADS + if (_lock) { + _lock->unlock(); + } +#endif +} + +PluginScriptLanguage::PluginScriptLanguage(const godot_pluginscript_language_desc *desc) + : _desc(*desc) { + _resource_loader = memnew(ResourceFormatLoaderPluginScript(this)); + _resource_saver = memnew(ResourceFormatSaverPluginScript(this)); + +// TODO: totally remove _lock attribute if NO_THREADS is set +#ifdef NO_THREADS + _lock = NULL; +#else + _lock = Mutex::create(); +#endif +} + +PluginScriptLanguage::~PluginScriptLanguage() { + memdelete(_resource_loader); + memdelete(_resource_saver); +#ifndef NO_THREADS + if (_lock) { + memdelete(_lock); + _lock = NULL; + } +#endif +} diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h new file mode 100644 index 0000000000..79b95ff4e6 --- /dev/null +++ b/modules/gdnative/pluginscript/pluginscript_language.h @@ -0,0 +1,132 @@ +/*************************************************************************/ +/* pluginscript_language.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 PLUGINSCRIPT_LANGUAGE_H +#define PLUGINSCRIPT_LANGUAGE_H + +// Godot imports +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/map.h" +#include "core/script_language.h" +#include "core/self_list.h" +// PluginScript imports +#include "pluginscript_loader.h" +#include <pluginscript/godot_pluginscript.h> + +class PluginScript; +class PluginScriptInstance; + +class PluginScriptLanguage : public ScriptLanguage { + friend class PluginScript; + friend class PluginScriptInstance; + + ResourceFormatLoaderPluginScript *_resource_loader; + ResourceFormatSaverPluginScript *_resource_saver; + const godot_pluginscript_language_desc _desc; + godot_pluginscript_language_data *_data; + + Mutex *_lock; + SelfList<PluginScript>::List _script_list; + +public: + virtual String get_name() const; + + _FORCE_INLINE_ ResourceFormatLoaderPluginScript *get_resource_loader() { return _resource_loader; }; + _FORCE_INLINE_ ResourceFormatSaverPluginScript *get_resource_saver() { return _resource_saver; }; + + /* LANGUAGE FUNCTIONS */ + virtual void init(); + virtual String get_type() const; + virtual String get_extension() const; + virtual Error execute_file(const String &p_path); + virtual void finish(); + + /* EDITOR FUNCTIONS */ + virtual void get_reserved_words(List<String> *p_words) const; + virtual void get_comment_delimiters(List<String> *p_delimiters) const; + virtual void get_string_delimiters(List<String> *p_delimiters) const; + virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; + virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const; + virtual Script *create_script() const; + virtual bool has_named_classes() const; + virtual bool supports_builtin_mode() const; + virtual bool can_inherit_from_file() { return true; } + virtual int find_function(const String &p_function, const String &p_code) const; + virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const; + virtual Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint); + virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const; + virtual void add_global_constant(const StringName &p_variable, const Variant &p_value); + + /* MULTITHREAD FUNCTIONS */ + + //some VMs need to be notified of thread creation/exiting to allocate a stack + // void thread_enter() {} + // void thread_exit() {} + + /* DEBUGGER FUNCTIONS */ + + virtual String debug_get_error() const; + virtual int debug_get_stack_level_count() const; + virtual int debug_get_stack_level_line(int p_level) const; + virtual String debug_get_stack_level_function(int p_level) const; + virtual String debug_get_stack_level_source(int p_level) const; + virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); + virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); + virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); + virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1); + + // virtual Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); } + + virtual void reload_all_scripts(); + virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload); + + /* LOADER FUNCTIONS */ + + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual void get_public_functions(List<MethodInfo> *p_functions) const; + virtual void get_public_constants(List<Pair<String, Variant> > *p_constants) const; + + virtual void profiling_start(); + virtual void profiling_stop(); + + virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max); + virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max); + + virtual void frame(); + + void lock(); + void unlock(); + + PluginScriptLanguage(const godot_pluginscript_language_desc *desc); + virtual ~PluginScriptLanguage(); +}; + +#endif // PLUGINSCRIPT_LANGUAGE_H diff --git a/modules/gdnative/pluginscript/pluginscript_loader.cpp b/modules/gdnative/pluginscript/pluginscript_loader.cpp new file mode 100644 index 0000000000..3648e1a5b4 --- /dev/null +++ b/modules/gdnative/pluginscript/pluginscript_loader.cpp @@ -0,0 +1,113 @@ +/*************************************************************************/ +/* pluginscript_loader.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +// Godot imports +#include "os/file_access.h" +// Pythonscript imports +#include "pluginscript_language.h" +#include "pluginscript_loader.h" +#include "pluginscript_script.h" + +ResourceFormatLoaderPluginScript::ResourceFormatLoaderPluginScript(PluginScriptLanguage *language) { + _language = language; +} + +RES ResourceFormatLoaderPluginScript::load(const String &p_path, const String &p_original_path, Error *r_error) { + if (r_error) + *r_error = ERR_FILE_CANT_OPEN; + + PluginScript *script = memnew(PluginScript); + script->init(_language); + + Ref<PluginScript> scriptres(script); + + Error err = script->load_source_code(p_path); + ERR_FAIL_COND_V(err != OK, RES()); + + script->set_path(p_original_path); + + script->reload(); + + if (r_error) + *r_error = OK; + + return scriptres; +} + +void ResourceFormatLoaderPluginScript::get_recognized_extensions(List<String> *p_extensions) const { + p_extensions->push_back(_language->get_extension()); +} + +bool ResourceFormatLoaderPluginScript::handles_type(const String &p_type) const { + return p_type == "Script" || p_type == _language->get_type(); +} + +String ResourceFormatLoaderPluginScript::get_resource_type(const String &p_path) const { + String el = p_path.get_extension().to_lower(); + if (el == _language->get_extension()) + return _language->get_type(); + return ""; +} + +ResourceFormatSaverPluginScript::ResourceFormatSaverPluginScript(PluginScriptLanguage *language) { + _language = language; +} + +Error ResourceFormatSaverPluginScript::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { + Ref<PluginScript> sqscr = p_resource; + ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER); + + String source = sqscr->get_source_code(); + + Error err; + FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); + ERR_FAIL_COND_V(err, err); + + file->store_string(source); + if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { + memdelete(file); + return ERR_CANT_CREATE; + } + file->close(); + memdelete(file); + return OK; +} + +void ResourceFormatSaverPluginScript::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { + + if (Object::cast_to<PluginScript>(*p_resource)) { + p_extensions->push_back(_language->get_extension()); + } +} + +bool ResourceFormatSaverPluginScript::recognize(const RES &p_resource) const { + + return Object::cast_to<PluginScript>(*p_resource) != NULL; +} diff --git a/modules/gdnative/pluginscript/pluginscript_loader.h b/modules/gdnative/pluginscript/pluginscript_loader.h new file mode 100644 index 0000000000..b85e7725a1 --- /dev/null +++ b/modules/gdnative/pluginscript/pluginscript_loader.h @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* pluginscript_loader.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 PYTHONSCRIPT_PY_LOADER_H +#define PYTHONSCRIPT_PY_LOADER_H + +// Godot imports +#include "core/script_language.h" +#include "io/resource_loader.h" +#include "io/resource_saver.h" + +class PluginScriptLanguage; + +class ResourceFormatLoaderPluginScript : public ResourceFormatLoader { + PluginScriptLanguage *_language; + +public: + ResourceFormatLoaderPluginScript(PluginScriptLanguage *language); + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + +class ResourceFormatSaverPluginScript : public ResourceFormatSaver { + PluginScriptLanguage *_language; + +public: + ResourceFormatSaverPluginScript(PluginScriptLanguage *language); + virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); + virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; + virtual bool recognize(const RES &p_resource) const; +}; + +#endif // PYTHONSCRIPT_PY_LOADER_H diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp new file mode 100644 index 0000000000..4169b07f63 --- /dev/null +++ b/modules/gdnative/pluginscript/pluginscript_script.cpp @@ -0,0 +1,449 @@ +/*************************************************************************/ +/* pluginscript_script.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +// Godot imports +#include "core/os/file_access.h" +// PluginScript imports +#include "pluginscript_instance.h" +#include "pluginscript_script.h" + +#if DEBUG_ENABLED +#define __ASSERT_SCRIPT_REASON "Cannot retrieve pluginscript class for this script, is you code correct ?" +#define ASSERT_SCRIPT_VALID() \ + { \ + ERR_EXPLAIN(__ASSERT_SCRIPT_REASON); \ + ERR_FAIL_COND(!can_instance()) \ + } +#define ASSERT_SCRIPT_VALID_V(ret) \ + { \ + ERR_EXPLAIN(__ASSERT_SCRIPT_REASON); \ + ERR_FAIL_COND_V(!can_instance(), ret) \ + } +#else +#define ASSERT_SCRIPT_VALID() +#define ASSERT_SCRIPT_VALID_V(ret) +#endif + +void PluginScript::_bind_methods() { +} + +#ifdef TOOLS_ENABLED + +void PluginScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) { + placeholders.erase(p_placeholder); +} + +#endif + +bool PluginScript::can_instance() const { + bool can = _valid || (!_tool && !ScriptServer::is_scripting_enabled()); + return can; +} + +Ref<Script> PluginScript::get_base_script() const { + if (_ref_base_parent.is_valid()) { + return Ref<PluginScript>(_ref_base_parent); + } else { + return Ref<Script>(); + } +} + +StringName PluginScript::get_instance_base_type() const { + if (_native_parent) + return _native_parent; + if (_ref_base_parent.is_valid()) + return _ref_base_parent->get_instance_base_type(); + return StringName(); +} + +void PluginScript::update_exports() { +// TODO +#ifdef TOOLS_ENABLED +#if 0 + ASSERT_SCRIPT_VALID(); + if (/*changed &&*/ placeholders.size()) { //hm :( + + //update placeholders if any + Map<StringName, Variant> propdefvalues; + List<PropertyInfo> propinfos; + const String *props = (const String *)pybind_get_prop_list(_py_exposed_class); + for (int i = 0; props[i] != ""; ++i) { + const String propname = props[i]; + pybind_get_prop_default_value(_py_exposed_class, propname.c_str(), (godot_variant *)&propdefvalues[propname]); + pybind_prop_info raw_info; + pybind_get_prop_info(_py_exposed_class, propname.c_str(), &raw_info); + PropertyInfo info; + info.type = (Variant::Type)raw_info.type; + info.name = propname; + info.hint = (PropertyHint)raw_info.hint; + info.hint_string = *(String *)&raw_info.hint_string; + info.usage = raw_info.usage; + propinfos.push_back(info); + } + for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { + E->get()->update(propinfos, propdefvalues); + } + } +#endif +#endif +} + +// TODO: rename p_this "p_owner" ? +ScriptInstance *PluginScript::instance_create(Object *p_this) { + ASSERT_SCRIPT_VALID_V(NULL); + // TODO check script validity ? + if (!_tool && !ScriptServer::is_scripting_enabled()) { +#ifdef TOOLS_ENABLED + // Instance a fake script for editing the values + PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(get_language(), Ref<Script>(this), p_this)); + placeholders.insert(si); + update_exports(); + return si; +#else + return NULL; +#endif + } + + PluginScript *top = this; + // TODO: can be optimized by storing a PluginScript::_base_parent direct pointer + while (top->_ref_base_parent.is_valid()) + top = top->_ref_base_parent.ptr(); + if (top->_native_parent) { + if (!ClassDB::is_parent_class(p_this->get_class_name(), top->_native_parent)) { + String msg = "Script inherits from native type '" + String(top->_native_parent) + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'"; + // TODO: implement PluginscriptLanguage::debug_break_parse + // if (ScriptDebugger::get_singleton()) { + // _language->debug_break_parse(get_path(), 0, msg); + // } + ERR_EXPLAIN(msg); + ERR_FAIL_V(NULL); + } + } + + PluginScriptInstance *instance = memnew(PluginScriptInstance()); + const bool success = instance->init(this, p_this); + if (success) { + _language->lock(); + _instances.insert(instance->get_owner()); + _language->unlock(); + return instance; + } else { + memdelete(instance); + ERR_FAIL_V(NULL); + } +} + +bool PluginScript::instance_has(const Object *p_this) const { + _language->lock(); + bool hasit = _instances.has((Object *)p_this); + _language->unlock(); + return hasit; +} + +bool PluginScript::has_source_code() const { + bool has = _source != ""; + return has; +} + +String PluginScript::get_source_code() const { + return _source; +} + +void PluginScript::set_source_code(const String &p_code) { + if (_source == p_code) + return; + _source = p_code; +} + +Error PluginScript::reload(bool p_keep_state) { + _language->lock(); + ERR_FAIL_COND_V(!p_keep_state && _instances.size(), ERR_ALREADY_IN_USE); + _language->unlock(); + + _valid = false; + String basedir = _path; + + if (basedir == "") + basedir = get_path(); + + if (basedir != "") + basedir = basedir.get_base_dir(); + + if (_data) { + _desc->finish(_data); + } + + Error err; + godot_pluginscript_script_manifest manifest = _desc->init( + _language->_data, + (godot_string *)&_path, + (godot_string *)&_source, + (godot_error *)&err); + if (err) { + // TODO: GDscript uses `ScriptDebugger` here to jump into the parsing error + return err; + } + _valid = true; + // Use the manifest to configure this script object + _data = manifest.data; + _name = *(StringName *)&manifest.name; + _tool = manifest.is_tool; + // Base name is either another PluginScript or a regular class accessible + // through ClassDB + StringName *base_name = (StringName *)&manifest.base; + for (SelfList<PluginScript> *e = _language->_script_list.first(); e != NULL; e = e->next()) { + if (e->self()->_name == *base_name) { + // Found you, base is a PluginScript ! + _ref_base_parent = Ref<PluginScript>(e->self()); + break; + } + } + if (!_ref_base_parent.is_valid()) { + // Base is a native ClassDB + if (!ClassDB::class_exists(*base_name)) { + ERR_EXPLAIN("Unknown script '" + String(_name) + "' parent '" + String(*base_name) + "'."); + ERR_FAIL_V(ERR_PARSE_ERROR); + } + _native_parent = *base_name; + } + + Dictionary *members = (Dictionary *)&manifest.member_lines; + for (const Variant *key = members->next(); key != NULL; key = members->next(key)) { + _member_lines[*key] = (*members)[key]; + } + Array *methods = (Array *)&manifest.methods; + for (int i = 0; i < methods->size(); ++i) { + Dictionary v = (*methods)[i]; + MethodInfo mi = MethodInfo::from_dict(v); + _methods_info[mi.name] = mi; + // rpc_mode is passed as an optional field and is not part of MethodInfo + Variant var = v["rpc_mode"]; + if (var == Variant()) { + _methods_rpc_mode[mi.name] = ScriptInstance::RPC_MODE_DISABLED; + } else { + _methods_rpc_mode[mi.name] = ScriptInstance::RPCMode(int(var)); + } + } + Array *signals = (Array *)&manifest.signals; + for (int i = 0; i < signals->size(); ++i) { + Variant v = (*signals)[i]; + MethodInfo mi = MethodInfo::from_dict(v); + _signals_info[mi.name] = mi; + } + Array *properties = (Array *)&manifest.properties; + for (int i = 0; i < properties->size(); ++i) { + Dictionary v = (*properties)[i]; + PropertyInfo pi = PropertyInfo::from_dict(v); + _properties_info[pi.name] = pi; + _properties_default_values[pi.name] = v["default_value"]; + // rset_mode is passed as an optional field and is not part of PropertyInfo + Variant var = v["rset_mode"]; + if (var == Variant()) { + _methods_rpc_mode[pi.name] = ScriptInstance::RPC_MODE_DISABLED; + } else { + _methods_rpc_mode[pi.name] = ScriptInstance::RPCMode(int(var)); + } + } + // Manifest's attributes must be explicitly freed + godot_string_name_destroy(&manifest.name); + godot_string_name_destroy(&manifest.base); + godot_dictionary_destroy(&manifest.member_lines); + godot_array_destroy(&manifest.methods); + godot_array_destroy(&manifest.signals); + godot_array_destroy(&manifest.properties); + +#ifdef TOOLS_ENABLED +/*for (Set<PlaceHolderScriptInstance*>::Element *E=placeholders.front();E;E=E->next()) { + + _update_placeholder(E->get()); + }*/ +#endif + return OK; +} + +void PluginScript::get_script_method_list(List<MethodInfo> *r_methods) const { + ASSERT_SCRIPT_VALID(); + for (Map<StringName, MethodInfo>::Element *e = _methods_info.front(); e != NULL; e = e->next()) { + r_methods->push_back(e->get()); + } +} + +void PluginScript::get_script_property_list(List<PropertyInfo> *r_properties) const { + ASSERT_SCRIPT_VALID(); + for (Map<StringName, PropertyInfo>::Element *e = _properties_info.front(); e != NULL; e = e->next()) { + r_properties->push_back(e->get()); + } +} + +bool PluginScript::has_method(const StringName &p_method) const { + ASSERT_SCRIPT_VALID_V(false); + return _methods_info.has(p_method); +} + +MethodInfo PluginScript::get_method_info(const StringName &p_method) const { + ASSERT_SCRIPT_VALID_V(MethodInfo()); + const Map<StringName, MethodInfo>::Element *e = _methods_info.find(p_method); + if (e != NULL) { + return e->get(); + } else { + return MethodInfo(); + } +} + +bool PluginScript::has_property(const StringName &p_method) const { + ASSERT_SCRIPT_VALID_V(false); + return _properties_info.has(p_method); +} + +PropertyInfo PluginScript::get_property_info(const StringName &p_property) const { + ASSERT_SCRIPT_VALID_V(PropertyInfo()); + const Map<StringName, PropertyInfo>::Element *e = _properties_info.find(p_property); + if (e != NULL) { + return e->get(); + } else { + return PropertyInfo(); + } +} + +bool PluginScript::get_property_default_value(const StringName &p_property, Variant &r_value) const { + ASSERT_SCRIPT_VALID_V(false); +#ifdef TOOLS_ENABLED + const Map<StringName, Variant>::Element *e = _properties_default_values.find(p_property); + if (e != NULL) { + r_value = e->get(); + return true; + } else { + return false; + } +#endif + return false; +} + +ScriptLanguage *PluginScript::get_language() const { + return _language; +} + +Error PluginScript::load_source_code(const String &p_path) { + + PoolVector<uint8_t> sourcef; + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + if (err) { + ERR_FAIL_COND_V(err, err); + } + + int len = f->get_len(); + sourcef.resize(len + 1); + PoolVector<uint8_t>::Write w = sourcef.write(); + int r = f->get_buffer(w.ptr(), len); + f->close(); + memdelete(f); + ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); + w[len] = 0; + + String s; + if (s.parse_utf8((const char *)w.ptr())) { + ERR_EXPLAIN("Script '" + p_path + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode."); + ERR_FAIL_V(ERR_INVALID_DATA); + } + + _source = s; +#ifdef TOOLS_ENABLED +// source_changed_cache=true; +#endif + _path = p_path; + return OK; +} + +bool PluginScript::has_script_signal(const StringName &p_signal) const { + ASSERT_SCRIPT_VALID_V(false); + return _signals_info.has(p_signal); +} + +void PluginScript::get_script_signal_list(List<MethodInfo> *r_signals) const { + ASSERT_SCRIPT_VALID(); + for (Map<StringName, MethodInfo>::Element *e = _signals_info.front(); e != NULL; e = e->next()) { + r_signals->push_back(e->get()); + } +} + +int PluginScript::get_member_line(const StringName &p_member) const { +#ifdef TOOLS_ENABLED + if (_member_lines.has(p_member)) + return _member_lines[p_member]; + else +#endif + return -1; +} + +ScriptInstance::RPCMode PluginScript::get_rpc_mode(const StringName &p_method) const { + ASSERT_SCRIPT_VALID_V(ScriptInstance::RPC_MODE_DISABLED); + const Map<StringName, ScriptInstance::RPCMode>::Element *e = _methods_rpc_mode.find(p_method); + if (e != NULL) { + return e->get(); + } else { + return ScriptInstance::RPC_MODE_DISABLED; + } +} + +ScriptInstance::RPCMode PluginScript::get_rset_mode(const StringName &p_variable) const { + ASSERT_SCRIPT_VALID_V(ScriptInstance::RPC_MODE_DISABLED); + const Map<StringName, ScriptInstance::RPCMode>::Element *e = _variables_rset_mode.find(p_variable); + if (e != NULL) { + return e->get(); + } else { + return ScriptInstance::RPC_MODE_DISABLED; + } +} + +PluginScript::PluginScript() + : _data(NULL), _tool(false), _valid(false), _script_list(this) { +} + +void PluginScript::init(PluginScriptLanguage *language) { + _desc = &language->_desc.script_desc; + _language = language; + +#ifdef DEBUG_ENABLED + _language->lock(); + _language->_script_list.add(&_script_list); + _language->unlock(); +#endif +} + +PluginScript::~PluginScript() { + _desc->finish(_data); + +#ifdef DEBUG_ENABLED + _language->lock(); + _language->_script_list.remove(&_script_list); + _language->unlock(); +#endif +} diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h new file mode 100644 index 0000000000..5600bca5ef --- /dev/null +++ b/modules/gdnative/pluginscript/pluginscript_script.h @@ -0,0 +1,127 @@ +/*************************************************************************/ +/* pluginscript_script.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 PLUGINSCRIPT_SCRIPT_H +#define PLUGINSCRIPT_SCRIPT_H + +// Godot imports +#include "core/script_language.h" +// PluginScript imports +#include "pluginscript_language.h" +#include <pluginscript/godot_pluginscript.h> + +class PyInstance; + +class PluginScript : public Script { + + GDCLASS(PluginScript, Script); + + friend class PluginScriptInstance; + friend class PluginScriptLanguage; + +private: + godot_pluginscript_script_data *_data; + const godot_pluginscript_script_desc *_desc; + PluginScriptLanguage *_language; + bool _tool; + bool _valid; + + Ref<PluginScript> _ref_base_parent; + StringName _native_parent; + SelfList<PluginScript> _script_list; + + Map<StringName, int> _member_lines; + Map<StringName, Variant> _properties_default_values; + Map<StringName, PropertyInfo> _properties_info; + Map<StringName, MethodInfo> _signals_info; + Map<StringName, MethodInfo> _methods_info; + Map<StringName, ScriptInstance::RPCMode> _variables_rset_mode; + Map<StringName, ScriptInstance::RPCMode> _methods_rpc_mode; + + Set<Object *> _instances; + //exported members + String _source; + String _path; + StringName _name; + +protected: + static void _bind_methods(); + +#ifdef TOOLS_ENABLED + Set<PlaceHolderScriptInstance *> placeholders; + //void _update_placeholder(PlaceHolderScriptInstance *p_placeholder); + virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder); +#endif +public: + virtual bool can_instance() const; + + virtual Ref<Script> get_base_script() const; //for script inheritance + + virtual StringName get_instance_base_type() const; // this may not work in all scripts, will return empty if so + virtual ScriptInstance *instance_create(Object *p_this); + virtual bool instance_has(const Object *p_this) const; + + virtual bool has_source_code() const; + virtual String get_source_code() const; + virtual void set_source_code(const String &p_code); + virtual Error reload(bool p_keep_state = false); + // TODO: load_source_code only allow utf-8 file, should handle bytecode as well ? + virtual Error load_source_code(const String &p_path); + + virtual bool has_method(const StringName &p_method) const; + virtual MethodInfo get_method_info(const StringName &p_method) const; + + bool has_property(const StringName &p_method) const; + PropertyInfo get_property_info(const StringName &p_property) const; + + bool is_tool() const { return _tool; } + + virtual ScriptLanguage *get_language() const; + + virtual bool has_script_signal(const StringName &p_signal) const; + virtual void get_script_signal_list(List<MethodInfo> *r_signals) const; + + virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const; + + virtual void update_exports(); + virtual void get_script_method_list(List<MethodInfo> *r_methods) const; + virtual void get_script_property_list(List<PropertyInfo> *r_propertieslist) const; + + virtual int get_member_line(const StringName &p_member) const; + + ScriptInstance::RPCMode get_rpc_mode(const StringName &p_method) const; + ScriptInstance::RPCMode get_rset_mode(const StringName &p_variable) const; + + PluginScript(); + void init(PluginScriptLanguage *language); + virtual ~PluginScript(); +}; + +#endif // PLUGINSCRIPT_SCRIPT_H diff --git a/modules/gdnative/pluginscript/register_types.cpp b/modules/gdnative/pluginscript/register_types.cpp new file mode 100644 index 0000000000..5829d08dff --- /dev/null +++ b/modules/gdnative/pluginscript/register_types.cpp @@ -0,0 +1,118 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "register_types.h" + +#include "core/project_settings.h" +#include "io/resource_loader.h" +#include "io/resource_saver.h" +#include "os/dir_access.h" +#include "os/os.h" +#include "scene/main/scene_tree.h" + +#include "pluginscript_language.h" +#include "pluginscript_script.h" +#include <pluginscript/godot_pluginscript.h> + +static List<PluginScriptLanguage *> pluginscript_languages; + +static Error _check_language_desc(const godot_pluginscript_language_desc *desc) { + ERR_FAIL_COND_V(!desc->name || desc->name == String(), ERR_BUG); + ERR_FAIL_COND_V(!desc->type || desc->type == String(), ERR_BUG); + ERR_FAIL_COND_V(!desc->extension || desc->extension == String(), ERR_BUG); + ERR_FAIL_COND_V(!desc->recognized_extensions || !desc->recognized_extensions[0], ERR_BUG); + ERR_FAIL_COND_V(!desc->init, ERR_BUG); + ERR_FAIL_COND_V(!desc->finish, ERR_BUG); + + // desc->reserved_words is not mandatory + // desc->comment_delimiters is not mandatory + // desc->string_delimiters is not mandatory + + // desc->get_template_source_code is not mandatory + // desc->validate is not mandatory + + // desc->get_template_source_code is not mandatory + // desc->validate is not mandatory + // desc->find_function is not mandatory + // desc->make_function is not mandatory + // desc->complete_code is not mandatory + // desc->auto_indent_code is not mandatory + // desc->add_global_constant is not mandatory + // desc->debug_get_error is not mandatory + // desc->debug_get_stack_level_count is not mandatory + // desc->debug_get_stack_level_line is not mandatory + // desc->debug_get_stack_level_function is not mandatory + // desc->debug_get_stack_level_source is not mandatory + // desc->debug_get_stack_level_locals is not mandatory + // desc->debug_get_stack_level_members is not mandatory + // desc->debug_get_globals is not mandatory + // desc->debug_parse_stack_level_expression is not mandatory + // desc->profiling_start is not mandatory + // desc->profiling_stop is not mandatory + // desc->profiling_get_accumulated_data is not mandatory + // desc->profiling_get_frame_data is not mandatory + // desc->frame is not mandatory + + ERR_FAIL_COND_V(!desc->script_desc.init, ERR_BUG); + ERR_FAIL_COND_V(!desc->script_desc.finish, ERR_BUG); + + ERR_FAIL_COND_V(!desc->script_desc.instance_desc.init, ERR_BUG); + ERR_FAIL_COND_V(!desc->script_desc.instance_desc.finish, ERR_BUG); + ERR_FAIL_COND_V(!desc->script_desc.instance_desc.set_prop, ERR_BUG); + ERR_FAIL_COND_V(!desc->script_desc.instance_desc.get_prop, ERR_BUG); + ERR_FAIL_COND_V(!desc->script_desc.instance_desc.call_method, ERR_BUG); + ERR_FAIL_COND_V(!desc->script_desc.instance_desc.notification, ERR_BUG); + // desc->script_desc.instance_desc.refcount_incremented is not mandatory + // desc->script_desc.instance_desc.refcount_decremented is not mandatory + return OK; +} + +void GDAPI godot_pluginscript_register_language(const godot_pluginscript_language_desc *language_desc) { + Error ret = _check_language_desc(language_desc); + if (ret) { + ERR_FAIL(); + } + PluginScriptLanguage *language = memnew(PluginScriptLanguage(language_desc)); + ScriptServer::register_language(language); + ResourceLoader::add_resource_format_loader(language->get_resource_loader()); + ResourceSaver::add_resource_format_saver(language->get_resource_saver()); + pluginscript_languages.push_back(language); +} + +void register_pluginscript_types() { + ClassDB::register_class<PluginScript>(); +} + +void unregister_pluginscript_types() { + for (List<PluginScriptLanguage *>::Element *e = pluginscript_languages.front(); e; e = e->next()) { + PluginScriptLanguage *language = e->get(); + ScriptServer::unregister_language(language); + memdelete(language); + } +} diff --git a/modules/gdnative/pluginscript/register_types.h b/modules/gdnative/pluginscript/register_types.h new file mode 100644 index 0000000000..70bbb16c62 --- /dev/null +++ b/modules/gdnative/pluginscript/register_types.h @@ -0,0 +1,31 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ +void register_pluginscript_types(); +void unregister_pluginscript_types(); diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index 9ad05b7194..87f9cddaa2 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -28,55 +28,202 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "register_types.h" +#include "gdnative/gdnative.h" + #include "gdnative.h" #include "io/resource_loader.h" #include "io/resource_saver.h" +#include "nativearvr/register_types.h" +#include "nativescript/register_types.h" +#include "pluginscript/register_types.h" + +#include "core/engine.h" #include "core/os/os.h" +#include "core/project_settings.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_node.h" +#include "gd_native_library_editor.h" +// Class used to discover singleton gdnative files + +void actual_discoverer_handler(); + +class GDNativeSingletonDiscover : public Object { + // GDCLASS(GDNativeSingletonDiscover, Object) + + virtual String get_class() const { + // okay, this is a really dirty hack. + // We're overriding get_class so we can connect it to a signal + // This works because get_class is a virtual method, so we don't + // need to register a new class to ClassDB just for this one + // little signal. + + actual_discoverer_handler(); + + return "Object"; + } +}; + +Set<String> get_gdnative_singletons(EditorFileSystemDirectory *p_dir) { + + Set<String> file_paths; + + // check children + + for (int i = 0; i < p_dir->get_file_count(); i++) { + String file_name = p_dir->get_file(i); + String file_type = p_dir->get_file_type(i); + + if (file_type != "GDNativeLibrary") { + continue; + } -godot_variant cb_standard_varcall(void *handle, godot_string *p_procedure, godot_array *p_args) { - if (handle == NULL) { - ERR_PRINT("No valid library handle, can't call standard varcall procedure"); - godot_variant ret; - godot_variant_new_nil(&ret); - return ret; + Ref<GDNativeLibrary> lib = ResourceLoader::load(p_dir->get_file_path(i)); + if (lib.is_valid() && lib->is_singleton_gdnative()) { + file_paths.insert(p_dir->get_file_path(i)); + } } - void *library_proc; - Error err = OS::get_singleton()->get_dynamic_library_symbol_handle( - handle, - *(String *)p_procedure, - library_proc, - true); // we roll our own message - if (err != OK) { - ERR_PRINT((String("GDNative procedure \"" + *(String *)p_procedure) + "\" does not exists and can't be called").utf8().get_data()); - godot_variant ret; - godot_variant_new_nil(&ret); - return ret; + // check subdirectories + for (int i = 0; i < p_dir->get_subdir_count(); i++) { + Set<String> paths = get_gdnative_singletons(p_dir->get_subdir(i)); + + for (Set<String>::Element *E = paths.front(); E; E = E->next()) { + file_paths.insert(E->get()); + } + } + + return file_paths; +} + +void actual_discoverer_handler() { + EditorFileSystemDirectory *dir = EditorFileSystem::get_singleton()->get_filesystem(); + + Set<String> file_paths = get_gdnative_singletons(dir); + + Array files; + files.resize(file_paths.size()); + int i = 0; + for (Set<String>::Element *E = file_paths.front(); E; i++, E = E->next()) { + files.set(i, E->get()); } + ProjectSettings::get_singleton()->set("gdnative/singletons", files); + + ProjectSettings::get_singleton()->save(); +} + +GDNativeSingletonDiscover *discoverer = NULL; + +static void editor_init_callback() { + + GDNativeLibraryEditor *library_editor = memnew(GDNativeLibraryEditor); + library_editor->set_name(TTR("GDNative")); + ProjectSettingsEditor::get_singleton()->get_tabs()->add_child(library_editor); + + discoverer = memnew(GDNativeSingletonDiscover); + EditorFileSystem::get_singleton()->connect("filesystem_changed", discoverer, "get_class"); +} + +#endif + +godot_variant cb_standard_varcall(void *p_procedure_handle, godot_array *p_args) { + godot_gdnative_procedure_fn proc; - proc = (godot_gdnative_procedure_fn)library_proc; + proc = (godot_gdnative_procedure_fn)p_procedure_handle; - return proc(NULL, p_args); + return proc(p_args); } GDNativeCallRegistry *GDNativeCallRegistry::singleton; +Vector<Ref<GDNative> > singleton_gdnatives; + void register_gdnative_types() { +#ifdef TOOLS_ENABLED + + if (Engine::get_singleton()->is_editor_hint()) { + EditorNode::add_init_callback(editor_init_callback); + } +#endif + ClassDB::register_class<GDNativeLibrary>(); ClassDB::register_class<GDNative>(); GDNativeCallRegistry::singleton = memnew(GDNativeCallRegistry); GDNativeCallRegistry::singleton->register_native_call_type("standard_varcall", cb_standard_varcall); + + register_nativearvr_types(); + register_nativescript_types(); + register_pluginscript_types(); + + // run singletons + + Array singletons = Array(); + if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) { + singletons = ProjectSettings::get_singleton()->get("gdnative/singletons"); + } + + singleton_gdnatives.resize(singletons.size()); + + for (int i = 0; i < singletons.size(); i++) { + String path = singletons[i]; + + Ref<GDNativeLibrary> lib = ResourceLoader::load(path); + + singleton_gdnatives[i].instance(); + singleton_gdnatives[i]->set_library(lib); + + if (!singleton_gdnatives[i]->initialize()) { + // Can't initialize. Don't make a native_call then + continue; + } + + void *proc_ptr; + Error err = singleton_gdnatives[i]->get_symbol( + "godot_gdnative_singleton", + proc_ptr); + + if (err != OK) { + ERR_PRINT((String("No godot_gdnative_singleton in \"" + singleton_gdnatives[i]->get_library()->get_active_library_path()) + "\" found").utf8().get_data()); + } else { + ((void (*)())proc_ptr)(); + } + } } void unregister_gdnative_types() { + + for (int i = 0; i < singleton_gdnatives.size(); i++) { + + if (singleton_gdnatives[i].is_null()) { + continue; + } + + if (!singleton_gdnatives[i]->is_initialized()) { + continue; + } + + singleton_gdnatives[i]->terminate(); + } + singleton_gdnatives.clear(); + + unregister_pluginscript_types(); + unregister_nativescript_types(); + unregister_nativearvr_types(); + memdelete(GDNativeCallRegistry::singleton); +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint() && discoverer != NULL) { + memdelete(discoverer); + } +#endif + // This is for printing out the sizes of the core types /* diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index 4803781c67..7036a708e5 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -131,7 +131,7 @@ int GDCompiler::_parse_assign_right_expression(CodeGen &codegen, const GDParser: switch (p_expression->op) { case GDParser::OperatorNode::OP_ASSIGN_ADD: var_op = Variant::OP_ADD; break; - case GDParser::OperatorNode::OP_ASSIGN_SUB: var_op = Variant::OP_SUBSTRACT; break; + case GDParser::OperatorNode::OP_ASSIGN_SUB: var_op = Variant::OP_SUBTRACT; break; case GDParser::OperatorNode::OP_ASSIGN_MUL: var_op = Variant::OP_MULTIPLY; break; case GDParser::OperatorNode::OP_ASSIGN_DIV: var_op = Variant::OP_DIVIDE; break; case GDParser::OperatorNode::OP_ASSIGN_MOD: var_op = Variant::OP_MODULE; break; @@ -759,7 +759,7 @@ int GDCompiler::_parse_expression(CodeGen &codegen, const GDParser::Node *p_expr if (!_create_binary_operator(codegen, on, Variant::OP_ADD, p_stack_level)) return -1; } break; case GDParser::OperatorNode::OP_SUB: { - if (!_create_binary_operator(codegen, on, Variant::OP_SUBSTRACT, p_stack_level)) return -1; + if (!_create_binary_operator(codegen, on, Variant::OP_SUBTRACT, p_stack_level)) return -1; } break; case GDParser::OperatorNode::OP_MUL: { if (!_create_binary_operator(codegen, on, Variant::OP_MULTIPLY, p_stack_level)) return -1; diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index b10694ddfd..d9b10ff3fa 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -61,7 +61,11 @@ Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const Str "func _ready():\n" + "%TS%# Called every time the node is added to the scene.\n" + "%TS%# Initialization here\n" + - "%TS%pass\n"; + "%TS%pass\n\n" + + "#func _process(delta):\n" + + "#%TS%# Called every frame. Delta is time since last frame.\n" + + "#%TS%# Update game logic here.\n" + + "#%TS%pass\n"; _template = _template.replace("%BASE%", p_base_class_name); _template = _template.replace("%TS%", _get_indentation()); @@ -127,6 +131,11 @@ bool GDScriptLanguage::has_named_classes() const { return false; } +bool GDScriptLanguage::supports_builtin_mode() const { + + return true; +} + int GDScriptLanguage::find_function(const String &p_function, const String &p_code) const { GDTokenizerText tokenizer; @@ -297,23 +306,25 @@ void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const //not really "functions", but.. { MethodInfo mi; - mi.name = "preload:Resource"; + mi.name = "preload"; mi.arguments.push_back(PropertyInfo(Variant::STRING, "path")); mi.return_val = PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_RESOURCE_TYPE, "Resource"); p_functions->push_back(mi); } { MethodInfo mi; - mi.name = "yield:GDFunctionState"; + mi.name = "yield"; mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object")); mi.arguments.push_back(PropertyInfo(Variant::STRING, "signal")); mi.default_arguments.push_back(Variant::NIL); mi.default_arguments.push_back(Variant::STRING); + mi.return_val = PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_RESOURCE_TYPE, "GDFunctionState"); p_functions->push_back(mi); } { MethodInfo mi; mi.name = "assert"; + mi.return_val.type = Variant::NIL; mi.arguments.push_back(PropertyInfo(Variant::BOOL, "condition")); p_functions->push_back(mi); } @@ -864,7 +875,7 @@ static bool _guess_expression_type(GDCompletionContext &context, const GDParser: MethodBind *mb = ClassDB::get_method(base_type, getter); if (mb) { PropertyInfo rt = mb->get_return_info(); - if (rt.usage & PROPERTY_USAGE_CLASS_IS_ENUM && t == Variant::INT) { + if ((rt.usage & PROPERTY_USAGE_CLASS_IS_ENUM) && t == Variant::INT) { r_type.enumeration = rt.class_name; } else if (t == Variant::OBJECT) { @@ -906,7 +917,7 @@ static bool _guess_expression_type(GDCompletionContext &context, const GDParser: Variant::Operator vop = Variant::OP_MAX; switch (op->op) { case GDParser::OperatorNode::OP_ADD: vop = Variant::OP_ADD; break; - case GDParser::OperatorNode::OP_SUB: vop = Variant::OP_SUBSTRACT; break; + case GDParser::OperatorNode::OP_SUB: vop = Variant::OP_SUBTRACT; break; case GDParser::OperatorNode::OP_MUL: vop = Variant::OP_MULTIPLY; break; case GDParser::OperatorNode::OP_DIV: vop = Variant::OP_DIVIDE; break; case GDParser::OperatorNode::OP_MOD: vop = Variant::OP_MODULE; break; @@ -1901,11 +1912,11 @@ static void _find_call_arguments(GDCompletionContext &context, const GDParser::N arghint += ", "; else arghint += " "; - if (i == p_argidx) { + if (i == p_argidx || ((mi.flags & METHOD_FLAG_VARARG) && i > p_argidx)) { arghint += String::chr(0xFFFF); } arghint += _get_visual_datatype(mi.arguments[i]) + " " + mi.arguments[i].name; - if (i == p_argidx) { + if (i == p_argidx || ((mi.flags & METHOD_FLAG_VARARG) && i > p_argidx)) { arghint += String::chr(0xFFFF); } } @@ -1950,7 +1961,6 @@ static void _find_call_arguments(GDCompletionContext &context, const GDParser::N //make sure identifier exists... const GDParser::IdentifierNode *id = static_cast<const GDParser::IdentifierNode *>(op->arguments[1]); - if (op->arguments[0]->type == GDParser::Node::TYPE_SELF) { //self, look up @@ -2019,7 +2029,7 @@ static void _find_call_arguments(GDCompletionContext &context, const GDParser::N base = script->get_native(); } else if (nc.is_valid()) { - if (context.function && !context.function->_static) { + if (!(context.function && context.function->_static)) { GDCompletionIdentifier ci; ci.type = Variant::OBJECT; @@ -2101,9 +2111,9 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base for (List<String>::Element *E = opts.front(); E; E = E->next()) { String opt = E->get().strip_edges(); - if (opt.begins_with("\"") && opt.ends_with("\"")) { + if (opt.is_quoted()) { r_forced = true; - String idopt = opt.substr(1, opt.length() - 2); + String idopt = opt.unquote(); if (idopt.replace("/", "_").is_valid_identifier()) { options.insert(idopt); } else { diff --git a/modules/gdscript/gd_function.cpp b/modules/gdscript/gd_function.cpp index e6f65fe0c2..ce503b62f2 100644 --- a/modules/gdscript/gd_function.cpp +++ b/modules/gdscript/gd_function.cpp @@ -41,11 +41,12 @@ Variant *GDFunction::_get_variant(int p_address, GDInstance *p_instance, GDScrip switch ((p_address & ADDR_TYPE_MASK) >> ADDR_BITS) { case ADDR_TYPE_SELF: { - - if (!p_instance) { +#ifdef DEBUG_ENABLED + if (unlikely(!p_instance)) { r_error = "Cannot access self without instance."; return NULL; } +#endif return &self; } break; case ADDR_TYPE_CLASS: { @@ -53,18 +54,22 @@ Variant *GDFunction::_get_variant(int p_address, GDInstance *p_instance, GDScrip return &p_script->_static_ref; } break; case ADDR_TYPE_MEMBER: { - //member indexing is O(1) - if (!p_instance) { +#ifdef DEBUG_ENABLED + if (unlikely(!p_instance)) { r_error = "Cannot access member without instance."; return NULL; } +#endif + //member indexing is O(1) return &p_instance->members[address]; } break; case ADDR_TYPE_CLASS_CONSTANT: { //todo change to index! GDScript *o = p_script; +#ifdef DEBUG_ENABLED ERR_FAIL_INDEX_V(address, _global_names_count, NULL); +#endif const StringName *sn = &_global_names_ptr[address]; while (o) { @@ -84,18 +89,22 @@ Variant *GDFunction::_get_variant(int p_address, GDInstance *p_instance, GDScrip ERR_FAIL_V(NULL); } break; case ADDR_TYPE_LOCAL_CONSTANT: { +#ifdef DEBUG_ENABLED ERR_FAIL_INDEX_V(address, _constant_count, NULL); +#endif return &_constants_ptr[address]; } break; case ADDR_TYPE_STACK: case ADDR_TYPE_STACK_VARIABLE: { +#ifdef DEBUG_ENABLED ERR_FAIL_INDEX_V(address, _stack_size, NULL); +#endif return &p_stack[address]; } break; case ADDR_TYPE_GLOBAL: { - +#ifdef DEBUG_ENABLED ERR_FAIL_INDEX_V(address, GDScriptLanguage::get_singleton()->get_global_array_size(), NULL); - +#endif return &GDScriptLanguage::get_singleton()->get_global_array()[address]; } break; case ADDR_TYPE_NIL: { @@ -161,8 +170,71 @@ static String _get_var_type(const Variant *p_type) { return basestr; } +#if defined(__GNUC__) && !defined(__clang__) +#define OPCODES_TABLE \ + static const void *switch_table_ops[] = { \ + &&OPCODE_OPERATOR, \ + &&OPCODE_EXTENDS_TEST, \ + &&OPCODE_SET, \ + &&OPCODE_GET, \ + &&OPCODE_SET_NAMED, \ + &&OPCODE_GET_NAMED, \ + &&OPCODE_SET_MEMBER, \ + &&OPCODE_GET_MEMBER, \ + &&OPCODE_ASSIGN, \ + &&OPCODE_ASSIGN_TRUE, \ + &&OPCODE_ASSIGN_FALSE, \ + &&OPCODE_CONSTRUCT, \ + &&OPCODE_CONSTRUCT_ARRAY, \ + &&OPCODE_CONSTRUCT_DICTIONARY, \ + &&OPCODE_CALL, \ + &&OPCODE_CALL_RETURN, \ + &&OPCODE_CALL_BUILT_IN, \ + &&OPCODE_CALL_SELF, \ + &&OPCODE_CALL_SELF_BASE, \ + &&OPCODE_YIELD, \ + &&OPCODE_YIELD_SIGNAL, \ + &&OPCODE_YIELD_RESUME, \ + &&OPCODE_JUMP, \ + &&OPCODE_JUMP_IF, \ + &&OPCODE_JUMP_IF_NOT, \ + &&OPCODE_JUMP_TO_DEF_ARGUMENT, \ + &&OPCODE_RETURN, \ + &&OPCODE_ITERATE_BEGIN, \ + &&OPCODE_ITERATE, \ + &&OPCODE_ASSERT, \ + &&OPCODE_BREAKPOINT, \ + &&OPCODE_LINE, \ + &&OPCODE_END \ + }; + +#define OPCODE(m_op) \ + m_op: +#define OPCODE_WHILE(m_test) +#define OPCODES_END \ + OPSEXIT: +#define OPCODES_OUT \ + OPSOUT: +#define DISPATCH_OPCODE goto *switch_table_ops[_code_ptr[ip]] +#define OPCODE_SWITCH(m_test) DISPATCH_OPCODE; +#define OPCODE_BREAK goto OPSEXIT +#define OPCODE_OUT goto OPSOUT +#else +#define OPCODES_TABLE +#define OPCODE(m_op) case m_op: +#define OPCODE_WHILE(m_test) while (m_test) +#define OPCODES_END +#define OPCODES_OUT +#define DISPATCH_OPCODE continue +#define OPCODE_SWITCH(m_test) switch (m_test) +#define OPCODE_BREAK break +#define OPCODE_OUT break +#endif + Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_argcount, Variant::CallError &r_err, CallState *p_state) { + OPCODES_TABLE; + if (!_code_ptr) { return Variant(); @@ -271,16 +343,26 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a if (ScriptDebugger::get_singleton()) GDScriptLanguage::get_singleton()->enter_function(p_instance, this, stack, &ip, &line); +#define GD_ERR_BREAK(m_cond) \ + { \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Breaking..:"); \ + OPCODE_BREAK; \ + } else \ + _err_error_exists = false; \ + } + #define CHECK_SPACE(m_space) \ - ERR_BREAK((ip + m_space) > _code_size) + GD_ERR_BREAK((ip + m_space) > _code_size) #define GET_VARIANT_PTR(m_v, m_code_ofs) \ Variant *m_v; \ m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, _class, self, stack, err_text); \ - if (!m_v) \ - break; + if (unlikely(!m_v)) \ + OPCODE_BREAK; #else +#define GD_ERR_BREAK(m_cond) #define CHECK_SPACE(m_space) #define GET_VARIANT_PTR(m_v, m_code_ofs) \ Variant *m_v; \ @@ -290,8 +372,8 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a #ifdef DEBUG_ENABLED - uint64_t function_start_time; - uint64_t function_call_time; + uint64_t function_start_time = 0; + uint64_t function_call_time = 0; if (GDScriptLanguage::get_singleton()->profiling) { function_start_time = OS::get_singleton()->get_ticks_usec(); @@ -302,32 +384,36 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a #endif bool exit_ok = false; - while (ip < _code_size) { - +#ifdef DEBUG_ENABLED + OPCODE_WHILE(ip < _code_size) { int last_opcode = _code_ptr[ip]; - switch (_code_ptr[ip]) { +#else + OPCODE_WHILE(true) { +#endif - case OPCODE_OPERATOR: { + OPCODE_SWITCH(_code_ptr[ip]) { + + OPCODE(OPCODE_OPERATOR) { CHECK_SPACE(5); bool valid; Variant::Operator op = (Variant::Operator)_code_ptr[ip + 1]; - ERR_BREAK(op >= Variant::OP_MAX); + GD_ERR_BREAK(op >= Variant::OP_MAX); GET_VARIANT_PTR(a, 2); GET_VARIANT_PTR(b, 3); GET_VARIANT_PTR(dst, 4); #ifdef DEBUG_ENABLED + Variant ret; Variant::evaluate(op, *a, *b, ret, valid); #else Variant::evaluate(op, *a, *b, *dst, valid); #endif - - if (!valid) { #ifdef DEBUG_ENABLED + if (!valid) { if (ret.get_type() == Variant::STRING) { //return a string when invalid with the error @@ -336,17 +422,14 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a } else { err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'."; } -#endif - break; + OPCODE_BREAK; } -#ifdef DEBUG_ENABLED *dst = ret; #endif - ip += 5; - continue; + DISPATCH_OPCODE; } - case OPCODE_EXTENDS_TEST: { + OPCODE(OPCODE_EXTENDS_TEST) { CHECK_SPACE(4); @@ -355,19 +438,17 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a GET_VARIANT_PTR(dst, 3); #ifdef DEBUG_ENABLED - if (a->get_type() != Variant::OBJECT || a->operator Object *() == NULL) { err_text = "Left operand of 'is' is not an instance of anything."; - break; + OPCODE_BREAK; } if (b->get_type() != Variant::OBJECT || b->operator Object *() == NULL) { err_text = "Right operand of 'is' is not a class."; - break; + OPCODE_BREAK; } #endif - Object *obj_A = *a; Object *obj_B = *b; @@ -399,20 +480,21 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a GDNativeClass *nc = Object::cast_to<GDNativeClass>(obj_B); +#ifdef DEBUG_ENABLED if (!nc) { err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "')."; - break; + OPCODE_BREAK; } - +#endif extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name()); } *dst = extends_ok; ip += 4; - continue; + DISPATCH_OPCODE; } - case OPCODE_SET: { + OPCODE(OPCODE_SET) { CHECK_SPACE(3); @@ -423,6 +505,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a bool valid; dst->set(*index, *value, &valid); +#ifdef DEBUG_ENABLED if (!valid) { String v = index->operator String(); if (v != "") { @@ -431,13 +514,13 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a v = "of type '" + _get_var_type(index) + "'"; } err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "')."; - break; + OPCODE_BREAK; } - +#endif ip += 4; - continue; + DISPATCH_OPCODE; } - case OPCODE_GET: { + OPCODE(OPCODE_GET) { CHECK_SPACE(3); @@ -453,6 +536,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a *dst = src->get(*index, &valid); #endif +#ifdef DEBUG_ENABLED if (!valid) { String v = index->operator String(); if (v != "") { @@ -461,15 +545,14 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a v = "of type '" + _get_var_type(index) + "'"; } err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "')."; - break; + OPCODE_BREAK; } -#ifdef DEBUG_ENABLED *dst = ret; #endif ip += 4; - continue; + DISPATCH_OPCODE; } - case OPCODE_SET_NAMED: { + OPCODE(OPCODE_SET_NAMED) { CHECK_SPACE(3); @@ -478,22 +561,23 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a int indexname = _code_ptr[ip + 2]; - ERR_BREAK(indexname < 0 || indexname >= _global_names_count); + GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count); const StringName *index = &_global_names_ptr[indexname]; bool valid; dst->set_named(*index, *value, &valid); +#ifdef DEBUG_ENABLED if (!valid) { String err_type; err_text = "Invalid set index '" + String(*index) + "' (on base: '" + _get_var_type(dst) + "')."; - break; + OPCODE_BREAK; } - +#endif ip += 4; - continue; + DISPATCH_OPCODE; } - case OPCODE_GET_NAMED: { + OPCODE(OPCODE_GET_NAMED) { CHECK_SPACE(4); @@ -502,7 +586,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a int indexname = _code_ptr[ip + 2]; - ERR_BREAK(indexname < 0 || indexname >= _global_names_count); + GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count); const StringName *index = &_global_names_ptr[indexname]; bool valid; @@ -513,26 +597,25 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a #else *dst = src->get_named(*index, &valid); #endif - +#ifdef DEBUG_ENABLED if (!valid) { if (src->has_method(*index)) { err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "'). Did you mean '." + index->operator String() + "()' ?"; } else { err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "')."; } - break; + OPCODE_BREAK; } -#ifdef DEBUG_ENABLED *dst = ret; #endif ip += 4; - continue; + DISPATCH_OPCODE; } - case OPCODE_SET_MEMBER: { + OPCODE(OPCODE_SET_MEMBER) { CHECK_SPACE(3); int indexname = _code_ptr[ip + 1]; - ERR_BREAK(indexname < 0 || indexname >= _global_names_count); + GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count); const StringName *index = &_global_names_ptr[indexname]; GET_VARIANT_PTR(src, 2); @@ -541,20 +624,20 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a #ifdef DEBUG_ENABLED if (!ok) { err_text = "Internal error setting property: " + String(*index); - break; + OPCODE_BREAK; } else if (!valid) { err_text = "Error setting property '" + String(*index) + "' with value of type " + Variant::get_type_name(src->get_type()) + "."; - break; + OPCODE_BREAK; } #endif ip += 3; - continue; + DISPATCH_OPCODE; } - case OPCODE_GET_MEMBER: { + OPCODE(OPCODE_GET_MEMBER) { CHECK_SPACE(3); int indexname = _code_ptr[ip + 1]; - ERR_BREAK(indexname < 0 || indexname >= _global_names_count); + GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count); const StringName *index = &_global_names_ptr[indexname]; GET_VARIANT_PTR(dst, 2); bool ok = ClassDB::get_property(p_instance->owner, *index, *dst); @@ -562,13 +645,13 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a #ifdef DEBUG_ENABLED if (!ok) { err_text = "Internal error getting property: " + String(*index); - break; + OPCODE_BREAK; } #endif ip += 3; - continue; + DISPATCH_OPCODE; } - case OPCODE_ASSIGN: { + OPCODE(OPCODE_ASSIGN) { CHECK_SPACE(3); GET_VARIANT_PTR(dst, 1); @@ -577,9 +660,9 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a *dst = *src; ip += 3; - continue; + DISPATCH_OPCODE; } - case OPCODE_ASSIGN_TRUE: { + OPCODE(OPCODE_ASSIGN_TRUE) { CHECK_SPACE(2); GET_VARIANT_PTR(dst, 1); @@ -587,9 +670,9 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a *dst = true; ip += 2; - continue; + DISPATCH_OPCODE; } - case OPCODE_ASSIGN_FALSE: { + OPCODE(OPCODE_ASSIGN_FALSE) { CHECK_SPACE(2); GET_VARIANT_PTR(dst, 1); @@ -597,9 +680,9 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a *dst = false; ip += 2; - continue; + DISPATCH_OPCODE; } - case OPCODE_CONSTRUCT: { + OPCODE(OPCODE_CONSTRUCT) { CHECK_SPACE(2); Variant::Type t = Variant::Type(_code_ptr[ip + 1]); @@ -615,17 +698,19 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a Variant::CallError err; *dst = Variant::construct(t, (const Variant **)argptrs, argc, err); +#ifdef DEBUG_ENABLED if (err.error != Variant::CallError::CALL_OK) { err_text = _get_call_error(err, "'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs); - break; + OPCODE_BREAK; } +#endif ip += 4 + argc; //construct a basic type - continue; + DISPATCH_OPCODE; } - case OPCODE_CONSTRUCT_ARRAY: { + OPCODE(OPCODE_CONSTRUCT_ARRAY) { CHECK_SPACE(1); int argc = _code_ptr[ip + 1]; @@ -643,9 +728,9 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a *dst = array; ip += 3 + argc; - continue; + DISPATCH_OPCODE; } - case OPCODE_CONSTRUCT_DICTIONARY: { + OPCODE(OPCODE_CONSTRUCT_DICTIONARY) { CHECK_SPACE(1); int argc = _code_ptr[ip + 1]; @@ -665,10 +750,10 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a *dst = dict; ip += 3 + argc * 2; - continue; + DISPATCH_OPCODE; } - case OPCODE_CALL_RETURN: - case OPCODE_CALL: { + OPCODE(OPCODE_CALL_RETURN) + OPCODE(OPCODE_CALL) { CHECK_SPACE(4); bool call_ret = _code_ptr[ip] == OPCODE_CALL_RETURN; @@ -677,10 +762,10 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a GET_VARIANT_PTR(base, 2); int nameg = _code_ptr[ip + 3]; - ERR_BREAK(nameg < 0 || nameg >= _global_names_count); + GD_ERR_BREAK(nameg < 0 || nameg >= _global_names_count); const StringName *methodname = &_global_names_ptr[nameg]; - ERR_BREAK(argc < 0); + GD_ERR_BREAK(argc < 0); ip += 4; CHECK_SPACE(argc + 1); Variant **argptrs = call_args; @@ -691,7 +776,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a } #ifdef DEBUG_ENABLED - uint64_t call_time; + uint64_t call_time = 0; if (GDScriptLanguage::get_singleton()->profiling) { call_time = OS::get_singleton()->get_ticks_usec(); @@ -711,7 +796,6 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a if (GDScriptLanguage::get_singleton()->profiling) { function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; } -#endif if (err.error != Variant::CallError::CALL_OK) { @@ -731,29 +815,30 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a if (base->is_ref()) { err_text = "Attempted to free a reference."; - break; + OPCODE_BREAK; } else if (base->get_type() == Variant::OBJECT) { err_text = "Attempted to free a locked object (calling or emitting)."; - break; + OPCODE_BREAK; } } } err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs); - break; + OPCODE_BREAK; } +#endif //_call_func(NULL,base,*methodname,ip,argc,p_instance,stack); ip += argc + 1; - continue; + DISPATCH_OPCODE; } - case OPCODE_CALL_BUILT_IN: { + OPCODE(OPCODE_CALL_BUILT_IN) { CHECK_SPACE(4); GDFunctions::Function func = GDFunctions::Function(_code_ptr[ip + 1]); int argc = _code_ptr[ip + 2]; - ERR_BREAK(argc < 0); + GD_ERR_BREAK(argc < 0); ip += 3; CHECK_SPACE(argc + 1); @@ -770,6 +855,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a GDFunctions::call(func, (const Variant **)argptrs, argc, *dst, err); +#ifdef DEBUG_ENABLED if (err.error != Variant::CallError::CALL_OK) { String methodstr = GDFunctions::get_func_name(func); @@ -779,25 +865,26 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a } else { err_text = _get_call_error(err, "built-in function '" + methodstr + "'", (const Variant **)argptrs); } - break; + OPCODE_BREAK; } +#endif ip += argc + 1; - continue; + DISPATCH_OPCODE; } - case OPCODE_CALL_SELF: { + OPCODE(OPCODE_CALL_SELF) { - break; + OPCODE_BREAK; } - case OPCODE_CALL_SELF_BASE: { + OPCODE(OPCODE_CALL_SELF_BASE) { CHECK_SPACE(2); int self_fun = _code_ptr[ip + 1]; -#ifdef DEBUG_ENABLED +#ifdef DEBUG_ENABLED if (self_fun < 0 || self_fun >= _global_names_count) { err_text = "compiler bug, function name not found"; - break; + OPCODE_BREAK; } #endif const StringName *methodname = &_global_names_ptr[self_fun]; @@ -857,14 +944,14 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a String methodstr = *methodname; err_text = _get_call_error(err, "function '" + methodstr + "'", (const Variant **)argptrs); - break; + OPCODE_BREAK; } ip += 4 + argc; - continue; + DISPATCH_OPCODE; } - case OPCODE_YIELD: - case OPCODE_YIELD_SIGNAL: { + OPCODE(OPCODE_YIELD) + OPCODE(OPCODE_YIELD_SIGNAL) { int ipofs = 1; if (_code_ptr[ip] == OPCODE_YIELD_SIGNAL) { @@ -898,162 +985,157 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a retvalue = gdfs; if (_code_ptr[ip] == OPCODE_YIELD_SIGNAL) { + //do the oneshot connect GET_VARIANT_PTR(argobj, 1); GET_VARIANT_PTR(argname, 2); - //do the oneshot connect +#ifdef DEBUG_ENABLED if (argobj->get_type() != Variant::OBJECT) { err_text = "First argument of yield() not of type object."; - break; + OPCODE_BREAK; } if (argname->get_type() != Variant::STRING) { err_text = "Second argument of yield() not a string (for signal name)."; - break; + OPCODE_BREAK; } +#endif Object *obj = argobj->operator Object *(); String signal = argname->operator String(); #ifdef DEBUG_ENABLED if (!obj) { err_text = "First argument of yield() is null."; - break; + OPCODE_BREAK; } if (ScriptDebugger::get_singleton()) { if (!ObjectDB::instance_validate(obj)) { err_text = "First argument of yield() is a previously freed instance."; - break; + OPCODE_BREAK; } } if (signal.length() == 0) { err_text = "Second argument of yield() is an empty string (for signal name)."; - break; + OPCODE_BREAK; } #endif Error err = obj->connect(signal, gdfs.ptr(), "_signal_callback", varray(gdfs), Object::CONNECT_ONESHOT); +#ifdef DEBUG_ENABLED if (err != OK) { err_text = "Error connecting to signal: " + signal + " during yield()."; - break; + OPCODE_BREAK; } +#endif } exit_ok = true; - break; + OPCODE_BREAK; } - case OPCODE_YIELD_RESUME: { + OPCODE(OPCODE_YIELD_RESUME) { CHECK_SPACE(2); +#ifdef DEBUG_ENABLED if (!p_state) { err_text = ("Invalid Resume (bug?)"); - break; + OPCODE_BREAK; } +#endif GET_VARIANT_PTR(result, 1); *result = p_state->result; ip += 2; - continue; + DISPATCH_OPCODE; } - case OPCODE_JUMP: { + OPCODE(OPCODE_JUMP) { CHECK_SPACE(2); int to = _code_ptr[ip + 1]; - ERR_BREAK(to < 0 || to > _code_size); + GD_ERR_BREAK(to < 0 || to > _code_size); ip = to; - continue; + DISPATCH_OPCODE; } - case OPCODE_JUMP_IF: { + OPCODE(OPCODE_JUMP_IF) { CHECK_SPACE(3); GET_VARIANT_PTR(test, 1); - bool valid; - bool result = test->booleanize(valid); -#ifdef DEBUG_ENABLED - if (!valid) { + bool result = test->booleanize(); - err_text = "cannot evaluate conditional expression of type: " + Variant::get_type_name(test->get_type()); - break; - } -#endif if (result) { int to = _code_ptr[ip + 2]; - ERR_BREAK(to < 0 || to > _code_size); + GD_ERR_BREAK(to < 0 || to > _code_size); ip = to; - continue; + DISPATCH_OPCODE; } ip += 3; - continue; + DISPATCH_OPCODE; } - case OPCODE_JUMP_IF_NOT: { + OPCODE(OPCODE_JUMP_IF_NOT) { CHECK_SPACE(3); GET_VARIANT_PTR(test, 1); - bool valid; - bool result = test->booleanize(valid); -#ifdef DEBUG_ENABLED - if (!valid) { + bool result = test->booleanize(); - err_text = "cannot evaluate conditional expression of type: " + Variant::get_type_name(test->get_type()); - break; - } -#endif if (!result) { int to = _code_ptr[ip + 2]; - ERR_BREAK(to < 0 || to > _code_size); + GD_ERR_BREAK(to < 0 || to > _code_size); ip = to; - continue; + DISPATCH_OPCODE; } ip += 3; - continue; + DISPATCH_OPCODE; } - case OPCODE_JUMP_TO_DEF_ARGUMENT: { + OPCODE(OPCODE_JUMP_TO_DEF_ARGUMENT) { CHECK_SPACE(2); ip = _default_arg_ptr[defarg]; - continue; + DISPATCH_OPCODE; } - case OPCODE_RETURN: { + OPCODE(OPCODE_RETURN) { CHECK_SPACE(2); GET_VARIANT_PTR(r, 1); retvalue = *r; exit_ok = true; - break; + OPCODE_BREAK; } - case OPCODE_ITERATE_BEGIN: { + OPCODE(OPCODE_ITERATE_BEGIN) { - CHECK_SPACE(8); //space for this an regular iterate + CHECK_SPACE(8); //space for this a regular iterate GET_VARIANT_PTR(counter, 1); GET_VARIANT_PTR(container, 2); bool valid; if (!container->iter_init(*counter, valid)) { +#ifdef DEBUG_ENABLED if (!valid) { err_text = "Unable to iterate on object of type " + Variant::get_type_name(container->get_type()) + "'."; - break; + OPCODE_BREAK; } +#endif int jumpto = _code_ptr[ip + 3]; - ERR_BREAK(jumpto < 0 || jumpto > _code_size); + GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); ip = jumpto; - continue; + DISPATCH_OPCODE; } GET_VARIANT_PTR(iterator, 4); *iterator = container->iter_get(*counter, valid); +#ifdef DEBUG_ENABLED if (!valid) { err_text = "Unable to obtain iterator object of type " + Variant::get_type_name(container->get_type()) + "'."; - break; + OPCODE_BREAK; } - +#endif ip += 5; //skip regular iterate which is always next - continue; + DISPATCH_OPCODE; } - case OPCODE_ITERATE: { + OPCODE(OPCODE_ITERATE) { CHECK_SPACE(4); @@ -1062,61 +1144,56 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a bool valid; if (!container->iter_next(*counter, valid)) { +#ifdef DEBUG_ENABLED if (!valid) { err_text = "Unable to iterate on object of type " + Variant::get_type_name(container->get_type()) + "' (type changed since first iteration?)."; - break; + OPCODE_BREAK; } +#endif int jumpto = _code_ptr[ip + 3]; - ERR_BREAK(jumpto < 0 || jumpto > _code_size); + GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); ip = jumpto; - continue; + DISPATCH_OPCODE; } GET_VARIANT_PTR(iterator, 4); *iterator = container->iter_get(*counter, valid); +#ifdef DEBUG_ENABLED if (!valid) { err_text = "Unable to obtain iterator object of type " + Variant::get_type_name(container->get_type()) + "' (but was obtained on first iteration?)."; - break; + OPCODE_BREAK; } - +#endif ip += 5; //loop again - continue; + DISPATCH_OPCODE; } - case OPCODE_ASSERT: { + OPCODE(OPCODE_ASSERT) { CHECK_SPACE(2); GET_VARIANT_PTR(test, 1); #ifdef DEBUG_ENABLED - bool valid; - bool result = test->booleanize(valid); - - if (!valid) { - - err_text = "cannot evaluate conditional expression of type: " + Variant::get_type_name(test->get_type()); - break; - } + bool result = test->booleanize(); if (!result) { err_text = "Assertion failed."; - break; + OPCODE_BREAK; } #endif - ip += 2; - continue; + DISPATCH_OPCODE; } - case OPCODE_BREAKPOINT: { + OPCODE(OPCODE_BREAKPOINT) { #ifdef DEBUG_ENABLED if (ScriptDebugger::get_singleton()) { GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement", true); } #endif ip += 1; - continue; + DISPATCH_OPCODE; } - case OPCODE_LINE: { + OPCODE(OPCODE_LINE) { CHECK_SPACE(2); line = _code_ptr[ip + 1]; @@ -1143,22 +1220,26 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a ScriptDebugger::get_singleton()->line_poll(); } - continue; + DISPATCH_OPCODE; } - case OPCODE_END: { + OPCODE(OPCODE_END) { exit_ok = true; - break; + OPCODE_BREAK; } +#if 0 default: { err_text = "Illegal opcode " + itos(_code_ptr[ip]) + " at address " + itos(ip); - break; + OPCODE_BREAK; } +#endif } + OPCODES_END +#ifdef DEBUG_ENABLED if (exit_ok) - break; + OPCODE_OUT; //error // function, file, line, error, explanation String err_file; @@ -1182,9 +1263,11 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), ERR_HANDLER_SCRIPT); } - break; +#endif + OPCODE_OUT; } + OPCODES_OUT #ifdef DEBUG_ENABLED if (GDScriptLanguage::get_singleton()->profiling) { uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - function_start_time; @@ -1495,7 +1578,7 @@ void GDFunctionState::_bind_methods() { ClassDB::bind_method(D_METHOD("is_valid", "extended_check"), &GDFunctionState::is_valid, DEFVAL(false)); ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &GDFunctionState::_signal_callback, MethodInfo("_signal_callback")); - ADD_SIGNAL(MethodInfo("completed", PropertyInfo(Variant::NIL, "result"))); + ADD_SIGNAL(MethodInfo("completed", PropertyInfo(Variant::NIL, "result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); } GDFunctionState::GDFunctionState() { diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp index 3bd0ce3fab..34d01c6beb 100644 --- a/modules/gdscript/gd_functions.cpp +++ b/modules/gdscript/gd_functions.cpp @@ -1177,20 +1177,28 @@ void GDFunctions::call(Function p_func, const Variant **p_args, int p_arg_count, VALIDATE_ARG_COUNT(1); switch (p_args[0]->get_type()) { + case Variant::STRING: { + + String d = *p_args[0]; + r_ret = d.length(); + } break; case Variant::DICTIONARY: { + Dictionary d = *p_args[0]; r_ret = d.size(); } break; case Variant::ARRAY: { + Array d = *p_args[0]; r_ret = d.size(); } break; case Variant::POOL_BYTE_ARRAY: { + PoolVector<uint8_t> d = *p_args[0]; r_ret = d.size(); - } break; case Variant::POOL_INT_ARRAY: { + PoolVector<int> d = *p_args[0]; r_ret = d.size(); } break; @@ -1200,14 +1208,14 @@ void GDFunctions::call(Function p_func, const Variant **p_args, int p_arg_count, r_ret = d.size(); } break; case Variant::POOL_STRING_ARRAY: { + PoolVector<String> d = *p_args[0]; r_ret = d.size(); - } break; case Variant::POOL_VECTOR2_ARRAY: { + PoolVector<Vector2> d = *p_args[0]; r_ret = d.size(); - } break; case Variant::POOL_VECTOR3_ARRAY: { @@ -1412,12 +1420,12 @@ MethodInfo GDFunctions::get_info(Function p_func) { } break; case MATH_ISNAN: { MethodInfo mi("is_nan", PropertyInfo(Variant::REAL, "s")); - mi.return_val.type = Variant::REAL; + mi.return_val.type = Variant::BOOL; return mi; } break; case MATH_ISINF: { MethodInfo mi("is_inf", PropertyInfo(Variant::REAL, "s")); - mi.return_val.type = Variant::REAL; + mi.return_val.type = Variant::BOOL; return mi; } break; case MATH_EASE: { @@ -1572,43 +1580,49 @@ MethodInfo GDFunctions::get_info(Function p_func) { } break; case TEXT_STR: { - MethodInfo mi("str", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::NIL, "...")); + MethodInfo mi("str"); mi.return_val.type = Variant::STRING; + mi.flags |= METHOD_FLAG_VARARG; return mi; } break; case TEXT_PRINT: { - MethodInfo mi("print", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::NIL, "...")); + MethodInfo mi("print"); mi.return_val.type = Variant::NIL; + mi.flags |= METHOD_FLAG_VARARG; return mi; } break; case TEXT_PRINT_TABBED: { - MethodInfo mi("printt", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::NIL, "...")); + MethodInfo mi("printt"); mi.return_val.type = Variant::NIL; + mi.flags |= METHOD_FLAG_VARARG; return mi; } break; case TEXT_PRINT_SPACED: { - MethodInfo mi("prints", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::NIL, "...")); + MethodInfo mi("prints"); mi.return_val.type = Variant::NIL; + mi.flags |= METHOD_FLAG_VARARG; return mi; } break; case TEXT_PRINTERR: { - MethodInfo mi("printerr", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::NIL, "...")); + MethodInfo mi("printerr"); mi.return_val.type = Variant::NIL; + mi.flags |= METHOD_FLAG_VARARG; return mi; } break; case TEXT_PRINTRAW: { - MethodInfo mi("printraw", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::NIL, "...")); + MethodInfo mi("printraw"); mi.return_val.type = Variant::NIL; + mi.flags |= METHOD_FLAG_VARARG; return mi; } break; @@ -1620,8 +1634,9 @@ MethodInfo GDFunctions::get_info(Function p_func) { } break; case STR_TO_VAR: { - MethodInfo mi("str2var:Variant", PropertyInfo(Variant::STRING, "string")); + MethodInfo mi(Variant::NIL, "str2var", PropertyInfo(Variant::STRING, "string")); mi.return_val.type = Variant::NIL; + mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; return mi; } break; case VAR_TO_BYTES: { @@ -1632,14 +1647,16 @@ MethodInfo GDFunctions::get_info(Function p_func) { } break; case BYTES_TO_VAR: { - MethodInfo mi("bytes2var:Variant", PropertyInfo(Variant::POOL_BYTE_ARRAY, "bytes")); + MethodInfo mi(Variant::NIL, "bytes2var", PropertyInfo(Variant::POOL_BYTE_ARRAY, "bytes")); mi.return_val.type = Variant::NIL; + mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; return mi; } break; case GEN_RANGE: { - MethodInfo mi("range", PropertyInfo(Variant::NIL, "...")); + MethodInfo mi("range"); mi.return_val.type = Variant::ARRAY; + mi.flags |= METHOD_FLAG_VARARG; return mi; } break; case RESOURCE_LOAD: { @@ -1663,14 +1680,15 @@ MethodInfo GDFunctions::get_info(Function p_func) { } break; case VALIDATE_JSON: { - MethodInfo mi("validate_json:Variant", PropertyInfo(Variant::STRING, "json")); + MethodInfo mi("validate_json", PropertyInfo(Variant::STRING, "json")); mi.return_val.type = Variant::STRING; return mi; } break; case PARSE_JSON: { - MethodInfo mi("parse_json:Variant", PropertyInfo(Variant::STRING, "json")); + MethodInfo mi(Variant::NIL, "parse_json", PropertyInfo(Variant::STRING, "json")); mi.return_val.type = Variant::NIL; + mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; return mi; } break; case TO_JSON: { diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index 072937a521..94385dc0d0 100644 --- a/modules/gdscript/gd_parser.cpp +++ b/modules/gdscript/gd_parser.cpp @@ -1284,7 +1284,7 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool if (expression[next_op + 1].is_op) { // this is not invalid and can really appear // but it becomes invalid anyway because no binary op - // can be followed by an unary op in a valid combination, + // can be followed by a unary op in a valid combination, // due to how precedence works, unaries will always disappear first _set_error("Unexpected two consecutive operators after ternary if."); @@ -1294,7 +1294,7 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool if (expression[next_op + 3].is_op) { // this is not invalid and can really appear // but it becomes invalid anyway because no binary op - // can be followed by an unary op in a valid combination, + // can be followed by a unary op in a valid combination, // due to how precedence works, unaries will always disappear first _set_error("Unexpected two consecutive operators after ternary else."); @@ -1331,7 +1331,7 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool if (expression[next_op + 1].is_op) { // this is not invalid and can really appear // but it becomes invalid anyway because no binary op - // can be followed by an unary op in a valid combination, + // can be followed by a unary op in a valid combination, // due to how precedence works, unaries will always disappear first _set_error("Unexpected two consecutive operators."); @@ -1699,7 +1699,7 @@ GDParser::Node *GDParser::_reduce_expression(Node *p_node, bool p_to_const) { _REDUCE_BINARY(Variant::OP_ADD); } break; case OperatorNode::OP_SUB: { - _REDUCE_BINARY(Variant::OP_SUBSTRACT); + _REDUCE_BINARY(Variant::OP_SUBTRACT); } break; case OperatorNode::OP_MUL: { _REDUCE_BINARY(Variant::OP_MULTIPLY); @@ -1857,7 +1857,7 @@ GDParser::PatternNode *GDParser::_parse_pattern(bool p_static) { tokenizer->advance(1); break; } else { - _set_error("'..' pattern only allowed at the end of an dictionary pattern"); + _set_error("'..' pattern only allowed at the end of a dictionary pattern"); return NULL; } } @@ -1906,7 +1906,8 @@ GDParser::PatternNode *GDParser::_parse_pattern(bool p_static) { // all the constants like strings and numbers default: { Node *value = _parse_and_reduce_expression(pattern, p_static); - if (error_set) { + if (!value) { + _set_error("Expect constant expression or variables in a pattern"); return NULL; } diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp index cf6529d5ae..3f3818ffb9 100644 --- a/modules/gdscript/gd_script.cpp +++ b/modules/gdscript/gd_script.cpp @@ -609,11 +609,6 @@ Error GDScript::reload(bool p_keep_state) { return OK; } -String GDScript::get_node_type() const { - - return ""; // ? -} - ScriptLanguage *GDScript::get_language() const { return GDScriptLanguage::get_singleton(); diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h index 6f05a4770b..e0d142014a 100644 --- a/modules/gdscript/gd_script.h +++ b/modules/gdscript/gd_script.h @@ -172,7 +172,6 @@ public: virtual Error reload(bool p_keep_state = false); - virtual String get_node_type() const; void set_script_path(const String &p_path) { path = p_path; } //because subclasses need a path too... Error load_source_code(const String &p_path); Error load_byte_code(const String &p_path); @@ -386,10 +385,10 @@ public: virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const; virtual Script *create_script() const; virtual bool has_named_classes() const; + virtual bool supports_builtin_mode() const; virtual bool can_inherit_from_file() { return true; } virtual int find_function(const String &p_function, const String &p_code) const; virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const; - virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return OK; } virtual Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint); #ifdef TOOLS_ENABLED virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_base_path, Object *p_owner, LookupResult &r_result); diff --git a/modules/gridmap/config.py b/modules/gridmap/config.py index 5698a37295..b3dbb9f46a 100644 --- a/modules/gridmap/config.py +++ b/modules/gridmap/config.py @@ -6,3 +6,9 @@ def can_build(platform): def configure(env): pass + +def get_doc_classes(): + return ["GridMap"] + +def get_doc_path(): + return "doc_classes" diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml new file mode 100644 index 0000000000..5b0fe56f25 --- /dev/null +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -0,0 +1,227 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="GridMap" inherits="Spatial" category="Core" version="3.0.alpha.custom_build"> + <brief_description> + Node for 3D tile-based maps. + </brief_description> + <description> + GridMap lets you place meshes on a grid interactively. It works both from the editor and can help you create in-game level editors. + GridMaps use a [MeshLibrary] which contain a list of tiles: meshes with materials plus optional collisions and extra elements. + A GridMap contains a collection of cells. Each grid cell refers to a [MeshLibrary] item. All cells in the map have the same dimensions. + A GridMap is split into a sparse collection of octants for efficient rendering and physics processing. Every octant has the same dimensions and can contain several cells. + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="clear"> + <return type="void"> + </return> + <description> + Clear all cells. + </description> + </method> + <method name="get_cell_item" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="x" type="int"> + </argument> + <argument index="1" name="y" type="int"> + </argument> + <argument index="2" name="z" type="int"> + </argument> + <description> + The [MeshLibrary] item index located at the grid-based X, Y and Z coordinates. If the cell is empty, [INVALID_CELL_ITEM] will be returned. + </description> + </method> + <method name="get_cell_item_orientation" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="x" type="int"> + </argument> + <argument index="1" name="y" type="int"> + </argument> + <argument index="2" name="z" type="int"> + </argument> + <description> + The orientation of the cell at the grid-based X, Y and Z coordinates. -1 is retuned if the cell is empty. + </description> + </method> + <method name="get_cell_size" qualifiers="const"> + <return type="Vector3"> + </return> + <description> + The dimensions of the grid's cells. + </description> + </method> + <method name="get_center_x" qualifiers="const"> + <return type="bool"> + </return> + <description> + Returns whether or not grid items are centered on the X axis. + </description> + </method> + <method name="get_center_y" qualifiers="const"> + <return type="bool"> + </return> + <description> + Returns whether or not grid items are centered on the Y axis. + </description> + </method> + <method name="get_center_z" qualifiers="const"> + <return type="bool"> + </return> + <description> + Returns whether or not grid items are centered on the Z axis. + </description> + </method> + <method name="get_meshes"> + <return type="Array"> + </return> + <description> + Array of [Transform] and [Mesh] references corresponding to the non empty cells in the grid. The transforms are specified in world space. + </description> + </method> + <method name="get_octant_size" qualifiers="const"> + <return type="int"> + </return> + <description> + The size of each octant measured in number of cells. This applies to all three axis. + </description> + </method> + <method name="get_theme" qualifiers="const"> + <return type="MeshLibrary"> + </return> + <description> + The assigned [MeshLibrary]. + </description> + </method> + <method name="get_used_cells" qualifiers="const"> + <return type="Array"> + </return> + <description> + Array of [Vector3] with the non empty cell coordinates in the grid map. + </description> + </method> + <method name="map_to_world" qualifiers="const"> + <return type="Vector3"> + </return> + <argument index="0" name="x" type="int"> + </argument> + <argument index="1" name="y" type="int"> + </argument> + <argument index="2" name="z" type="int"> + </argument> + <description> + </description> + </method> + <method name="resource_changed"> + <return type="void"> + </return> + <argument index="0" name="resource" type="Resource"> + </argument> + <description> + </description> + </method> + <method name="set_cell_item"> + <return type="void"> + </return> + <argument index="0" name="x" type="int"> + </argument> + <argument index="1" name="y" type="int"> + </argument> + <argument index="2" name="z" type="int"> + </argument> + <argument index="3" name="item" type="int"> + </argument> + <argument index="4" name="orientation" type="int" default="0"> + </argument> + <description> + Set the mesh index for the cell referenced by its grid-based X, Y and Z coordinates. + A negative item index will clear the cell. + Optionally, the item's orientation can be passed. + </description> + </method> + <method name="set_cell_size"> + <return type="void"> + </return> + <argument index="0" name="size" type="Vector3"> + </argument> + <description> + Sets the height, width and depth of the grid's cells. + </description> + </method> + <method name="set_center_x"> + <return type="void"> + </return> + <argument index="0" name="enable" type="bool"> + </argument> + <description> + Set grid items to be centered on the X axis. By default it is enabled. + </description> + </method> + <method name="set_center_y"> + <return type="void"> + </return> + <argument index="0" name="enable" type="bool"> + </argument> + <description> + Set grid items to be centered on the Y axis. By default it is enabled. + </description> + </method> + <method name="set_center_z"> + <return type="void"> + </return> + <argument index="0" name="enable" type="bool"> + </argument> + <description> + Set grid items to be centered on the Z axis. By default it is enabled. + </description> + </method> + <method name="set_clip"> + <return type="void"> + </return> + <argument index="0" name="enabled" type="bool"> + </argument> + <argument index="1" name="clipabove" type="bool" default="true"> + </argument> + <argument index="2" name="floor" type="int" default="0"> + </argument> + <argument index="3" name="axis" type="int" enum="Vector3.Axis" default="0"> + </argument> + <description> + </description> + </method> + <method name="set_octant_size"> + <return type="void"> + </return> + <argument index="0" name="size" type="int"> + </argument> + <description> + Sets the size for each octant measured in number of cells. This applies to all three axis. + </description> + </method> + <method name="set_theme"> + <return type="void"> + </return> + <argument index="0" name="theme" type="MeshLibrary"> + </argument> + <description> + Sets the collection of meshes for the map. + </description> + </method> + <method name="world_to_map" qualifiers="const"> + <return type="Vector3"> + </return> + <argument index="0" name="pos" type="Vector3"> + </argument> + <description> + </description> + </method> + </methods> + <constants> + <constant name="INVALID_CELL_ITEM" value="-1" enum=""> + Invalid cell item that can be used in [method set_cell_item] to clear cells (or represent an empty cell in [method get_cell_item]). + </constant> + </constants> +</class> diff --git a/modules/gridmap/doc_classes/README.md b/modules/gridmap/doc_classes/README.md new file mode 100644 index 0000000000..b1ec9058c8 --- /dev/null +++ b/modules/gridmap/doc_classes/README.md @@ -0,0 +1 @@ +Doc classes will appear here when generating
\ No newline at end of file diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 1b932f040e..cb14a5ee9c 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -333,6 +333,23 @@ int GridMap::get_cell_item_orientation(int p_x, int p_y, int p_z) const { return cell_map[key].rot; } +Vector3 GridMap::world_to_map(const Vector3 &p_world_pos) const { + Vector3 map_pos = p_world_pos / cell_size; + map_pos.x = floor(map_pos.x); + map_pos.y = floor(map_pos.y); + map_pos.z = floor(map_pos.z); + return map_pos; +} + +Vector3 GridMap::map_to_world(int p_x, int p_y, int p_z) const { + Vector3 offset = _get_offset(); + Vector3 world_pos( + p_x * cell_size.x + offset.x, + p_y * cell_size.y + offset.y, + p_z * cell_size.z + offset.z); + return world_pos; +} + void GridMap::_octant_transform(const OctantKey &p_key) { ERR_FAIL_COND(!octant_map.has(p_key)); @@ -396,8 +413,6 @@ bool GridMap::_octant_update(const OctantKey &p_key) { Map<int, List<Pair<Transform, IndexKey> > > multimesh_items; - print_line("updating octant " + itos(p_key.x) + ", " + itos(p_key.y) + ", " + itos(p_key.z) + " cells: " + itos(g.cells.size())); - for (Set<IndexKey>::Element *E = g.cells.front(); E; E = E->next()) { ERR_CONTINUE(!cell_map.has(E->get())); @@ -409,7 +424,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) { //print_line("OCTANT, CELLS: "+itos(ii.cells.size())); Vector3 cellpos = Vector3(E->get().x, E->get().y, E->get().z); - Vector3 ofs(cell_size.x * 0.5 * int(center_x), cell_size.y * 0.5 * int(center_y), cell_size.z * 0.5 * int(center_z)); + Vector3 ofs = _get_offset(); Transform xform; @@ -464,7 +479,6 @@ bool GridMap::_octant_update(const OctantKey &p_key) { //update multimeshes for (Map<int, List<Pair<Transform, IndexKey> > >::Element *E = multimesh_items.front(); E; E = E->next()) { - print_line("multimesh item " + itos(E->key()) + " transforms " + itos(E->get().size())); Octant::MultimeshInstance mmi; RID mm = VS::get_singleton()->multimesh_create(); @@ -655,6 +669,24 @@ void GridMap::_notification(int p_what) { //_update_area_instances(); } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + _update_visibility(); + } break; + } +} + +void GridMap::_update_visibility() { + if (!is_inside_tree()) + return; + + _change_notify("visible"); + + for (Map<OctantKey, Octant *>::Element *e = octant_map.front(); e; e = e->next()) { + Octant *octant = e->value(); + for (int i = 0; i < octant->multimesh_instances.size(); i++) { + Octant::MultimeshInstance &mi = octant->multimesh_instances[i]; + VS::get_singleton()->instance_set_visible(mi.instance, is_visible()); + } } } @@ -720,6 +752,7 @@ void GridMap::_update_octants_callback() { to_delete.pop_back(); } + _update_visibility(); awaiting_update = false; } @@ -738,6 +771,9 @@ void GridMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cell_item", "x", "y", "z"), &GridMap::get_cell_item); ClassDB::bind_method(D_METHOD("get_cell_item_orientation", "x", "y", "z"), &GridMap::get_cell_item_orientation); + ClassDB::bind_method(D_METHOD("world_to_map", "pos"), &GridMap::world_to_map); + ClassDB::bind_method(D_METHOD("map_to_world", "x", "y", "z"), &GridMap::map_to_world); + //ClassDB::bind_method(D_METHOD("_recreate_octants"),&GridMap::_recreate_octants); ClassDB::bind_method(D_METHOD("_update_octants_callback"), &GridMap::_update_octants_callback); ClassDB::bind_method(D_METHOD("resource_changed", "resource"), &GridMap::resource_changed); @@ -753,6 +789,8 @@ void GridMap::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &GridMap::clear); + ClassDB::bind_method(D_METHOD("get_used_cells"), &GridMap::get_used_cells); + ClassDB::bind_method(D_METHOD("get_meshes"), &GridMap::get_meshes); BIND_CONSTANT(INVALID_CELL_ITEM); @@ -783,7 +821,7 @@ void GridMap::set_clip(bool p_enabled, bool p_clip_above, int p_floor, Vector3:: void GridMap::set_cell_scale(float p_scale) { cell_scale = p_scale; - _queue_octants_dirty(); + _recreate_octant_data(); } float GridMap::get_cell_scale() const { @@ -791,12 +829,25 @@ float GridMap::get_cell_scale() const { return cell_scale; } +Array GridMap::get_used_cells() const { + + Array a; + a.resize(cell_map.size()); + int i = 0; + for (Map<IndexKey, Cell>::Element *E = cell_map.front(); E; E = E->next()) { + Vector3 p(E->key().x, E->key().y, E->key().z); + a[i++] = p; + } + + return a; +} + Array GridMap::get_meshes() { if (theme.is_null()) return Array(); - Vector3 ofs(cell_size.x * 0.5 * int(center_x), cell_size.y * 0.5 * int(center_y), cell_size.z * 0.5 * int(center_z)); + Vector3 ofs = _get_offset(); Array meshes; for (Map<IndexKey, Cell>::Element *E = cell_map.front(); E; E = E->next()) { @@ -826,6 +877,13 @@ Array GridMap::get_meshes() { return meshes; } +Vector3 GridMap::_get_offset() const { + return Vector3( + cell_size.x * 0.5 * int(center_x), + cell_size.y * 0.5 * int(center_y), + cell_size.z * 0.5 * int(center_z)); +} + GridMap::GridMap() { cell_size = Vector3(2, 2, 2); diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index 9e1d250680..5bfdf1dac3 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -184,12 +184,15 @@ class GridMap : public Spatial { void _clear_internal(); + Vector3 _get_offset() const; + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; void _notification(int p_what); + void _update_visibility(); static void _bind_methods(); public: @@ -217,11 +220,16 @@ public: int get_cell_item(int p_x, int p_y, int p_z) const; int get_cell_item_orientation(int p_x, int p_y, int p_z) const; + Vector3 world_to_map(const Vector3 &p_pos) const; + Vector3 map_to_world(int p_x, int p_y, int p_z) const; + void set_clip(bool p_enabled, bool p_clip_above = true, int p_floor = 0, Vector3::Axis p_axis = Vector3::AXIS_X); void set_cell_scale(float p_scale); float get_cell_scale() const; + Array get_used_cells() const; + Array get_meshes(); void clear(); diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 6f0a13e07f..f6a76ad2a1 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -1159,14 +1159,14 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { outer_mat.instance(); outer_mat->set_albedo(Color(0.7, 0.7, 1.0, 0.8)); - outer_mat->set_flag(SpatialMaterial::FLAG_ONTOP, true); + outer_mat->set_on_top_of_alpha(); outer_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); outer_mat->set_line_width(3.0); outer_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); selection_floor_mat.instance(); selection_floor_mat->set_albedo(Color(0.80, 0.80, 1.0, 1)); - selection_floor_mat->set_flag(SpatialMaterial::FLAG_ONTOP, true); + selection_floor_mat->set_on_top_of_alpha(); selection_floor_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); selection_floor_mat->set_line_width(3.0); //selection_floor_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp index d883b0f280..08ac624504 100644 --- a/modules/hdr/image_loader_hdr.cpp +++ b/modules/hdr/image_loader_hdr.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_loader_jpegd.cpp */ +/* image_loader_hdr.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -38,7 +38,6 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force String header = f->get_token(); - print_line("HEADER: " + header); ERR_FAIL_COND_V(header != "#?RADIANCE" && header != "#?RGBE", ERR_FILE_UNRECOGNIZED); while (true) { @@ -64,8 +63,6 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force int width = f->get_line().to_int(); - print_line("HDR w: " + itos(width) + " h:" + itos(height)); - PoolVector<uint8_t> imgdata; imgdata.resize(height * width * sizeof(uint32_t)); @@ -102,7 +99,6 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force len <<= 8; len |= f->get_8(); - print_line("line: " + itos(len)); if (len != width) { ERR_EXPLAIN("invalid decoded scanline length, corrupt HDR"); ERR_FAIL_V(ERR_FILE_CORRUPT); diff --git a/modules/hdr/image_loader_hdr.h b/modules/hdr/image_loader_hdr.h index e6703dc142..569978d28d 100644 --- a/modules/hdr/image_loader_hdr.h +++ b/modules/hdr/image_loader_hdr.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_loader_jpegd.h */ +/* image_loader_hdr.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/modules/mobile_vr/SCsub b/modules/mobile_vr/SCsub new file mode 100644 index 0000000000..b4e2edcca1 --- /dev/null +++ b/modules/mobile_vr/SCsub @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +import os +import methods + +Import('env') +Import('env_modules') + +env_mobile_vr = env_modules.Clone() + +env_mobile_vr.add_source_files(env.modules_sources, '*.cpp') + +SConscript("shaders/SCsub") diff --git a/modules/mobile_vr/config.py b/modules/mobile_vr/config.py new file mode 100644 index 0000000000..cf96c66125 --- /dev/null +++ b/modules/mobile_vr/config.py @@ -0,0 +1,12 @@ +def can_build(platform): + # should probably change this to only be true on iOS and Android + return True + +def configure(env): + pass + +def get_doc_classes(): + return ["MobileVRInterface"] + +def get_doc_path(): + return "doc_classes" diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml new file mode 100644 index 0000000000..c945a99a9a --- /dev/null +++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml @@ -0,0 +1,134 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="MobileVRInterface" inherits="ARVRInterface" category="Core" version="3.0.alpha.custom_build"> + <brief_description> + Generic mobile VR implementation + </brief_description> + <description> + This is a generic mobile VR implementation where you need to provide details about the phone and HMD used. It does not rely on any existing framework. This is the most basic interface we have. For the best effect you do need a mobile phone with a gyroscope and accelerometer. + Note that even though there is no positional tracking the camera will assume the headset is at a height of 1.85 meters. + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="get_display_to_lens" qualifiers="const"> + <return type="float"> + </return> + <description> + Returns the distance between the display and the lens. + </description> + </method> + <method name="get_display_width" qualifiers="const"> + <return type="float"> + </return> + <description> + Return the width of the LCD screen of the device. + </description> + </method> + <method name="get_iod" qualifiers="const"> + <return type="float"> + </return> + <description> + Returns the interocular distance. + </description> + </method> + <method name="get_k1" qualifiers="const"> + <return type="float"> + </return> + <description> + Returns the k1 lens constant. + </description> + </method> + <method name="get_k2" qualifiers="const"> + <return type="float"> + </return> + <description> + Retuns the k2 lens constant + </description> + </method> + <method name="get_oversample" qualifiers="const"> + <return type="float"> + </return> + <description> + Returns the oversampling setting. + </description> + </method> + <method name="set_display_to_lens"> + <return type="void"> + </return> + <argument index="0" name="display_to_lens" type="float"> + </argument> + <description> + Sets the distance between display and the lens. + </description> + </method> + <method name="set_display_width"> + <return type="void"> + </return> + <argument index="0" name="display_width" type="float"> + </argument> + <description> + Sets the width of the LCD screen of the device. + </description> + </method> + <method name="set_iod"> + <return type="void"> + </return> + <argument index="0" name="iod" type="float"> + </argument> + <description> + Sets the interocular distance. + </description> + </method> + <method name="set_k1"> + <return type="void"> + </return> + <argument index="0" name="k" type="float"> + </argument> + <description> + Sets the k1 lens constant. + </description> + </method> + <method name="set_k2"> + <return type="void"> + </return> + <argument index="0" name="k" type="float"> + </argument> + <description> + Sets the k2 lens constant. + </description> + </method> + <method name="set_oversample"> + <return type="void"> + </return> + <argument index="0" name="oversample" type="float"> + </argument> + <description> + Sets the oversampling setting. + </description> + </method> + </methods> + <members> + <member name="display_to_lens" type="float" setter="set_display_to_lens" getter="get_display_to_lens"> + The distance between the display and the lenses inside of the device in centimeters. + </member> + <member name="display_width" type="float" setter="set_display_width" getter="get_display_width"> + The width of the display in centimeters. + </member> + <member name="iod" type="float" setter="set_iod" getter="get_iod"> + The interocular distance, also known as the interpupillary distance. The distance between the pupils of the left and right eye. + </member> + <member name="k1" type="float" setter="set_k1" getter="get_k1"> + The k1 lens factor is one of the two constants that define the strength of the lens used and directly influences the lens distortion effect. + </member> + <member name="k2" type="float" setter="set_k2" getter="get_k2"> + The k2 lens factor, see k1. + </member> + <member name="oversample" type="float" setter="set_oversample" getter="get_oversample"> + The oversample setting. Because of the lens distortion we have to render our buffers at a higher resolution then the screen can natively handle. A value between 1.5 and 2.0 often provides good results but at the cost of performance. + </member> + </members> + <constants> + </constants> +</class> diff --git a/modules/mobile_vr/mobile_interface.cpp b/modules/mobile_vr/mobile_interface.cpp new file mode 100644 index 0000000000..eb87bb2cf0 --- /dev/null +++ b/modules/mobile_vr/mobile_interface.cpp @@ -0,0 +1,504 @@ +/*************************************************************************/ +/* mobile_interface.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "mobile_interface.h" +#include "core/os/input.h" +#include "core/os/os.h" +#include "servers/visual/visual_server_global.h" + +StringName MobileVRInterface::get_name() const { + return "Native mobile"; +}; + +int MobileVRInterface::get_capabilities() const { + return ARVRInterface::ARVR_STEREO; +}; + +Vector3 MobileVRInterface::scale_magneto(const Vector3 &p_magnetometer) { + // Our magnetometer doesn't give us nice clean data. + // Well it may on Mac OS X because we're getting a calibrated value in the current implementation but Android we're getting raw data. + // This is a fairly simple adjustment we can do to correct for the magnetometer data being elliptical + + Vector3 mag_raw = p_magnetometer; + Vector3 mag_scaled = p_magnetometer; + + // update our variables every x frames + if (mag_count > 20) { + mag_current_min = mag_next_min; + mag_current_max = mag_next_max; + mag_count = 0; + } else { + mag_count++; + }; + + // adjust our min and max + if (mag_raw.x > mag_next_max.x) mag_next_max.x = mag_raw.x; + if (mag_raw.y > mag_next_max.y) mag_next_max.y = mag_raw.y; + if (mag_raw.z > mag_next_max.z) mag_next_max.z = mag_raw.z; + + if (mag_raw.x < mag_next_min.x) mag_next_min.x = mag_raw.x; + if (mag_raw.y < mag_next_min.y) mag_next_min.y = mag_raw.y; + if (mag_raw.z < mag_next_min.z) mag_next_min.z = mag_raw.z; + + // scale our x, y and z + if (!(mag_current_max.x - mag_current_min.x)) { + mag_raw.x -= (mag_current_min.x + mag_current_max.x) / 2.0; + mag_scaled.x = (mag_raw.x - mag_current_min.x) / ((mag_current_max.x - mag_current_min.x) * 2.0 - 1.0); + }; + + if (!(mag_current_max.y - mag_current_min.y)) { + mag_raw.y -= (mag_current_min.y + mag_current_max.y) / 2.0; + mag_scaled.y = (mag_raw.y - mag_current_min.y) / ((mag_current_max.y - mag_current_min.y) * 2.0 - 1.0); + }; + + if (!(mag_current_max.z - mag_current_min.z)) { + mag_raw.z -= (mag_current_min.z + mag_current_max.z) / 2.0; + mag_scaled.z = (mag_raw.z - mag_current_min.z) / ((mag_current_max.z - mag_current_min.z) * 2.0 - 1.0); + }; + + return mag_scaled; +}; + +Basis MobileVRInterface::combine_acc_mag(const Vector3 &p_grav, const Vector3 &p_magneto) { + // yup, stock standard cross product solution... + Vector3 up = -p_grav.normalized(); + + Vector3 magneto_east = up.cross(p_magneto.normalized()); // or is this west?, but should be horizon aligned now + magneto_east.normalize(); + + Vector3 magneto = up.cross(magneto_east); // and now we have a horizon aligned north + magneto.normalize(); + + // We use our gravity and magnetometer vectors to construct our matrix + Basis acc_mag_m3; + acc_mag_m3.elements[0] = -magneto_east; + acc_mag_m3.elements[1] = up; + acc_mag_m3.elements[2] = magneto; + + return acc_mag_m3; +}; + +void MobileVRInterface::set_position_from_sensors() { + _THREAD_SAFE_METHOD_ + + // this is a helper function that attempts to adjust our transform using our 9dof sensors + // 9dof is a misleading marketing term coming from 3 accelerometer axis + 3 gyro axis + 3 magnetometer axis = 9 axis + // but in reality this only offers 3 dof (yaw, pitch, roll) orientation + + uint64_t ticks = OS::get_singleton()->get_ticks_usec(); + uint64_t ticks_elapsed = ticks - last_ticks; + float delta_time = (double)ticks_elapsed / 1000000.0; + + // few things we need + Input *input = Input::get_singleton(); + Vector3 down(0.0, -1.0, 0.0); // Down is Y negative + Vector3 north(0.0, 0.0, 1.0); // North is Z positive + + // make copies of our inputs + Vector3 acc = input->get_accelerometer(); + Vector3 gyro = input->get_gyroscope(); + Vector3 grav = input->get_gravity(); + Vector3 magneto = scale_magneto(input->get_magnetometer()); // this may be overkill on iOS because we're already getting a calibrated magnetometer reading + + if (sensor_first) { + sensor_first = false; + } else { + acc = scrub(acc, last_accerometer_data, 2, 0.2); + magneto = scrub(magneto, last_magnetometer_data, 3, 0.3); + }; + + last_accerometer_data = acc; + last_magnetometer_data = magneto; + + if (grav.length() < 0.1) { + // not ideal but use our accelerometer, this will contain shakey shakey user behaviour + // maybe look into some math but I'm guessing that if this isn't available, its because we lack the gyro sensor to actually work out + // what a stable gravity vector is + grav = acc; + if (grav.length() > 0.1) { + has_gyro = true; + }; + } else { + has_gyro = true; + }; + + bool has_magneto = magneto.length() > 0.1; + bool has_grav = grav.length() > 0.1; + +#ifdef ANDROID_ENABLED + ///@TODO needs testing, i don't have a gyro, potentially can be removed depending on what comes out of issue #8101 + // On Android x and z axis seem inverted + gyro.x = -gyro.x; + gyro.z = -gyro.z; + grav.x = -grav.x; + grav.z = -grav.z; + magneto.x = -magneto.x; + magneto.z = -magneto.z; +#endif + + if (has_gyro) { + // start with applying our gyro (do NOT smooth our gyro!) + Basis rotate; + rotate.rotate(orientation.get_axis(0), gyro.x * delta_time); + rotate.rotate(orientation.get_axis(1), gyro.y * delta_time); + rotate.rotate(orientation.get_axis(2), gyro.z * delta_time); + orientation = rotate * orientation; + + tracking_state = ARVRInterface::ARVR_NORMAL_TRACKING; + }; + + ///@TODO improve this, the magnetometer is very fidgity sometimes flipping the axis for no apparent reason (probably a bug on my part) + // if you have a gyro + accelerometer that combo tends to be better then combining all three but without a gyro you need the magnetometer.. + if (has_magneto && has_grav && !has_gyro) { + // convert to quaternions, easier to smooth those out + Quat transform_quat(orientation); + Quat acc_mag_quat(combine_acc_mag(grav, magneto)); + transform_quat = transform_quat.slerp(acc_mag_quat, 0.1); + orientation = Basis(transform_quat); + + tracking_state = ARVRInterface::ARVR_NORMAL_TRACKING; + } else if (has_grav) { + // use gravity vector to make sure down is down... + // transform gravity into our world space + grav.normalize(); + Vector3 grav_adj = orientation.xform(grav); + float dot = grav_adj.dot(down); + if ((dot > -1.0) && (dot < 1.0)) { + // axis around which we have this rotation + Vector3 axis = grav_adj.cross(down); + axis.normalize(); + + Basis drift_compensation(axis, acos(dot) * delta_time * 10); + orientation = drift_compensation * orientation; + }; + }; + + // JIC + orientation.orthonormalize(); + + last_ticks = ticks; +}; + +void MobileVRInterface::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_iod", "iod"), &MobileVRInterface::set_iod); + ClassDB::bind_method(D_METHOD("get_iod"), &MobileVRInterface::get_iod); + + ClassDB::bind_method(D_METHOD("set_display_width", "display_width"), &MobileVRInterface::set_display_width); + ClassDB::bind_method(D_METHOD("get_display_width"), &MobileVRInterface::get_display_width); + + ClassDB::bind_method(D_METHOD("set_display_to_lens", "display_to_lens"), &MobileVRInterface::set_display_to_lens); + ClassDB::bind_method(D_METHOD("get_display_to_lens"), &MobileVRInterface::get_display_to_lens); + + ClassDB::bind_method(D_METHOD("set_oversample", "oversample"), &MobileVRInterface::set_oversample); + ClassDB::bind_method(D_METHOD("get_oversample"), &MobileVRInterface::get_oversample); + + ClassDB::bind_method(D_METHOD("set_k1", "k"), &MobileVRInterface::set_k1); + ClassDB::bind_method(D_METHOD("get_k1"), &MobileVRInterface::get_k1); + + ClassDB::bind_method(D_METHOD("set_k2", "k"), &MobileVRInterface::set_k2); + ClassDB::bind_method(D_METHOD("get_k2"), &MobileVRInterface::get_k2); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "iod", PROPERTY_HINT_RANGE, "4.0,10.0,0.1"), "set_iod", "get_iod"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "display_width", PROPERTY_HINT_RANGE, "5.0,25.0,0.1"), "set_display_width", "get_display_width"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "display_to_lens", PROPERTY_HINT_RANGE, "5.0,25.0,0.1"), "set_display_to_lens", "get_display_to_lens"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "oversample", PROPERTY_HINT_RANGE, "1.0,2.0,0.1"), "set_oversample", "get_oversample"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "k1", PROPERTY_HINT_RANGE, "0.1,10.0,0.0001"), "set_k1", "get_k1"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "k2", PROPERTY_HINT_RANGE, "0.1,10.0,0.0001"), "set_k2", "get_k2"); +} + +void MobileVRInterface::set_iod(const real_t p_iod) { + intraocular_dist = p_iod; +}; + +real_t MobileVRInterface::get_iod() const { + return intraocular_dist; +}; + +void MobileVRInterface::set_display_width(const real_t p_display_width) { + display_width = p_display_width; +}; + +real_t MobileVRInterface::get_display_width() const { + return display_width; +}; + +void MobileVRInterface::set_display_to_lens(const real_t p_display_to_lens) { + display_to_lens = p_display_to_lens; +}; + +real_t MobileVRInterface::get_display_to_lens() const { + return display_to_lens; +}; + +void MobileVRInterface::set_oversample(const real_t p_oversample) { + oversample = p_oversample; +}; + +real_t MobileVRInterface::get_oversample() const { + return oversample; +}; + +void MobileVRInterface::set_k1(const real_t p_k1) { + k1 = p_k1; +}; + +real_t MobileVRInterface::get_k1() const { + return k1; +}; + +void MobileVRInterface::set_k2(const real_t p_k2) { + k2 = p_k2; +}; + +real_t MobileVRInterface::get_k2() const { + return k2; +}; + +bool MobileVRInterface::is_stereo() { + // needs stereo... + return true; +}; + +bool MobileVRInterface::is_initialized() { + return (initialized); +}; + +bool MobileVRInterface::initialize() { + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL_V(arvr_server, false); + + if (!initialized) { + // reset our sensor data and orientation + mag_count = 0; + has_gyro = false; + sensor_first = true; + mag_next_min = Vector3(10000, 10000, 10000); + mag_next_max = Vector3(-10000, -10000, -10000); + mag_current_min = Vector3(0, 0, 0); + mag_current_max = Vector3(0, 0, 0); + + // reset our orientation + orientation = Basis(); + + // make this our primary interface + arvr_server->set_primary_interface(this); + + last_ticks = OS::get_singleton()->get_ticks_usec(); + ; + initialized = true; + }; + + return true; +}; + +void MobileVRInterface::uninitialize() { + if (initialized) { + ARVRServer *arvr_server = ARVRServer::get_singleton(); + if (arvr_server != NULL) { + // no longer our primary interface + arvr_server->clear_primary_interface_if(this); + } + + initialized = false; + }; +}; + +Size2 MobileVRInterface::get_recommended_render_targetsize() { + _THREAD_SAFE_METHOD_ + + // we use half our window size + Size2 target_size = OS::get_singleton()->get_window_size(); + target_size.x *= 0.5 * oversample; + target_size.y *= oversample; + + return target_size; +}; + +Transform MobileVRInterface::get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform) { + _THREAD_SAFE_METHOD_ + + Transform transform_for_eye; + + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL_V(arvr_server, transform_for_eye); + + if (initialized) { + float world_scale = arvr_server->get_world_scale(); + + // we don't need to check for the existance of our HMD, doesn't effect our values... + // note * 0.01 to convert cm to m and * 0.5 as we're moving half in each direction... + if (p_eye == ARVRInterface::EYE_LEFT) { + transform_for_eye.origin.x = -(intraocular_dist * 0.01 * 0.5 * world_scale); + } else if (p_eye == ARVRInterface::EYE_RIGHT) { + transform_for_eye.origin.x = intraocular_dist * 0.01 * 0.5 * world_scale; + } else { + // for mono we don't reposition, we want our center position. + }; + + // just scale our origin point of our transform + Transform hmd_transform; + hmd_transform.basis = orientation; + hmd_transform.origin = Vector3(0.0, eye_height * world_scale, 0.0); + + transform_for_eye = p_cam_transform * (arvr_server->get_reference_frame()) * hmd_transform * transform_for_eye; + } else { + // huh? well just return what we got.... + transform_for_eye = p_cam_transform; + }; + + return transform_for_eye; +}; + +CameraMatrix MobileVRInterface::get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { + _THREAD_SAFE_METHOD_ + + CameraMatrix eye; + + if (p_eye == ARVRInterface::EYE_MONO) { + ///@TODO for now hardcode some of this, what is really needed here is that this needs to be in sync with the real cameras properties + // which probably means implementing a specific class for iOS and Android. For now this is purely here as an example. + // Note also that if you use a normal viewport with AR/VR turned off you can still use the tracker output of this interface + // to position a stock standard Godot camera and have control over this. + // This will make more sense when we implement ARkit on iOS (probably a separate interface). + eye.set_perspective(60.0, p_aspect, p_z_near, p_z_far, false); + } else { + eye.set_for_hmd(p_eye == ARVRInterface::EYE_LEFT ? 1 : 2, p_aspect, intraocular_dist, display_width, display_to_lens, oversample, p_z_near, p_z_far); + }; + + return eye; +}; + +void MobileVRInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { + _THREAD_SAFE_METHOD_ + + // We must have a valid render target + ERR_FAIL_COND(!p_render_target.is_valid()); + + // Because we are rendering to our device we must use our main viewport! + ERR_FAIL_COND(p_screen_rect == Rect2()); + + float offset_x = 0.0; + float aspect_ratio = 0.5 * p_screen_rect.size.x / p_screen_rect.size.y; + Vector2 eye_center; + + if (p_eye == ARVRInterface::EYE_LEFT) { + offset_x = -1.0; + eye_center.x = ((-intraocular_dist / 2.0) + (display_width / 4.0)) / (display_width / 2.0); + } else if (p_eye == ARVRInterface::EYE_RIGHT) { + eye_center.x = ((intraocular_dist / 2.0) - (display_width / 4.0)) / (display_width / 2.0); + } + + // unset our render target so we are outputting to our main screen by making RasterizerStorageGLES3::system_fbo our current FBO + VSG::rasterizer->set_current_render_target(RID()); + + // now output to screen + // VSG::rasterizer->blit_render_target_to_screen(p_render_target, screen_rect, 0); + + // get our render target + RID eye_texture = VSG::storage->render_target_get_texture(p_render_target); + uint32_t texid = VS::get_singleton()->texture_get_texid(eye_texture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texid); + + lens_shader.bind(); + lens_shader.set_uniform(LensDistortedShaderGLES3::OFFSET_X, offset_x); + lens_shader.set_uniform(LensDistortedShaderGLES3::K1, k1); + lens_shader.set_uniform(LensDistortedShaderGLES3::K2, k2); + lens_shader.set_uniform(LensDistortedShaderGLES3::EYE_CENTER, eye_center); + lens_shader.set_uniform(LensDistortedShaderGLES3::UPSCALE, oversample); + lens_shader.set_uniform(LensDistortedShaderGLES3::ASPECT_RATIO, aspect_ratio); + + glBindVertexArray(half_screen_array); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glBindVertexArray(0); +}; + +void MobileVRInterface::process() { + _THREAD_SAFE_METHOD_ + + if (initialized) { + set_position_from_sensors(); + }; +}; + +MobileVRInterface::MobileVRInterface() { + initialized = false; + + // Just set some defaults for these. At some point we need to look at adding a lookup table for common device + headset combos and/or support reading cardboard QR codes + eye_height = 1.85; + intraocular_dist = 6.0; + display_width = 14.5; + display_to_lens = 4.0; + oversample = 1.5; + k1 = 0.215; + k2 = 0.215; + last_ticks = 0; + + // create our shader stuff + lens_shader.init(); + + { + glGenBuffers(1, &half_screen_quad); + glBindBuffer(GL_ARRAY_BUFFER, half_screen_quad); + { + const float qv[16] = { + 0, -1, + -1, -1, + 0, 1, + -1, 1, + 1, 1, + 1, 1, + 1, -1, + 1, -1, + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + + glGenVertexArrays(1, &half_screen_array); + glBindVertexArray(half_screen_array); + glBindBuffer(GL_ARRAY_BUFFER, half_screen_quad); + glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0); + glEnableVertexAttribArray(0); + glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, ((uint8_t *)NULL) + 8); + glEnableVertexAttribArray(4); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + } +}; + +MobileVRInterface::~MobileVRInterface() { + // and make sure we cleanup if we haven't already + if (is_initialized()) { + uninitialize(); + }; +}; diff --git a/modules/mobile_vr/mobile_interface.h b/modules/mobile_vr/mobile_interface.h new file mode 100644 index 0000000000..747377ae46 --- /dev/null +++ b/modules/mobile_vr/mobile_interface.h @@ -0,0 +1,152 @@ +/*************************************************************************/ +/* mobile_interface.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 MOBILE_VR_INTERFACE_H +#define MOBILE_VR_INTERFACE_H + +#include "servers/arvr/arvr_interface.h" +#include "servers/arvr/arvr_positional_tracker.h" + +#include "shaders/lens_distorted.glsl.gen.h" + +/** + @author Bastiaan Olij <mux213@gmail.com> + + The mobile interface is a native VR interface that can be used on Android and iOS phones. + It contains a basic implementation supporting 3DOF tracking if a gyroscope and accelerometer are + present and sets up the proper projection matrices based on the values provided. + + We're planning to eventually do separate interfaces towards mobile SDKs that have far more capabilities and + do not rely on the user providing most of these settings (though enhancing this with auto detection features + based on the device we're running on would be cool). I'm mostly adding this as an example or base plate for + more advanced interfaces. +*/ + +class MobileVRInterface : public ARVRInterface { + GDCLASS(MobileVRInterface, ARVRInterface); + +private: + bool initialized; + Basis orientation; + float eye_height; + uint64_t last_ticks; + + LensDistortedShaderGLES3 lens_shader; + GLuint half_screen_quad; + GLuint half_screen_array; + + real_t intraocular_dist; + real_t display_width; + real_t display_to_lens; + real_t oversample; + + //@TODO not yet used, these are needed in our distortion shader... + real_t k1; + real_t k2; + + /* + logic for processing our sensor data, this was originally in our positional tracker logic but I think + that doesn't make sense in hindsight. It only makes marginally more sense to park it here for now, + this probably deserves an object of its own + */ + Vector3 scale_magneto(const Vector3 &p_magnetometer); + Basis combine_acc_mag(const Vector3 &p_grav, const Vector3 &p_magneto); + + int mag_count; + bool has_gyro; + bool sensor_first; + Vector3 last_accerometer_data; + Vector3 last_magnetometer_data; + Vector3 mag_current_min; + Vector3 mag_current_max; + Vector3 mag_next_min; + Vector3 mag_next_max; + + ///@TODO a few support functions for trackers, most are math related and should likely be moved elsewhere + float floor_decimals(float p_value, float p_decimals) { + float power_of_10 = pow(10.0f, p_decimals); + return floor(p_value * power_of_10) / power_of_10; + }; + + Vector3 floor_decimals(const Vector3 &p_vector, float p_decimals) { + return Vector3(floor_decimals(p_vector.x, p_decimals), floor_decimals(p_vector.y, p_decimals), floor_decimals(p_vector.z, p_decimals)); + }; + + Vector3 low_pass(const Vector3 &p_vector, const Vector3 &p_last_vector, float p_factor) { + return p_vector + (p_factor * (p_last_vector - p_vector)); + }; + + Vector3 scrub(const Vector3 &p_vector, const Vector3 &p_last_vector, float p_decimals, float p_factor) { + return low_pass(floor_decimals(p_vector, p_decimals), p_last_vector, p_factor); + }; + + void set_position_from_sensors(); + +protected: + static void _bind_methods(); + +public: + void set_iod(const real_t p_iod); + real_t get_iod() const; + + void set_display_width(const real_t p_display_width); + real_t get_display_width() const; + + void set_display_to_lens(const real_t p_display_to_lens); + real_t get_display_to_lens() const; + + void set_oversample(const real_t p_oversample); + real_t get_oversample() const; + + void set_k1(const real_t p_k1); + real_t get_k1() const; + + void set_k2(const real_t p_k2); + real_t get_k2() const; + + virtual StringName get_name() const; + virtual int get_capabilities() const; + + virtual bool is_initialized(); + virtual bool initialize(); + virtual void uninitialize(); + + virtual Size2 get_recommended_render_targetsize(); + virtual bool is_stereo(); + virtual Transform get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform); + virtual CameraMatrix get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); + virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); + + virtual void process(); + + MobileVRInterface(); + ~MobileVRInterface(); +}; + +#endif // MOBILE_VR_INTERFACE_H diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp new file mode 100644 index 0000000000..f742ecbf00 --- /dev/null +++ b/modules/mobile_vr/register_types.cpp @@ -0,0 +1,43 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "register_types.h" + +#include "mobile_interface.h" + +void register_mobile_vr_types() { + ClassDB::register_class<MobileVRInterface>(); + + Ref<MobileVRInterface> mobile_vr; + mobile_vr.instance(); + ARVRServer::get_singleton()->add_interface(mobile_vr); +} + +void unregister_mobile_vr_types() { +} diff --git a/modules/mobile_vr/register_types.h b/modules/mobile_vr/register_types.h new file mode 100644 index 0000000000..a492fff397 --- /dev/null +++ b/modules/mobile_vr/register_types.h @@ -0,0 +1,31 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ +void register_mobile_vr_types(); +void unregister_mobile_vr_types(); diff --git a/modules/mobile_vr/shaders/SCsub b/modules/mobile_vr/shaders/SCsub new file mode 100644 index 0000000000..cf53c9ebe0 --- /dev/null +++ b/modules/mobile_vr/shaders/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import('env') + +if 'GLES3_GLSL' in env['BUILDERS']: + env.GLES3_GLSL('lens_distorted.glsl'); + diff --git a/modules/mobile_vr/shaders/lens_distorted.glsl b/modules/mobile_vr/shaders/lens_distorted.glsl new file mode 100644 index 0000000000..5a2975d737 --- /dev/null +++ b/modules/mobile_vr/shaders/lens_distorted.glsl @@ -0,0 +1,59 @@ +[vertex] + +layout(location=0) in highp vec4 vertex_attrib; +layout(location=4) in vec2 uv_in; + +uniform float offset_x; + +out vec2 uv_interp; + +void main() { + + uv_interp = uv_in; + gl_Position = vec4(vertex_attrib.x + offset_x, vertex_attrib.y, 0.0, 1.0); +} + +[fragment] + +uniform sampler2D source; //texunit:0 + +uniform vec2 eye_center; +uniform float k1; +uniform float k2; +uniform float upscale; +uniform float aspect_ratio; + +in vec2 uv_interp; + +layout(location = 0) out vec4 frag_color; + +void main() { + vec2 coords = uv_interp; + vec2 offset = coords - eye_center; + + // take aspect ratio into account + offset.y /= aspect_ratio; + + // distort + vec2 offset_sq = offset * offset; + float radius_sq = offset_sq.x + offset_sq.y; + float radius_s4 = radius_sq * radius_sq; + float distortion_scale = 1.0 + (k1 * radius_sq) + (k2 * radius_s4); + offset *= distortion_scale; + + // reapply aspect ratio + offset.y *= aspect_ratio; + + // add our eye center back in + coords = offset + eye_center; + coords /= upscale; + + // and check our color + if (coords.x < -1.0 || coords.y < -1.0 || coords.x > 1.0 || coords.y > 1.0) { + frag_color = vec4(0.0, 0.0, 0.0, 1.0); + } else { + coords = (coords + vec2(1.0)) / vec2(2.0); + frag_color = textureLod(source, coords, 0.0); + } +} + diff --git a/modules/mono/SCsub b/modules/mono/SCsub new file mode 100644 index 0000000000..27e60c4623 --- /dev/null +++ b/modules/mono/SCsub @@ -0,0 +1,204 @@ +#!/usr/bin/env python + +Import('env') + +from compat import byte_to_str + +def make_cs_files_header(src, dst): + with open(dst, 'w') as header: + header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n') + header.write('#ifndef _CS_FILES_DATA_H\n') + header.write('#define _CS_FILES_DATA_H\n\n') + header.write('#include "map.h"\n') + header.write('#include "ustring.h"\n') + inserted_files = '' + import os + for file in os.listdir(src): + if file.endswith('.cs'): + with open(os.path.join(src, file), 'rb') as f: + buf = f.read() + decomp_size = len(buf) + import zlib + buf = zlib.compress(buf) + name = os.path.splitext(file)[0] + header.write('\nstatic const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n') + header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n') + header.write('static const unsigned char _cs_' + name + '_compressed[] = { ') + for i, buf_idx in enumerate(range(len(buf))): + if i > 0: + header.write(', ') + header.write(byte_to_str(buf[buf_idx])) + inserted_files += '\tr_files.insert(\"' + file + '\", ' \ + 'CompressedFile(_cs_' + name + '_compressed_size, ' \ + '_cs_' + name + '_uncompressed_size, ' \ + '_cs_' + name + '_compressed));\n' + header.write(' };\n') + header.write('\nstruct CompressedFile\n' '{\n' + '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n' + '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n' + '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n' + '\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n' + '\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n' + ) + header.write('#endif // _CS_FILES_DATA_H') + + +env.add_source_files(env.modules_sources, '*.cpp') +env.add_source_files(env.modules_sources, 'mono_gd/*.cpp') +env.add_source_files(env.modules_sources, 'utils/*.cpp') + +if env['tools']: + env.add_source_files(env.modules_sources, 'editor/*.cpp') + make_cs_files_header('glue/cs_files', 'glue/cs_compressed.gen.h') + +vars = Variables() +vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True)) +vars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False)) +vars.Update(env) + +# Glue sources +if env['mono_glue']: + env.add_source_files(env.modules_sources, 'glue/*.cpp') +else: + env.Append(CPPDEFINES=['MONO_GLUE_DISABLED']) + +if ARGUMENTS.get('yolo_copy', False): + env.Append(CPPDEFINES=['YOLO_COPY']) + + +# Build GodotSharpTools solution + + +import os + + +def find_msbuild_unix(filename): + import os.path + import sys + + hint_dirs = ['/opt/novell/mono/bin'] + if sys.platform == "darwin": + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin'] + hint_dirs + + for hint_dir in hint_dirs: + hint_path = os.path.join(hint_dir, filename) + if os.path.isfile(hint_path): + return hint_path + + for hint_dir in os.environ["PATH"].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, filename) + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + return None + + +def find_msbuild_windows(): + import mono_reg_utils as monoreg + + bits = env['bits'] + + if bits == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + + if not mono_root: + raise RuntimeError('Cannot find mono root directory') + + msbuild_tools_path = monoreg.find_msbuild_tools_path_reg() + + if msbuild_tools_path: + return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), os.path.join(mono_root, 'lib', 'mono', '4.5')) + else: + msbuild_mono = os.path.join(mono_root, 'bin', 'msbuild.bat') + + if os.path.isfile(msbuild_mono): + return (msbuild_mono, '') + + return None + + +def mono_build_solution(source, target, env): + import subprocess + import mono_reg_utils as monoreg + from shutil import copyfile + + framework_path_override = '' + + if os.name == 'nt': + msbuild_info = find_msbuild_windows() + if msbuild_info is None: + raise RuntimeError('Cannot find MSBuild executable') + msbuild_path = msbuild_info[0] + framework_path_override = msbuild_info[1] + else: + msbuild_path = find_msbuild_unix('msbuild') + if msbuild_path is None: + xbuild_fallback = env['xbuild_fallback'] + + if xbuild_fallback and os.name == 'nt': + print("Option 'xbuild_fallback' not supported on Windows") + xbuild_fallback = False + + if xbuild_fallback: + print('Cannot find MSBuild executable, trying with xbuild') + print('Warning: xbuild is deprecated') + + msbuild_path = find_msbuild_unix('xbuild') + + if msbuild_path is None: + raise RuntimeError('Cannot find xbuild executable') + else: + raise RuntimeError('Cannot find MSBuild executable') + + print('MSBuild path: ' + msbuild_path) + + build_config = 'Release' + + msbuild_args = [ + msbuild_path, + os.path.abspath(str(source[0])), + '/p:Configuration=' + build_config, + ] + + if framework_path_override: + msbuild_args += ['/p:FrameworkPathOverride=' + framework_path_override] + + msbuild_env = os.environ.copy() + + # Needed when running from Developer Command Prompt for VS + if 'PLATFORM' in msbuild_env: + del msbuild_env['PLATFORM'] + + try: + subprocess.check_call(msbuild_args, env=msbuild_env) + except subprocess.CalledProcessError: + raise RuntimeError('GodotSharpTools build failed') + + src_dir = os.path.abspath(os.path.join(str(source[0]), os.pardir, 'bin', build_config)) + dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir)) + + if not os.path.isdir(dst_dir): + if os.path.exists(dst_dir): + raise RuntimeError('Target directory is a file') + os.makedirs(dst_dir) + + asm_file = 'GodotSharpTools.dll' + + copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file)) + + +mono_sln_builder = Builder(action = mono_build_solution) +env.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder}) +env.MonoBuildSolution( + os.path.join(Dir('#bin').abspath, 'GodotSharpTools.dll'), + 'editor/GodotSharpTools/GodotSharpTools.sln' +) diff --git a/modules/mono/config.py b/modules/mono/config.py new file mode 100644 index 0000000000..44eef45f76 --- /dev/null +++ b/modules/mono/config.py @@ -0,0 +1,183 @@ + +import imp +import os +import sys + +from SCons.Script import BoolVariable, Environment, Variables + + +monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py') + + +def find_file_in_dir(directory, files, prefix='', extension=''): + if not extension.startswith('.'): + extension = '.' + extension + for curfile in files: + if os.path.isfile(os.path.join(directory, prefix + curfile + extension)): + return curfile + return '' + + +def can_build(platform): + if platform in ["javascript"]: + return False # Not yet supported + return True + + +def is_enabled(): + # The module is disabled by default. Use module_mono_enabled=yes to enable it. + return False + + +def copy_file_no_replace(src_dir, dst_dir, name): + from shutil import copyfile + + src_path = os.path.join(src_dir, name) + dst_path = os.path.join(dst_dir, name) + need_copy = True + + if not os.path.isdir(dst_dir): + os.mkdir(dst_dir) + elif os.path.exists(dst_path): + need_copy = False + + if need_copy: + copyfile(src_path, dst_path) + + +def configure(env): + env.use_ptrcall = True + + envvars = Variables() + envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) + envvars.Update(env) + + bits = env['bits'] + + mono_static = env['mono_static'] + + mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] + + if env['platform'] == 'windows': + if mono_static: + raise RuntimeError('mono-static: Not supported on Windows') + + if bits == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + elif os.name == 'nt': + mono_root = monoreg.find_mono_root_dir(bits) + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + elif os.name == 'nt': + mono_root = monoreg.find_mono_root_dir(bits) + + if not mono_root: + raise RuntimeError('Mono installation directory not found') + + mono_lib_path = os.path.join(mono_root, 'lib') + + env.Append(LIBPATH=mono_lib_path) + env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) + + mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') + + if not mono_lib_name: + raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + + if os.getenv('VCINSTALLDIR'): + env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) + else: + env.Append(LIBS=mono_lib_name) + + mono_bin_path = os.path.join(mono_root, 'bin') + + mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') + + if not mono_dll_name: + raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) + + copy_file_no_replace(mono_bin_path, 'bin', mono_dll_name + '.dll') + else: + sharedlib_ext = '.dylib' if sys.platform == 'darwin' else '.so' + + mono_root = '' + + if bits == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + + if mono_root: + mono_lib_path = os.path.join(mono_root, 'lib') + + env.Append(LIBPATH=mono_lib_path) + env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) + + mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a') + + if not mono_lib: + raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + + env.Append(CPPFLAGS=['-D_REENTRANT']) + + if mono_static: + mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a') + + if sys.platform == "darwin": + env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file]) + elif sys.platform == "linux" or sys.platform == "linux2": + env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive']) + else: + raise RuntimeError('mono-static: Not supported on this platform') + else: + env.Append(LIBS=[mono_lib]) + + if sys.platform == "darwin": + env.Append(LIBS=['iconv', 'pthread']) + elif sys.platform == "linux" or sys.platform == "linux2": + env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) + + if not mono_static: + mono_so_name = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension=sharedlib_ext) + + if not mono_so_name: + raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path) + + copy_file_no_replace(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) + else: + if mono_static: + raise RuntimeError('mono-static: Not supported with pkg-config. Specify a mono prefix manually') + + env.ParseConfig('pkg-config monosgen-2 --cflags --libs') + + mono_lib_path = '' + mono_so_name = '' + + tmpenv = Environment() + tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') + + for hint_dir in tmpenv['LIBPATH']: + name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) + if name_found: + mono_lib_path = hint_dir + mono_so_name = name_found + break + + if not mono_so_name: + raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH'])) + + copy_file_no_replace(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) + + env.Append(LINKFLAGS='-rdynamic') + + +def get_doc_classes(): + return ["@C#", "CSharpScript", "GodotSharp"] + + +def get_doc_path(): + return "doc_classes" diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp new file mode 100644 index 0000000000..3d91a6de6c --- /dev/null +++ b/modules/mono/csharp_script.cpp @@ -0,0 +1,1988 @@ +/*************************************************************************/ +/* csharp_script.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "csharp_script.h" + +#include <mono/metadata/threads.h> + +#include "os/file_access.h" +#include "os/os.h" +#include "os/thread.h" +#include "project_settings.h" + +#ifdef TOOLS_ENABLED +#include "editor/bindings_generator.h" +#include "editor/csharp_project.h" +#include "editor/editor_node.h" +#include "editor/godotsharp_editor.h" +#include "utils/string_utils.h" +#endif + +#include "godotsharp_dirs.h" +#include "mono_gd/gd_mono_class.h" +#include "mono_gd/gd_mono_marshal.h" +#include "signal_awaiter_utils.h" + +#define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var) + +#ifdef TOOLS_ENABLED +static bool _create_project_solution_if_needed() { + + String sln_path = GodotSharpDirs::get_project_sln_path(); + String csproj_path = GodotSharpDirs::get_project_csproj_path(); + + if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) { + // A solution does not yet exist, create a new one + + CRASH_COND(GodotSharpEditor::get_singleton() == NULL); + return GodotSharpEditor::get_singleton()->call("_create_project_solution"); + } + + return true; +} +#endif + +CSharpLanguage *CSharpLanguage::singleton = NULL; + +String CSharpLanguage::get_name() const { + + return "C#"; +} + +String CSharpLanguage::get_type() const { + + return "CSharpScript"; +} + +String CSharpLanguage::get_extension() const { + + return "cs"; +} + +Error CSharpLanguage::execute_file(const String &p_path) { + + // ?? + return OK; +} + +#ifdef TOOLS_ENABLED +void gdsharp_editor_init_callback() { + + EditorNode *editor = EditorNode::get_singleton(); + editor->add_child(memnew(GodotSharpEditor(editor))); +} +#endif + +void CSharpLanguage::init() { + + gdmono = memnew(GDMono); + gdmono->initialize(); + +#ifdef MONO_GLUE_DISABLED + WARN_PRINT("This binary is built with `mono_glue=no` and cannot be used for scripting"); +#endif + +#if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED) + if (gdmono->get_editor_tools_assembly() != NULL) { + List<String> cmdline_args = OS::get_singleton()->get_cmdline_args(); + BindingsGenerator::handle_cmdline_args(cmdline_args); + } +#endif + +#ifdef TOOLS_ENABLED + EditorNode::add_init_callback(&gdsharp_editor_init_callback); +#endif +} + +void CSharpLanguage::finish() { + + if (gdmono) { + memdelete(gdmono); + gdmono = NULL; + } +} + +void CSharpLanguage::get_reserved_words(List<String> *p_words) const { + + static const char *_reserved_words[] = { + // Reserved keywords + "abstract", + "as", + "base", + "bool", + "break", + "byte", + "case", + "catch", + "char", + "checked", + "class", + "const", + "continue", + "decimal", + "default", + "delegate", + "do", + "double", + "else", + "enum", + "event", + "explicit", + "extern", + "false", + "finally", + "fixed", + "float", + "for", + "forech", + "goto", + "if", + "implicit", + "in", + "int", + "interface", + "internal", + "is", + "lock", + "long", + "namespace", + "new", + "null", + "object", + "operator", + "out", + "override", + "params", + "private", + "protected", + "public", + "readonly", + "ref", + "return", + "sbyte", + "sealed", + "short", + "sizeof", + "stackalloc", + "static", + "string", + "struct", + "switch", + "this", + "throw", + "true", + "try", + "typeof", + "uint", + "ulong", + "unchecked", + "unsafe", + "ushort", + "using", + "virtual", + "volatile", + "void", + "while", + + // Contextual keywords. Not reserved words, but I guess we should include + // them because this seems to be used only for syntax highlighting. + "add", + "ascending", + "by", + "descending", + "dynamic", + "equals", + "from", + "get", + "global", + "group", + "in", + "into", + "join", + "let", + "on", + "orderby", + "partial", + "remove", + "select", + "set", + "value", + "var", + "where", + "yield", + 0 + }; + + const char **w = _reserved_words; + + while (*w) { + p_words->push_back(*w); + w++; + } +} + +void CSharpLanguage::get_comment_delimiters(List<String> *p_delimiters) const { + + p_delimiters->push_back("//"); // single-line comment + p_delimiters->push_back("/* */"); // delimited comment +} + +void CSharpLanguage::get_string_delimiters(List<String> *p_delimiters) const { + + p_delimiters->push_back("' '"); // character literal + p_delimiters->push_back("\" \""); // regular string literal + p_delimiters->push_back("@\" \""); // verbatim string literal +} + +Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { + + String script_template = "using " BINDINGS_NAMESPACE ";\n" + "using System;\n" + "\n" + "public class %CLASS_NAME% : %BASE_CLASS_NAME%\n" + "{\n" + " // Member variables here, example:\n" + " // private int a = 2;\n" + " // private string b = \"textvar\";\n" + "\n" + " public override void _Ready()\n" + " {\n" + " // Called every time the node is added to the scene.\n" + " // Initialization here\n" + " \n" + " }\n" + "\n" + "// public override void _Process(float delta)\n" + "// {\n" + "// // Called every frame. Delta is time since last frame.\n" + "// // Update game logic here.\n" + "// \n" + "// }\n" + "}\n"; + + script_template = script_template.replace("%BASE_CLASS_NAME%", p_base_class_name) + .replace("%CLASS_NAME%", p_class_name); + + Ref<CSharpScript> script; + script.instance(); + script->set_source_code(script_template); + script->set_name(p_class_name); + + return script; +} + +Script *CSharpLanguage::create_script() const { + + return memnew(CSharpScript); +} + +bool CSharpLanguage::has_named_classes() const { + + return false; +} + +bool CSharpLanguage::supports_builtin_mode() const { + + return false; +} + +static String variant_type_to_managed_name(const String &p_var_type_name) { + + if (p_var_type_name.empty()) + return "object"; + + if (!ClassDB::class_exists(p_var_type_name)) { + Variant::Type var_types[] = { + Variant::BOOL, + Variant::INT, + Variant::REAL, + Variant::STRING, + Variant::VECTOR2, + Variant::RECT2, + Variant::VECTOR3, + Variant::TRANSFORM2D, + Variant::PLANE, + Variant::QUAT, + Variant::RECT3, + Variant::BASIS, + Variant::TRANSFORM, + Variant::COLOR, + Variant::NODE_PATH, + Variant::_RID + }; + + for (int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) { + if (p_var_type_name == Variant::get_type_name(var_types[i])) + return p_var_type_name; + } + + if (p_var_type_name == "String") + return "string"; // I prefer this one >:[ + + // TODO these will be rewritten later into custom containers + + if (p_var_type_name == "Array") + return "object[]"; + + if (p_var_type_name == "Dictionary") + return "Dictionary<object, object>"; + + if (p_var_type_name == "PoolByteArray") + return "byte[]"; + if (p_var_type_name == "PoolIntArray") + return "int[]"; + if (p_var_type_name == "PoolRealArray") + return "float[]"; + if (p_var_type_name == "PoolStringArray") + return "string[]"; + if (p_var_type_name == "PoolVector2Array") + return "Vector2[]"; + if (p_var_type_name == "PoolVector3Array") + return "Vector3[]"; + if (p_var_type_name == "PoolColorArray") + return "Color[]"; + + return "object"; + } + + return p_var_type_name; +} + +String CSharpLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const { +#ifdef TOOLS_ENABLED + // FIXME + // - Due to Godot's API limitation this just appends the function to the end of the file + // - Use fully qualified name if there is ambiguity + String s = "private void " + p_name + "("; + for (int i = 0; i < p_args.size(); i++) { + const String &arg = p_args[i]; + + if (i > 0) + s += ", "; + + s += variant_type_to_managed_name(arg.get_slice(":", 1)) + " " + escape_csharp_keyword(arg.get_slice(":", 0)); + } + s += ")\n{\n // Replace with function body\n}\n"; + + return s; +#else + return String(); +#endif +} + +void CSharpLanguage::frame() { + + const Ref<MonoGCHandle> &task_scheduler_handle = GDMonoUtils::mono_cache.task_scheduler_handle; + + if (task_scheduler_handle.is_valid()) { + MonoObject *task_scheduler = task_scheduler_handle->get_target(); + + if (task_scheduler) { + GDMonoUtils::GodotTaskScheduler_Activate thunk = CACHED_METHOD_THUNK(GodotTaskScheduler, Activate); + + ERR_FAIL_NULL(thunk); + + MonoObject *ex; + thunk(task_scheduler, &ex); + + if (ex) { + mono_print_unhandled_exception(ex); + ERR_FAIL(); + } + } + } +} + +struct CSharpScriptDepSort { + + // must support sorting so inheritance works properly (parent must be reloaded first) + bool operator()(const Ref<CSharpScript> &A, const Ref<CSharpScript> &B) const { + if (A == B) + return false; // shouldn't happen but.. + GDMonoClass *I = B->base; + while (I) { + if (I == A->script_class) { + // A is a base of B + return true; + } + + I = I->get_parent_class(); + } + + return false; // not a base + } +}; + +void CSharpLanguage::reload_all_scripts() { + +#ifdef DEBUG_ENABLED + +#ifndef NO_THREADS + lock->lock(); +#endif + + List<Ref<CSharpScript> > scripts; + + SelfList<CSharpScript> *elem = script_list.first(); + while (elem) { + if (elem->self()->get_path().is_resource_file()) { + scripts.push_back(Ref<CSharpScript>(elem->self())); //cast to gdscript to avoid being erased by accident + } + elem = elem->next(); + } + +#ifndef NO_THREADS + lock->unlock(); +#endif + + //as scripts are going to be reloaded, must proceed without locking here + + scripts.sort_custom<CSharpScriptDepSort>(); //update in inheritance dependency order + + for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) { + E->get()->load_source_code(E->get()->get_path()); + E->get()->reload(true); + } +#endif +} + +void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) { + + (void)p_script; // UNUSED + +#ifdef TOOLS_ENABLED + MonoReloadNode::get_singleton()->restart_reload_timer(); + reload_assemblies_if_needed(p_soft_reload); +#endif +} + +#ifdef TOOLS_ENABLED +void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { + + if (gdmono->is_runtime_initialized()) { + + GDMonoAssembly *proj_assembly = gdmono->get_project_assembly(); + + String name = ProjectSettings::get_singleton()->get("application/config/name"); + if (name.empty()) { + name = "UnnamedProject"; + } + + if (proj_assembly) { + String proj_asm_path = proj_assembly->get_path(); + + if (!FileAccess::exists(proj_assembly->get_path())) { + // Maybe it wasn't loaded from the default path, so check this as well + proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name); + if (!FileAccess::exists(proj_asm_path)) + return; // No assembly to load + } + + if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time()) + return; // Already up to date + } else { + if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name))) + return; // No assembly to load + } + } + +#ifndef NO_THREADS + lock->lock(); +#endif + + List<Ref<CSharpScript> > scripts; + + SelfList<CSharpScript> *elem = script_list.first(); + while (elem) { + if (elem->self()->get_path().is_resource_file()) { + + scripts.push_back(Ref<CSharpScript>(elem->self())); //cast to CSharpScript to avoid being erased by accident + } + elem = elem->next(); + } + +#ifndef NO_THREADS + lock->unlock(); +#endif + + //when someone asks you why dynamically typed languages are easier to write.... + + Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > > to_reload; + + //as scripts are going to be reloaded, must proceed without locking here + + scripts.sort_custom<CSharpScriptDepSort>(); //update in inheritance dependency order + + for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) { + + to_reload.insert(E->get(), Map<ObjectID, List<Pair<StringName, Variant> > >()); + + if (!p_soft_reload) { + + //save state and remove script from instances + Map<ObjectID, List<Pair<StringName, Variant> > > &map = to_reload[E->get()]; + + while (E->get()->instances.front()) { + Object *obj = E->get()->instances.front()->get(); + //save instance info + List<Pair<StringName, Variant> > state; + if (obj->get_script_instance()) { + + obj->get_script_instance()->get_property_state(state); + + Ref<MonoGCHandle> gchandle = CAST_CSHARP_INSTANCE(obj->get_script_instance())->gchandle; + if (gchandle.is_valid()) + gchandle->release(); + + map[obj->get_instance_id()] = state; + obj->set_script(RefPtr()); + } + } + + //same thing for placeholders + while (E->get()->placeholders.size()) { + + Object *obj = E->get()->placeholders.front()->get()->get_owner(); + //save instance info + List<Pair<StringName, Variant> > state; + if (obj->get_script_instance()) { + obj->get_script_instance()->get_property_state(state); + map[obj->get_instance_id()] = state; + obj->set_script(RefPtr()); + } + } + + for (Map<ObjectID, List<Pair<StringName, Variant> > >::Element *F = E->get()->pending_reload_state.front(); F; F = F->next()) { + map[F->key()] = F->get(); //pending to reload, use this one instead + } + + E->get()->_clear(); + } + } + + if (gdmono->reload_scripts_domain() != OK) + return; + + for (Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > >::Element *E = to_reload.front(); E; E = E->next()) { + + Ref<CSharpScript> scr = E->key(); + scr->exports_invalidated = true; + scr->reload(p_soft_reload); + scr->update_exports(); + + //restore state if saved + for (Map<ObjectID, List<Pair<StringName, Variant> > >::Element *F = E->get().front(); F; F = F->next()) { + + Object *obj = ObjectDB::get_instance(F->key()); + if (!obj) + continue; + + if (!p_soft_reload) { + //clear it just in case (may be a pending reload state) + obj->set_script(RefPtr()); + } + obj->set_script(scr.get_ref_ptr()); + if (!obj->get_script_instance()) { + //failed, save reload state for next time if not saved + if (!scr->pending_reload_state.has(obj->get_instance_id())) { + scr->pending_reload_state[obj->get_instance_id()] = F->get(); + } + continue; + } + + for (List<Pair<StringName, Variant> >::Element *G = F->get().front(); G; G = G->next()) { + obj->get_script_instance()->set(G->get().first, G->get().second); + } + + scr->pending_reload_state.erase(obj->get_instance_id()); //as it reloaded, remove pending state + } + + //if instance states were saved, set them! + } + + if (Engine::get_singleton()->is_editor_hint()) + EditorNode::get_singleton()->get_property_editor()->update_tree(); +} +#endif + +void CSharpLanguage::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("cs"); +} + +#ifdef TOOLS_ENABLED +Error CSharpLanguage::open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { + + return GodotSharpEditor::get_singleton()->open_in_external_editor(p_script, p_line, p_col); +} + +bool CSharpLanguage::overrides_external_editor() { + + return GodotSharpEditor::get_singleton()->overrides_external_editor(); +} +#endif + +void CSharpLanguage::thread_enter() { + +#if 0 + if (mono->is_runtime_initialized()) { + GDMonoUtils::attach_current_thread(); + } +#endif +} + +void CSharpLanguage::thread_exit() { + +#if 0 + if (mono->is_runtime_initialized()) { + GDMonoUtils::detach_current_thread(); + } +#endif +} + +bool CSharpLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) { + + // Break because of parse error + if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) { + // TODO + //_debug_parse_err_line = p_line; + //_debug_parse_err_file = p_file; + //_debug_error = p_error; + ScriptDebugger::get_singleton()->debug(this, false); + return true; + } else { + return false; + } +} + +bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) { + + if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) { + // TODO + //_debug_parse_err_line = -1; + //_debug_parse_err_file = ""; + //_debug_error = p_error; + ScriptDebugger::get_singleton()->debug(this, p_allow_continue); + return true; + } else { + return false; + } +} + +void CSharpLanguage::set_language_index(int p_idx) { + + ERR_FAIL_COND(lang_idx != -1); + lang_idx = p_idx; +} + +CSharpLanguage::CSharpLanguage() { + + ERR_FAIL_COND(singleton); + singleton = this; + + gdmono = NULL; + +#ifdef NO_THREADS + lock = NULL; + gchandle_bind_lock = NULL; +#else + lock = Mutex::create(); + script_bind_lock = Mutex::create(); +#endif + + lang_idx = -1; +} + +CSharpLanguage::~CSharpLanguage() { + + finish(); + + if (lock) { + memdelete(lock); + lock = NULL; + } + + if (script_bind_lock) { + memdelete(script_bind_lock); + script_bind_lock = NULL; + } + + singleton = NULL; +} + +void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { + +#ifdef DEBUG_ENABLED + // I don't trust you + if (p_object->get_script_instance()) + CRASH_COND(NULL != CAST_CSHARP_INSTANCE(p_object->get_script_instance())); +#endif + + StringName type_name = p_object->get_class_name(); + + // ¯\_(ツ)_/¯ + const ClassDB::ClassInfo *classinfo = ClassDB::classes.getptr(type_name); + while (classinfo && !classinfo->exposed) + classinfo = classinfo->inherits_ptr; + ERR_FAIL_NULL_V(classinfo, NULL); + type_name = classinfo->name; + + GDMonoClass *type_class = GDMonoUtils::type_get_proxy_class(type_name); + + ERR_FAIL_NULL_V(type_class, NULL); + + MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(type_class, type_name, p_object); + + ERR_FAIL_NULL_V(mono_object, NULL); + + // Tie managed to unmanaged + bool strong_handle = true; + Reference *ref = Object::cast_to<Reference>(p_object); + + if (ref) { + strong_handle = false; + + // Unsafe refcount increment. The managed instance also counts as a reference. + // This way if the unmanaged world has no references to our owner + // but the managed instance is alive, the refcount will be 1 instead of 0. + // See: _GodotSharp::_dispose_object(Object *p_object) + + ref->reference(); + } + + Ref<MonoGCHandle> gchandle = strong_handle ? MonoGCHandle::create_strong(mono_object) : + MonoGCHandle::create_weak(mono_object); + +#ifndef NO_THREADS + script_bind_lock->lock(); +#endif + + void *data = (void *)gchandle_bindings.insert(p_object, gchandle); + +#ifndef NO_THREADS + script_bind_lock->unlock(); +#endif + + return data; +} + +void CSharpLanguage::free_instance_binding_data(void *p_data) { + +#ifndef NO_THREADS + script_bind_lock->lock(); +#endif + + gchandle_bindings.erase((Map<Object *, Ref<MonoGCHandle> >::Element *)p_data); + +#ifndef NO_THREADS + script_bind_lock->unlock(); +#endif +} + +void CSharpInstance::_ml_call_reversed(GDMonoClass *klass, const StringName &p_method, const Variant **p_args, int p_argcount) { + + GDMonoClass *base = klass->get_parent_class(); + if (base && base != script->native) + _ml_call_reversed(base, p_method, p_args, p_argcount); + + GDMonoMethod *method = klass->get_method(p_method, p_argcount); + + if (method) { + method->invoke(get_mono_object(), p_args); + } +} + +CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle) { + + CSharpInstance *instance = memnew(CSharpInstance); + + Reference *ref = Object::cast_to<Reference>(p_owner); + + instance->base_ref = ref != NULL; + instance->script = Ref<CSharpScript>(p_script); + instance->owner = p_owner; + instance->gchandle = p_gchandle; + + if (instance->base_ref) + instance->_reference_owner_unsafe(); + + p_script->instances.insert(p_owner); + + return instance; +} + +MonoObject *CSharpInstance::get_mono_object() const { +#ifdef DEBUG_ENABLED + CRASH_COND(gchandle.is_null()); +#endif + return gchandle->get_target(); +} + +bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { + + ERR_FAIL_COND_V(!script.is_valid(), false); + + GDMonoClass *top = script->script_class; + + while (top && top != script->native) { + GDMonoField *field = script->script_class->get_field(p_name); + + if (field) { + MonoObject *mono_object = get_mono_object(); + + ERR_EXPLAIN("Reference has been garbage collected?"); + ERR_FAIL_NULL_V(mono_object, false); + + field->set_value(mono_object, p_value); + + return true; + } + + top = top->get_parent_class(); + } + + // Call _set + + Variant name = p_name; + const Variant *args[2] = { &name, &p_value }; + + MonoObject *mono_object = get_mono_object(); + top = script->script_class; + + while (top && top != script->native) { + GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_set), 2); + + if (method) { + MonoObject *ret = method->invoke(mono_object, args); + + if (ret && GDMonoMarshal::unbox<MonoBoolean>(ret) == true) + return true; + } + + top = top->get_parent_class(); + } + + return false; +} + +bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const { + + ERR_FAIL_COND_V(!script.is_valid(), false); + + GDMonoClass *top = script->script_class; + + while (top && top != script->native) { + GDMonoField *field = top->get_field(p_name); + + if (field) { + MonoObject *mono_object = get_mono_object(); + + ERR_EXPLAIN("Reference has been garbage collected?"); + ERR_FAIL_NULL_V(mono_object, false); + + MonoObject *value = field->get_value(mono_object); + r_ret = GDMonoMarshal::mono_object_to_variant(value, field->get_type()); + return true; + } + + // Call _get + + GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get), 1); + + if (method) { + Variant name = p_name; + const Variant *args[1] = { &name }; + + MonoObject *ret = method->invoke(get_mono_object(), args); + + if (ret) { + r_ret = GDMonoMarshal::mono_object_to_variant(ret); + return true; + } + } + + top = top->get_parent_class(); + } + + return false; +} + +void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const { + + for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) { + p_properties->push_back(E->value()); + } +} + +Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const { + + if (script->member_info.has(p_name)) { + if (r_is_valid) + *r_is_valid = true; + return script->member_info[p_name].type; + } + + if (r_is_valid) + *r_is_valid = false; + + return Variant::NIL; +} + +bool CSharpInstance::has_method(const StringName &p_method) const { + + if (!script.is_valid()) + return false; + + GDMonoClass *top = script->script_class; + + while (top && top != script->native) { + if (top->has_method(p_method)) { + return true; + } + + top = top->get_parent_class(); + } + + return false; +} + +Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) { + + MonoObject *mono_object = get_mono_object(); + + ERR_EXPLAIN("Reference has been garbage collected?"); + ERR_FAIL_NULL_V(mono_object, Variant()); + + if (!script.is_valid()) + return Variant(); + + GDMonoClass *top = script->script_class; + + while (top && top != script->native) { + GDMonoMethod *method = top->get_method(p_method, p_argcount); + + if (method) { + MonoObject *return_value = method->invoke(mono_object, p_args); + + if (return_value) { + return GDMonoMarshal::mono_object_to_variant(return_value, method->get_return_type()); + } else { + return Variant(); + } + } + + top = top->get_parent_class(); + } + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; + + return Variant(); +} + +void CSharpInstance::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) { + + if (script.is_valid()) { + MonoObject *mono_object = get_mono_object(); + + GDMonoClass *top = script->script_class; + + while (top && top != script->native) { + GDMonoMethod *method = top->get_method(p_method, p_argcount); + + if (method) + method->invoke(mono_object, p_args); + + top = top->get_parent_class(); + } + } +} + +void CSharpInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) { + + if (script.is_valid()) { + _ml_call_reversed(script->script_class, p_method, p_args, p_argcount); + } +} + +void CSharpInstance::_reference_owner_unsafe() { + +#ifdef DEBUG_ENABLED + CRASH_COND(!base_ref); +#endif + + // Unsafe refcount increment. The managed instance also counts as a reference. + // This way if the unmanaged world has no references to our owner + // but the managed instance is alive, the refcount will be 1 instead of 0. + // See: _unreference_owner_unsafe() + + // May not me referenced yet, so we must use init_ref() instead of reference() + Object::cast_to<Reference>(owner)->init_ref(); +} + +void CSharpInstance::_unreference_owner_unsafe() { + +#ifdef DEBUG_ENABLED + CRASH_COND(!base_ref); +#endif + + // Called from CSharpInstance::mono_object_disposed() or ~CSharpInstance() + + // Unsafe refcount decrement. The managed instance also counts as a reference. + // See: _reference_owner_unsafe() + + if (Object::cast_to<Reference>(owner)->unreference()) { + memdelete(owner); + owner = NULL; + } +} + +void CSharpInstance::mono_object_disposed() { + + if (base_ref) + _unreference_owner_unsafe(); +} + +void CSharpInstance::refcount_incremented() { + + CRASH_COND(!base_ref); + + Reference *ref_owner = Object::cast_to<Reference>(owner); + + if (ref_owner->reference_get_count() > 1) { // Remember the managed side holds a reference, hence 1 instead of 0 here + // The reference count was increased after the managed side was the only one referencing our owner. + // This means the owner is being referenced again by the unmanaged side, + // so the owner must hold the managed side alive again to avoid it from being GCed. + + // Release the current weak handle and replace it with a strong handle. + uint32_t strong_gchandle = MonoGCHandle::make_strong_handle(gchandle->get_target()); + gchandle->release(); + gchandle->set_handle(strong_gchandle); + } +} + +bool CSharpInstance::refcount_decremented() { + + CRASH_COND(!base_ref); + + Reference *ref_owner = Object::cast_to<Reference>(owner); + + int refcount = ref_owner->reference_get_count(); + + if (refcount == 1) { // Remember the managed side holds a reference, hence 1 instead of 0 here + // If owner owner is no longer referenced by the unmanaged side, + // the managed instance takes responsibility of deleting the owner when GCed. + + // Release the current strong handle and replace it with a weak handle. + uint32_t weak_gchandle = MonoGCHandle::make_weak_handle(gchandle->get_target()); + gchandle->release(); + gchandle->set_handle(weak_gchandle); + + return false; + } + + ref_dying = (refcount == 0); + + return ref_dying; +} + +ScriptInstance::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) const { + + GDMonoClass *top = script->script_class; + + while (top && top != script->native) { + GDMonoMethod *method = top->get_method(p_method); + + if (method) { // TODO should we reject static methods? + // TODO cache result + if (method->has_attribute(CACHED_CLASS(RemoteAttribute))) + return RPC_MODE_REMOTE; + if (method->has_attribute(CACHED_CLASS(SyncAttribute))) + return RPC_MODE_SYNC; + if (method->has_attribute(CACHED_CLASS(MasterAttribute))) + return RPC_MODE_MASTER; + if (method->has_attribute(CACHED_CLASS(SlaveAttribute))) + return RPC_MODE_SLAVE; + } + + top = top->get_parent_class(); + } + + return RPC_MODE_DISABLED; +} + +ScriptInstance::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variable) const { + + GDMonoClass *top = script->script_class; + + while (top && top != script->native) { + GDMonoField *field = top->get_field(p_variable); + + if (field) { // TODO should we reject static fields? + // TODO cache result + if (field->has_attribute(CACHED_CLASS(RemoteAttribute))) + return RPC_MODE_REMOTE; + if (field->has_attribute(CACHED_CLASS(SyncAttribute))) + return RPC_MODE_SYNC; + if (field->has_attribute(CACHED_CLASS(MasterAttribute))) + return RPC_MODE_MASTER; + if (field->has_attribute(CACHED_CLASS(SlaveAttribute))) + return RPC_MODE_SLAVE; + } + + top = top->get_parent_class(); + } + + return RPC_MODE_DISABLED; +} + +void CSharpInstance::notification(int p_notification) { + + Variant value = p_notification; + const Variant *args[1] = { &value }; + + call_multilevel(CACHED_STRING_NAME(_notification), args, 1); +} + +Ref<Script> CSharpInstance::get_script() const { + + return script; +} + +ScriptLanguage *CSharpInstance::get_language() { + + return CSharpLanguage::get_singleton(); +} + +CSharpInstance::CSharpInstance() { + + owner = NULL; + base_ref = false; + ref_dying = false; +} + +CSharpInstance::~CSharpInstance() { + + if (gchandle.is_valid()) { + gchandle->release(); // Make sure it's released + } + + if (base_ref && !ref_dying) { // it may be called from the owner's destructor +#ifdef DEBUG_ENABLED + CRASH_COND(!owner); // dunno, just in case +#endif + _unreference_owner_unsafe(); + } + + if (script.is_valid() && owner) { +#ifndef NO_THREADS + CSharpLanguage::singleton->lock->lock(); +#endif + +#ifdef DEBUG_ENABLED + // CSharpInstance must not be created unless it's going to be added to the list for sure + Set<Object *>::Element *match = script->instances.find(owner); + CRASH_COND(!match); + script->instances.erase(match); +#else + script->instances.erase(owner); +#endif + +#ifndef NO_THREADS + CSharpLanguage::singleton->lock->unlock(); +#endif + } +} + +#ifdef TOOLS_ENABLED +void CSharpScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) { + + placeholders.erase(p_placeholder); +} +#endif + +#ifdef TOOLS_ENABLED +void CSharpScript::_update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames) { + + if (base_cache.is_valid()) { + base_cache->_update_exports_values(values, propnames); + } + + for (Map<StringName, Variant>::Element *E = exported_members_defval_cache.front(); E; E = E->next()) { + values[E->key()] = E->get(); + } + + for (List<PropertyInfo>::Element *E = exported_members_cache.front(); E; E = E->next()) { + propnames.push_back(E->get()); + } +} +#endif + +bool CSharpScript::_update_exports() { + +#ifdef TOOLS_ENABLED + if (!valid) + return false; + + bool changed = false; + + if (exports_invalidated) { + exports_invalidated = false; + + changed = true; + + member_info.clear(); + exported_members_cache.clear(); + exported_members_defval_cache.clear(); + + // We are creating a temporary new instance of the class here to get the default value + // TODO Workaround. Should be replaced with IL opcodes analysis + + MonoObject *tmp_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_raw()); + + if (tmp_object) { + CACHED_FIELD(GodotObject, ptr)->set_value_raw(tmp_object, tmp_object); // FIXME WTF is this workaround + + GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0); + MonoObject *ex = NULL; + ctor->invoke(tmp_object, NULL, &ex); + + if (ex) { + ERR_PRINT("Exception thrown from constructor of temporary MonoObject:"); + mono_print_unhandled_exception(ex); + tmp_object = NULL; + ERR_FAIL_V(false); + } + } else { + ERR_PRINT("Failed to create temporary MonoObject"); + return false; + } + + GDMonoClass *top = script_class; + + while (top && top != native) { + const Vector<GDMonoField *> &fields = top->get_all_fields(); + + for (int i = 0; i < fields.size(); i++) { + GDMonoField *field = fields[i]; + + if (field->is_static()) { + if (field->has_attribute(CACHED_CLASS(ExportAttribute))) + ERR_PRINTS("Cannot export field because it is static: " + top->get_full_name() + "." + field->get_name()); + continue; + } + + String name = field->get_name(); + StringName cname = name; + + if (member_info.has(cname)) + continue; + + ManagedType field_type = field->get_type(); + Variant::Type type = GDMonoMarshal::managed_to_variant_type(field_type); + + if (field->has_attribute(CACHED_CLASS(ExportAttribute))) { + // Field has Export attribute + MonoObject *attr = field->get_attribute(CACHED_CLASS(ExportAttribute)); + + PropertyHint hint; + String hint_string; + + if (type == Variant::NIL) { + ERR_PRINTS("Unknown type of exported field: " + top->get_full_name() + "." + field->get_name()); + continue; + } else if (type == Variant::INT && field_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(field_type.type_class->get_raw())) { + type = Variant::INT; + hint = PROPERTY_HINT_ENUM; + + Vector<MonoClassField *> fields = field_type.type_class->get_enum_fields(); + + for (int i = 0; i < fields.size(); i++) { + if (i > 0) + hint_string += ","; + hint_string += mono_field_get_name(fields[i]); + } + } else if (type == Variant::OBJECT && CACHED_CLASS(GodotReference)->is_assignable_from(field_type.type_class)) { + hint = PROPERTY_HINT_RESOURCE_TYPE; + hint_string = NATIVE_GDMONOCLASS_NAME(field_type.type_class); + } else { + hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)); + hint_string = CACHED_FIELD(ExportAttribute, hint_string)->get_string_value(attr); + } + + PropertyInfo prop_info = PropertyInfo(type, name, hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE); + + member_info[cname] = prop_info; + exported_members_cache.push_back(prop_info); + + if (tmp_object) { + exported_members_defval_cache[cname] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object)); + } + } else { + member_info[cname] = PropertyInfo(type, name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE); + } + } + + top = top->get_parent_class(); + } + } + + if (placeholders.size()) { + // Update placeholders if any + Map<StringName, Variant> values; + List<PropertyInfo> propnames; + _update_exports_values(values, propnames); + + for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { + E->get()->update(propnames, values); + } + } + + return changed; +#endif + return false; +} + +void CSharpScript::_clear() { + + tool = false; + valid = false; + + base = NULL; + native = NULL; + script_class = NULL; +} + +Variant CSharpScript::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) { + + GDMonoClass *top = script_class; + + while (top && top != native) { + GDMonoMethod *method = top->get_method(p_method, p_argcount); + + if (method && method->is_static()) { + MonoObject *result = method->invoke(NULL, p_args); + + if (result) { + return GDMonoMarshal::mono_object_to_variant(result, method->get_return_type()); + } else { + return Variant(); + } + } + + top = top->get_parent_class(); + } + + // No static method found. Try regular instance calls + return Script::call(p_method, p_args, p_argcount, r_error); +} + +void CSharpScript::_resource_path_changed() { + + String path = get_path(); + + if (!path.empty()) { + name = get_path().get_file().get_basename(); + } +} + +void CSharpScript::_bind_methods() { + + ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &CSharpScript::_new, MethodInfo(Variant::OBJECT, "new")); +} + +Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class) { + + // This method should not fail + + CRASH_COND(!p_class); + + Ref<CSharpScript> script = memnew(CSharpScript); + + script->name = p_class->get_name(); + script->script_class = p_class; + script->native = GDMonoUtils::get_class_native_base(script->script_class); + + CRASH_COND(script->native == NULL); + + GDMonoClass *base = script->script_class->get_parent_class(); + + if (base != script->native) + script->base = base; + +#ifdef DEBUG_ENABLED + // For debug builds, we must fetch from all native base methods as well. + // Native base methods must be fetched before the current class. + // Not needed if the script class itself is a native class. + + if (script->script_class != script->native) { + GDMonoClass *native_top = script->native; + while (native_top) { + native_top->fetch_methods_with_godot_api_checks(script->native); + + if (native_top == CACHED_CLASS(GodotObject)) + break; + + native_top = native_top->get_parent_class(); + } + } +#endif + + script->script_class->fetch_methods_with_godot_api_checks(script->native); + + // Need to fetch method from base classes as well + GDMonoClass *top = script->script_class; + while (top && top != script->native) { + top->fetch_methods_with_godot_api_checks(script->native); + top = top->get_parent_class(); + } + + return script; +} + +bool CSharpScript::can_instance() const { + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + + if (get_path().find("::") == -1) { // Ignore if built-in script. Can happen if the file is deleted... + if (_create_project_solution_if_needed()) { + CSharpProject::add_item(GodotSharpDirs::get_project_csproj_path(), + "Compile", + ProjectSettings::get_singleton()->globalize_path(get_path())); + } else { + ERR_PRINTS("Cannot add " + get_path() + " to the C# project because it could not be created."); + } + } + } +#endif + + return valid || (!tool && !ScriptServer::is_scripting_enabled()); +} + +StringName CSharpScript::get_instance_base_type() const { + + if (native) + return native->get_name(); + else + return StringName(); +} + +CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error) { + + /* STEP 1, CREATE */ + + CSharpInstance *instance = memnew(CSharpInstance); + instance->base_ref = p_isref; + instance->script = Ref<CSharpScript>(this); + instance->owner = p_owner; + instance->owner->set_script_instance(instance); + + if (instance->base_ref) + instance->_reference_owner_unsafe(); + + /* STEP 2, INITIALIZE AND CONSTRUCT */ + + MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_raw()); + + if (!mono_object) { + instance->script = Ref<CSharpScript>(); + instance->owner->set_script_instance(NULL); + r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL; + ERR_EXPLAIN("Failed to allocate memory for the object"); + ERR_FAIL_V(NULL); + } + +#ifndef NO_THREADS + CSharpLanguage::singleton->lock->lock(); +#endif + + instances.insert(instance->owner); + +#ifndef NO_THREADS + CSharpLanguage::singleton->lock->unlock(); +#endif + + CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, instance->owner); + + // Construct + GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount); + ctor->invoke(mono_object, p_args, NULL); + + // Tie managed to unmanaged + instance->gchandle = MonoGCHandle::create_strong(mono_object); + + /* STEP 3, PARTY */ + + //@TODO make thread safe + return instance; +} + +Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Variant::CallError &r_error) { + + if (!valid) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); + } + + r_error.error = Variant::CallError::CALL_OK; + REF ref; + Object *owner = NULL; + + ERR_FAIL_NULL_V(native, Variant()); + + owner = ClassDB::instance(NATIVE_GDMONOCLASS_NAME(native)); + + Reference *r = Object::cast_to<Reference>(owner); + if (r) { + ref = REF(r); + } + + CSharpInstance *instance = _create_instance(p_args, p_argcount, owner, r != NULL, r_error); + if (!instance) { + if (ref.is_null()) { + memdelete(owner); //no owner, sorry + } + return Variant(); + } + + if (ref.is_valid()) { + return ref; + } else { + return owner; + } +} + +ScriptInstance *CSharpScript::instance_create(Object *p_this) { + + if (!valid) + return NULL; + + if (!tool && !ScriptServer::is_scripting_enabled()) { +#ifdef TOOLS_ENABLED + PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(CSharpLanguage::get_singleton(), Ref<Script>(this), p_this)); + placeholders.insert(si); + _update_exports(); + return si; +#else + return NULL; +#endif + } + + if (native) { + String native_name = native->get_name(); + if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) { + if (ScriptDebugger::get_singleton()) { + CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0, "Script inherits from native type '" + native_name + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'"); + } + ERR_EXPLAIN("Script inherits from native type '" + native_name + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'"); + ERR_FAIL_V(NULL); + } + } + + Variant::CallError unchecked_error; + return _create_instance(NULL, 0, p_this, Object::cast_to<Reference>(p_this), unchecked_error); +} + +bool CSharpScript::instance_has(const Object *p_this) const { + +#ifndef NO_THREADS + CSharpLanguage::singleton->lock->lock(); +#endif + + bool ret = instances.has((Object *)p_this); + +#ifndef NO_THREADS + CSharpLanguage::singleton->lock->unlock(); +#endif + + return ret; +} + +bool CSharpScript::has_source_code() const { + + return !source.empty(); +} + +String CSharpScript::get_source_code() const { + + return source; +} + +void CSharpScript::set_source_code(const String &p_code) { + + if (source == p_code) + return; + source = p_code; +#ifdef TOOLS_ENABLED + source_changed_cache = true; +#endif +} + +bool CSharpScript::has_method(const StringName &p_method) const { + + return script_class->has_method(p_method); +} + +Error CSharpScript::reload(bool p_keep_state) { + +#ifndef NO_THREADS + CSharpLanguage::singleton->lock->lock(); +#endif + + bool has_instances = instances.size(); + +#ifndef NO_THREADS + CSharpLanguage::singleton->lock->unlock(); +#endif + + ERR_FAIL_COND_V(!p_keep_state && has_instances, ERR_ALREADY_IN_USE); + + GDMonoAssembly *project_assembly = GDMono::get_singleton()->get_project_assembly(); + + if (project_assembly) { + script_class = project_assembly->get_object_derived_class(name); + + if (!script_class) { + ERR_PRINTS("Cannot find class " + name + " for script " + get_path()); + } +#ifdef DEBUG_ENABLED + else if (OS::get_singleton()->is_stdout_verbose()) { + OS::get_singleton()->print(String("Found class " + script_class->get_namespace() + "." + + script_class->get_name() + " for script " + get_path() + "\n") + .utf8()); + } +#endif + + valid = script_class != NULL; + + if (script_class) { + tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute)); + + native = GDMonoUtils::get_class_native_base(script_class); + + CRASH_COND(native == NULL); + + GDMonoClass *base_class = script_class->get_parent_class(); + + if (base_class != native) + base = base_class; + +#ifdef DEBUG_ENABLED + // For debug builds, we must fetch from all native base methods as well. + // Native base methods must be fetched before the current class. + // Not needed if the script class itself is a native class. + + if (script_class != native) { + GDMonoClass *native_top = native; + while (native_top) { + native_top->fetch_methods_with_godot_api_checks(native); + + if (native_top == CACHED_CLASS(GodotObject)) + break; + + native_top = native_top->get_parent_class(); + } + } +#endif + + script_class->fetch_methods_with_godot_api_checks(native); + + // Need to fetch method from base classes as well + GDMonoClass *top = script_class; + while (top && top != native) { + top->fetch_methods_with_godot_api_checks(native); + top = top->get_parent_class(); + } + } + + return OK; + } + + return ERR_FILE_MISSING_DEPENDENCIES; +} + +ScriptLanguage *CSharpScript::get_language() const { + + return CSharpLanguage::get_singleton(); +} + +bool CSharpScript::get_property_default_value(const StringName &p_property, Variant &r_value) const { + +#ifdef TOOLS_ENABLED + + const Map<StringName, Variant>::Element *E = exported_members_defval_cache.find(p_property); + if (E) { + r_value = E->get(); + return true; + } + + if (base_cache.is_valid()) { + return base_cache->get_property_default_value(p_property, r_value); + } + +#endif + return false; +} + +void CSharpScript::update_exports() { + +#ifdef TOOLS_ENABLED + _update_exports(); +#endif +} + +Ref<Script> CSharpScript::get_base_script() const { + + // TODO search in metadata file once we have it, not important any way? + return Ref<Script>(); +} + +void CSharpScript::get_script_property_list(List<PropertyInfo> *p_list) const { + + for (Map<StringName, PropertyInfo>::Element *E = member_info.front(); E; E = E->next()) { + p_list->push_back(E->value()); + } +} + +int CSharpScript::get_member_line(const StringName &p_member) const { + + // TODO omnisharp + return -1; +} + +Error CSharpScript::load_source_code(const String &p_path) { + + PoolVector<uint8_t> sourcef; + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + ERR_FAIL_COND_V(err != OK, err); + + int len = f->get_len(); + sourcef.resize(len + 1); + PoolVector<uint8_t>::Write w = sourcef.write(); + int r = f->get_buffer(w.ptr(), len); + f->close(); + memdelete(f); + ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); + w[len] = 0; + + String s; + if (s.parse_utf8((const char *)w.ptr())) { + + ERR_EXPLAIN("Script '" + p_path + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode."); + ERR_FAIL_V(ERR_INVALID_DATA); + } + + source = s; + +#ifdef TOOLS_ENABLED + source_changed_cache = true; +#endif + + return OK; +} + +StringName CSharpScript::get_script_name() const { + + return name; +} + +CSharpScript::CSharpScript() + : script_list(this) { + + _clear(); + +#ifdef TOOLS_ENABLED + source_changed_cache = false; + exports_invalidated = true; +#endif + + _resource_path_changed(); + +#ifdef DEBUG_ENABLED + +#ifndef NO_THREADS + CSharpLanguage::get_singleton()->lock->lock(); +#endif + + CSharpLanguage::get_singleton()->script_list.add(&script_list); + +#ifndef NO_THREADS + CSharpLanguage::get_singleton()->lock->unlock(); +#endif + +#endif // DEBUG_ENABLED +} + +CSharpScript::~CSharpScript() { + +#ifdef DEBUG_ENABLED + +#ifndef NO_THREADS + CSharpLanguage::get_singleton()->lock->lock(); +#endif + + CSharpLanguage::get_singleton()->script_list.remove(&script_list); + +#ifndef NO_THREADS + CSharpLanguage::get_singleton()->lock->unlock(); +#endif + +#endif // DEBUG_ENABLED +} + +/*************** RESOURCE ***************/ + +RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p_original_path, Error *r_error) { + + if (r_error) + *r_error = ERR_FILE_CANT_OPEN; + + // TODO ignore anything inside bin/ and obj/ in tools builds? + + CSharpScript *script = memnew(CSharpScript); + + Ref<CSharpScript> scriptres(script); + +#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED) + Error err = script->load_source_code(p_path); + ERR_FAIL_COND_V(err != OK, RES()); +#endif + + script->set_path(p_original_path); + +#ifndef TOOLS_ENABLED + +#ifdef DEBUG_ENABLED + // User is responsible for thread attach/detach + ERR_EXPLAIN("Thread is not attached"); + CRASH_COND(mono_domain_get() == NULL); +#endif + +#else + if (Engine::get_singleton()->is_editor_hint() && mono_domain_get() == NULL) { + + CRASH_COND(Thread::get_caller_id() == Thread::get_main_id()); + + // Thread is not attached, but we will make an exception in this case + // because this may be called by one of the editor's worker threads. + // Attach this thread temporarily to reload the script. + + MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN); + CRASH_COND(mono_thread == NULL); + script->reload(); + mono_thread_detach(mono_thread); + + } else // just reload it normally +#endif + script->reload(); + + if (r_error) + *r_error = OK; + + return scriptres; +} + +void ResourceFormatLoaderCSharpScript::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("cs"); +} + +bool ResourceFormatLoaderCSharpScript::handles_type(const String &p_type) const { + + return p_type == "Script" || p_type == CSharpLanguage::get_singleton()->get_type(); +} + +String ResourceFormatLoaderCSharpScript::get_resource_type(const String &p_path) const { + + return p_path.get_extension().to_lower() == "cs" ? CSharpLanguage::get_singleton()->get_type() : ""; +} + +Error ResourceFormatSaverCSharpScript::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { + + Ref<CSharpScript> sqscr = p_resource; + ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER); + + String source = sqscr->get_source_code(); + +#ifdef TOOLS_ENABLED + if (!FileAccess::exists(p_path)) { + // The file does not yet exists, let's assume the user just created this script + + if (_create_project_solution_if_needed()) { + CSharpProject::add_item(GodotSharpDirs::get_project_csproj_path(), + "Compile", + ProjectSettings::get_singleton()->globalize_path(p_path)); + } else { + ERR_PRINTS("Cannot add " + p_path + " to the C# project because it could not be created."); + } + } +#endif + + Error err; + FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); + ERR_FAIL_COND_V(err, err); + + file->store_string(source); + + if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { + memdelete(file); + return ERR_CANT_CREATE; + } + + file->close(); + memdelete(file); + + if (ScriptServer::is_reload_scripts_on_save_enabled()) { + CSharpLanguage::get_singleton()->reload_tool_script(p_resource, false); + } + + return OK; +} + +void ResourceFormatSaverCSharpScript::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { + + if (Object::cast_to<CSharpScript>(p_resource.ptr())) { + p_extensions->push_back("cs"); + } +} + +bool ResourceFormatSaverCSharpScript::recognize(const RES &p_resource) const { + + return Object::cast_to<CSharpScript>(p_resource.ptr()) != NULL; +} + +CSharpLanguage::StringNameCache::StringNameCache() { + + _signal_callback = StaticCString::create("_signal_callback"); + _set = StaticCString::create("_set"); + _get = StaticCString::create("_get"); + _notification = StaticCString::create("_notification"); + dotctor = StaticCString::create(".ctor"); +} diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h new file mode 100644 index 0000000000..65a6450da5 --- /dev/null +++ b/modules/mono/csharp_script.h @@ -0,0 +1,340 @@ +/*************************************************************************/ +/* csharp_script.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 CSHARP_SCRIPT_H +#define CSHARP_SCRIPT_H + +#include "io/resource_loader.h" +#include "io/resource_saver.h" +#include "script_language.h" +#include "self_list.h" + +#include "mono_gc_handle.h" +#include "mono_gd/gd_mono.h" +#include "mono_gd/gd_mono_header.h" +#include "mono_gd/gd_mono_internals.h" + +class CSharpScript; +class CSharpInstance; +class CSharpLanguage; + +#ifdef NO_SAFE_CAST +template <typename TScriptInstance, typename TScriptLanguage> +TScriptInstance *cast_script_instance(ScriptInstance *p_inst) { + return p_inst->get_language() == TScriptLanguage::get_singleton() ? static_cast<TScriptInstance *>(p_inst) : NULL; +} +#else +template <typename TScriptInstance, typename TScriptLanguage> +TScriptInstance *cast_script_instance(ScriptInstance *p_inst) { + return dynamic_cast<TScriptInstance *>(p_inst); +} +#endif + +#define CAST_CSHARP_INSTANCE(m_inst) (cast_script_instance<CSharpInstance, CSharpLanguage>(m_inst)) + +class CSharpScript : public Script { + + GDCLASS(CSharpScript, Script) + + friend class CSharpInstance; + friend class CSharpLanguage; + friend class CSharpScriptDepSort; + + bool tool; + bool valid; + + bool builtin; + + GDMonoClass *base; + GDMonoClass *native; + GDMonoClass *script_class; + + Ref<CSharpScript> base_cache; // TODO what's this for? + + Set<Object *> instances; + + String source; + StringName name; + + SelfList<CSharpScript> script_list; + +#ifdef TOOLS_ENABLED + List<PropertyInfo> exported_members_cache; // members_cache + Map<StringName, Variant> exported_members_defval_cache; // member_default_values_cache + Set<PlaceHolderScriptInstance *> placeholders; + bool source_changed_cache; + bool exports_invalidated; + + void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames); + virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder); +#endif + +#ifdef DEBUG_ENABLED + Map<ObjectID, List<Pair<StringName, Variant> > > pending_reload_state; +#endif + + Map<StringName, PropertyInfo> member_info; + + void _clear(); + + bool _update_exports(); + CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error); + Variant _new(const Variant **p_args, int p_argcount, Variant::CallError &r_error); + + // Do not use unless you know what you are doing + friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *); + static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class); + +protected: + static void _bind_methods(); + + Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error); + virtual void _resource_path_changed(); + +public: + virtual bool can_instance() const; + virtual StringName get_instance_base_type() const; + virtual ScriptInstance *instance_create(Object *p_this); + virtual bool instance_has(const Object *p_this) const; + + virtual bool has_source_code() const; + virtual String get_source_code() const; + virtual void set_source_code(const String &p_code); + + virtual Error reload(bool p_keep_state = false); + + /* TODO */ virtual bool has_script_signal(const StringName &p_signal) const { return false; } + /* TODO */ virtual void get_script_signal_list(List<MethodInfo> *r_signals) const {} + + /* TODO */ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const; + virtual void get_script_property_list(List<PropertyInfo> *p_list) const; + virtual void update_exports(); + + virtual bool is_tool() const { return tool; } + virtual Ref<Script> get_base_script() const; + virtual ScriptLanguage *get_language() const; + + /* TODO */ virtual void get_script_method_list(List<MethodInfo> *p_list) const {} + bool has_method(const StringName &p_method) const; + /* TODO */ MethodInfo get_method_info(const StringName &p_method) const { return MethodInfo(); } + + virtual int get_member_line(const StringName &p_member) const; + + Error load_source_code(const String &p_path); + + StringName get_script_name() const; + + CSharpScript(); + ~CSharpScript(); +}; + +class CSharpInstance : public ScriptInstance { + + friend class CSharpScript; + friend class CSharpLanguage; + Object *owner; + Ref<CSharpScript> script; + Ref<MonoGCHandle> gchandle; + bool base_ref; + bool ref_dying; + + void _ml_call_reversed(GDMonoClass *klass, const StringName &p_method, const Variant **p_args, int p_argcount); + + void _reference_owner_unsafe(); + void _unreference_owner_unsafe(); + + // Do not use unless you know what you are doing + friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *); + static CSharpInstance *create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle); + +public: + MonoObject *get_mono_object() const; + + virtual bool set(const StringName &p_name, const Variant &p_value); + virtual bool get(const StringName &p_name, Variant &r_ret) const; + virtual void get_property_list(List<PropertyInfo> *p_properties) const; + virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const; + + /* TODO */ virtual void get_method_list(List<MethodInfo> *p_list) const {} + virtual bool has_method(const StringName &p_method) const; + virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error); + virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount); + virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount); + + void mono_object_disposed(); + + void refcount_incremented(); + bool refcount_decremented(); + + RPCMode get_rpc_mode(const StringName &p_method) const; + RPCMode get_rset_mode(const StringName &p_variable) const; + + virtual void notification(int p_notification); + + virtual Ref<Script> get_script() const; + + virtual ScriptLanguage *get_language(); + + CSharpInstance(); + ~CSharpInstance(); +}; + +class CSharpLanguage : public ScriptLanguage { + + friend class CSharpScript; + friend class CSharpInstance; + + static CSharpLanguage *singleton; + + GDMono *gdmono; + SelfList<CSharpScript>::List script_list; + + Mutex *lock; + Mutex *script_bind_lock; + + Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > > to_reload; + + Map<Object *, Ref<MonoGCHandle> > gchandle_bindings; + + struct StringNameCache { + + StringName _signal_callback; + StringName _set; + StringName _get; + StringName _notification; + StringName dotctor; // .ctor + + StringNameCache(); + }; + + StringNameCache string_names; + + int lang_idx; + +public: + _FORCE_INLINE_ int get_language_index() { return lang_idx; } + void set_language_index(int p_idx); + + _FORCE_INLINE_ const StringNameCache &get_string_names() { return string_names; } + + _FORCE_INLINE_ static CSharpLanguage *get_singleton() { return singleton; } + + bool debug_break(const String &p_error, bool p_allow_continue = true); + bool debug_break_parse(const String &p_file, int p_line, const String &p_error); + +#ifdef TOOLS_ENABLED + void reload_assemblies_if_needed(bool p_soft_reload); +#endif + + virtual String get_name() const; + + /* LANGUAGE FUNCTIONS */ + virtual String get_type() const; + virtual String get_extension() const; + virtual Error execute_file(const String &p_path); + virtual void init(); + virtual void finish(); + + /* EDITOR FUNCTIONS */ + virtual void get_reserved_words(List<String> *p_words) const; + virtual void get_comment_delimiters(List<String> *p_delimiters) const; + virtual void get_string_delimiters(List<String> *p_delimiters) const; + virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; + /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const { return true; } + virtual Script *create_script() const; + virtual bool has_named_classes() const; + virtual bool supports_builtin_mode() const; + /* TODO? */ virtual int find_function(const String &p_function, const String &p_code) const { return -1; } + virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const; + /* TODO? */ Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, String &r_call_hint) { return ERR_UNAVAILABLE; } + /* TODO? */ virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {} + /* TODO */ virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) {} + + /* DEBUGGER FUNCTIONS */ + /* TODO */ virtual String debug_get_error() const { return ""; } + /* TODO */ virtual int debug_get_stack_level_count() const { return 1; } + /* TODO */ virtual int debug_get_stack_level_line(int p_level) const { return 1; } + /* TODO */ virtual String debug_get_stack_level_function(int p_level) const { return ""; } + /* TODO */ virtual String debug_get_stack_level_source(int p_level) const { return ""; } + /* TODO */ virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {} + /* TODO */ virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {} + /* TODO */ virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {} + /* TODO */ virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) { return ""; } + /* TODO */ virtual Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); } + + /* PROFILING FUNCTIONS */ + /* TODO */ virtual void profiling_start() {} + /* TODO */ virtual void profiling_stop() {} + /* TODO */ virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) { return 0; } + /* TODO */ virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) { return 0; } + + virtual void frame(); + + /* TODO? */ virtual void get_public_functions(List<MethodInfo> *p_functions) const {} + /* TODO? */ virtual void get_public_constants(List<Pair<String, Variant> > *p_constants) const {} + + virtual void reload_all_scripts(); + virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload); + + /* LOADER FUNCTIONS */ + virtual void get_recognized_extensions(List<String> *p_extensions) const; + +#ifdef TOOLS_ENABLED + virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col); + virtual bool overrides_external_editor(); +#endif + + /* THREAD ATTACHING */ + virtual void thread_enter(); + virtual void thread_exit(); + + // Don't use these. I'm watching you + virtual void *alloc_instance_binding_data(Object *p_object); + virtual void free_instance_binding_data(void *p_data); + + CSharpLanguage(); + ~CSharpLanguage(); +}; + +class ResourceFormatLoaderCSharpScript : public ResourceFormatLoader { +public: + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + +class ResourceFormatSaverCSharpScript : public ResourceFormatSaver { +public: + virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); + virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; + virtual bool recognize(const RES &p_resource) const; +}; + +#endif // CSHARP_SCRIPT_H diff --git a/modules/mono/doc_classes/@C#.xml b/modules/mono/doc_classes/@C#.xml new file mode 100644 index 0000000000..487ba9835f --- /dev/null +++ b/modules/mono/doc_classes/@C#.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="@C#" category="Core" version="3.0.alpha.custom_build"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml new file mode 100644 index 0000000000..5f21c9774d --- /dev/null +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="CSharpScript" inherits="Script" category="Core" version="3.0.alpha.custom_build"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="new" qualifiers="vararg"> + <return type="Object"> + </return> + <description> + </description> + </method> + </methods> + <constants> + </constants> +</class> diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml new file mode 100644 index 0000000000..e7e06ddd8f --- /dev/null +++ b/modules/mono/doc_classes/GodotSharp.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="GodotSharp" inherits="Object" category="Core" version="3.0.alpha.custom_build"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="attach_thread"> + <return type="void"> + </return> + <description> + Attaches the current thread to the mono runtime. + </description> + </method> + <method name="detach_thread"> + <return type="void"> + </return> + <description> + Detaches the current thread from the mono runtime. + </description> + </method> + <method name="is_domain_loaded"> + <return type="bool"> + </return> + <description> + Returns whether the scripts domain is loaded. + </description> + </method> + <method name="is_finalizing_domain"> + <return type="bool"> + </return> + <description> + Returns whether the scripts domain is being finalized. + </description> + </method> + </methods> + <constants> + </constants> +</class> diff --git a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs new file mode 100644 index 0000000000..04da0600cc --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs @@ -0,0 +1,369 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; +using Microsoft.Build.Framework; + +namespace GodotSharpTools.Build +{ + public class BuildInstance : IDisposable + { + [MethodImpl(MethodImplOptions.InternalCall)] + private extern static void godot_icall_BuildInstance_ExitCallback(string solution, string config, int exitCode); + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern static void godot_icall_BuildInstance_get_MSBuildInfo(ref string msbuildPath, ref string frameworkPath); + + private struct MSBuildInfo + { + public string path; + public string frameworkPathOverride; + } + + private static MSBuildInfo GetMSBuildInfo() + { + MSBuildInfo msbuildInfo = new MSBuildInfo(); + + godot_icall_BuildInstance_get_MSBuildInfo(ref msbuildInfo.path, ref msbuildInfo.frameworkPathOverride); + + if (msbuildInfo.path == null) + throw new FileNotFoundException("Cannot find the MSBuild executable."); + + return msbuildInfo; + } + + private string solution; + private string config; + + private Process process; + + private int exitCode; + public int ExitCode { get { return exitCode; } } + + public bool IsRunning { get { return process != null && !process.HasExited; } } + + public BuildInstance(string solution, string config) + { + this.solution = solution; + this.config = config; + } + + public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null) + { + MSBuildInfo msbuildInfo = GetMSBuildInfo(); + + List<string> customPropertiesList = new List<string>(); + + if (customProperties != null) + customPropertiesList.AddRange(customProperties); + + if (msbuildInfo.frameworkPathOverride != null) + customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.frameworkPathOverride); + + string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList); + + ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.path, compilerArgs); + + // No console output, thanks + startInfo.RedirectStandardOutput = true; + startInfo.RedirectStandardError = true; + startInfo.UseShellExecute = false; + + // Needed when running from Developer Command Prompt for VS + RemovePlatformVariable(startInfo.EnvironmentVariables); + + using (Process process = new Process()) + { + process.StartInfo = startInfo; + + process.Start(); + + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + process.WaitForExit(); + + exitCode = process.ExitCode; + } + + return true; + } + + public bool BuildAsync(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null) + { + if (process != null) + throw new InvalidOperationException("Already in use"); + + MSBuildInfo msbuildInfo = GetMSBuildInfo(); + + List<string> customPropertiesList = new List<string>(); + + if (customProperties != null) + customPropertiesList.AddRange(customProperties); + + if (msbuildInfo.frameworkPathOverride.Length > 0) + customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.frameworkPathOverride); + + string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList); + + ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.path, compilerArgs); + + // No console output, thanks + startInfo.RedirectStandardOutput = true; + startInfo.RedirectStandardError = true; + startInfo.UseShellExecute = false; + + // Needed when running from Developer Command Prompt for VS + RemovePlatformVariable(startInfo.EnvironmentVariables); + + process = new Process(); + process.StartInfo = startInfo; + process.EnableRaisingEvents = true; + process.Exited += new EventHandler(BuildProcess_Exited); + + process.Start(); + + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + return true; + } + + private string BuildArguments(string loggerAssemblyPath, string loggerOutputDir, List<string> customProperties) + { + string arguments = string.Format(@"""{0}"" /v:normal /t:Build ""/p:{1}"" ""/l:{2},{3};{4}""", + solution, + "Configuration=" + config, + typeof(GodotBuildLogger).FullName, + loggerAssemblyPath, + loggerOutputDir + ); + + foreach (string customProperty in customProperties) + { + arguments += " \"/p:" + customProperty + "\""; + } + + return arguments; + } + + private void RemovePlatformVariable(StringDictionary environmentVariables) + { + // EnvironmentVariables is case sensitive? Seriously? + + List<string> platformEnvironmentVariables = new List<string>(); + + foreach (string env in environmentVariables.Keys) + { + if (env.ToUpper() == "PLATFORM") + platformEnvironmentVariables.Add(env); + } + + foreach (string env in platformEnvironmentVariables) + environmentVariables.Remove(env); + } + + private void BuildProcess_Exited(object sender, System.EventArgs e) + { + exitCode = process.ExitCode; + + godot_icall_BuildInstance_ExitCallback(solution, config, exitCode); + + Dispose(); + } + + public void Dispose() + { + if (process != null) + { + process.Dispose(); + process = null; + } + } + } + + public class GodotBuildLogger : ILogger + { + public string Parameters { get; set; } + public LoggerVerbosity Verbosity { get; set; } + + public void Initialize(IEventSource eventSource) + { + if (null == Parameters) + throw new LoggerException("Log directory was not set."); + + string[] parameters = Parameters.Split(';'); + + string logDir = parameters[0]; + + if (String.IsNullOrEmpty(logDir)) + throw new LoggerException("Log directory was not set."); + + if (parameters.Length > 1) + throw new LoggerException("Too many parameters passed."); + + string logFile = Path.Combine(logDir, "msbuild_log.txt"); + string issuesFile = Path.Combine(logDir, "msbuild_issues.csv"); + + try + { + if (!Directory.Exists(logDir)) + Directory.CreateDirectory(logDir); + + this.logStreamWriter = new StreamWriter(logFile); + this.issuesStreamWriter = new StreamWriter(issuesFile); + } + catch (Exception ex) + { + if + ( + ex is UnauthorizedAccessException + || ex is ArgumentNullException + || ex is PathTooLongException + || ex is DirectoryNotFoundException + || ex is NotSupportedException + || ex is ArgumentException + || ex is SecurityException + || ex is IOException + ) + { + throw new LoggerException("Failed to create log file: " + ex.Message); + } + else + { + // Unexpected failure + throw; + } + } + + eventSource.ProjectStarted += new ProjectStartedEventHandler(eventSource_ProjectStarted); + eventSource.TaskStarted += new TaskStartedEventHandler(eventSource_TaskStarted); + eventSource.MessageRaised += new BuildMessageEventHandler(eventSource_MessageRaised); + eventSource.WarningRaised += new BuildWarningEventHandler(eventSource_WarningRaised); + eventSource.ErrorRaised += new BuildErrorEventHandler(eventSource_ErrorRaised); + eventSource.ProjectFinished += new ProjectFinishedEventHandler(eventSource_ProjectFinished); + } + + void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e) + { + string line = String.Format("{0}({1},{2}): error {3}: {4}", e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message); + + if (e.ProjectFile.Length > 0) + line += string.Format(" [{0}]", e.ProjectFile); + + WriteLine(line); + + string errorLine = String.Format(@"error,{0},{1},{2},{3},{4},{5}", + e.File.CsvEscape(), e.LineNumber, e.ColumnNumber, + e.Code.CsvEscape(), e.Message.CsvEscape(), e.ProjectFile.CsvEscape()); + issuesStreamWriter.WriteLine(errorLine); + } + + void eventSource_WarningRaised(object sender, BuildWarningEventArgs e) + { + string line = String.Format("{0}({1},{2}): warning {3}: {4}", e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message, e.ProjectFile); + + if (e.ProjectFile != null && e.ProjectFile.Length > 0) + line += string.Format(" [{0}]", e.ProjectFile); + + WriteLine(line); + + string warningLine = String.Format(@"warning,{0},{1},{2},{3},{4},{5}", + e.File.CsvEscape(), e.LineNumber, e.ColumnNumber, + e.Code.CsvEscape(), e.Message.CsvEscape(), e.ProjectFile != null ? e.ProjectFile.CsvEscape() : string.Empty); + issuesStreamWriter.WriteLine(warningLine); + } + + void eventSource_MessageRaised(object sender, BuildMessageEventArgs e) + { + // BuildMessageEventArgs adds Importance to BuildEventArgs + // Let's take account of the verbosity setting we've been passed in deciding whether to log the message + if ((e.Importance == MessageImportance.High && IsVerbosityAtLeast(LoggerVerbosity.Minimal)) + || (e.Importance == MessageImportance.Normal && IsVerbosityAtLeast(LoggerVerbosity.Normal)) + || (e.Importance == MessageImportance.Low && IsVerbosityAtLeast(LoggerVerbosity.Detailed)) + ) + { + WriteLineWithSenderAndMessage(String.Empty, e); + } + } + + void eventSource_TaskStarted(object sender, TaskStartedEventArgs e) + { + // TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName + // To keep this log clean, this logger will ignore these events. + } + + void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e) + { + WriteLine(e.Message); + indent++; + } + + void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e) + { + indent--; + WriteLine(e.Message); + } + + /// <summary> + /// Write a line to the log, adding the SenderName + /// </summary> + private void WriteLineWithSender(string line, BuildEventArgs e) + { + if (0 == String.Compare(e.SenderName, "MSBuild", true /*ignore case*/)) + { + // Well, if the sender name is MSBuild, let's leave it out for prettiness + WriteLine(line); + } + else + { + WriteLine(e.SenderName + ": " + line); + } + } + + /// <summary> + /// Write a line to the log, adding the SenderName and Message + /// (these parameters are on all MSBuild event argument objects) + /// </summary> + private void WriteLineWithSenderAndMessage(string line, BuildEventArgs e) + { + if (0 == String.Compare(e.SenderName, "MSBuild", true /*ignore case*/)) + { + // Well, if the sender name is MSBuild, let's leave it out for prettiness + WriteLine(line + e.Message); + } + else + { + WriteLine(e.SenderName + ": " + line + e.Message); + } + } + + private void WriteLine(string line) + { + for (int i = indent; i > 0; i--) + { + logStreamWriter.Write("\t"); + } + logStreamWriter.WriteLine(line); + } + + public void Shutdown() + { + logStreamWriter.Close(); + issuesStreamWriter.Close(); + } + + public bool IsVerbosityAtLeast(LoggerVerbosity checkVerbosity) + { + return this.Verbosity >= checkVerbosity; + } + + private StreamWriter logStreamWriter; + private StreamWriter issuesStreamWriter; + private int indent; + } +} diff --git a/modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs b/modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs new file mode 100644 index 0000000000..303be3b732 --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs @@ -0,0 +1,58 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Diagnostics; + +namespace GodotSharpTools.Editor +{ + public class MonoDevelopInstance + { + private Process process; + private string solutionFile; + + public void Execute(string[] files) + { + bool newWindow = process == null || process.HasExited; + + List<string> args = new List<string>(); + + args.Add("--ipc-tcp"); + + if (newWindow) + args.Add("\"" + Path.GetFullPath(solutionFile) + "\""); + + foreach (var file in files) + { + int semicolonIndex = file.IndexOf(';'); + + string filePath = semicolonIndex < 0 ? file : file.Substring(0, semicolonIndex); + string cursor = semicolonIndex < 0 ? string.Empty : file.Substring(semicolonIndex); + + args.Add("\"" + Path.GetFullPath(filePath.NormalizePath()) + cursor + "\""); + } + + if (newWindow) + { + ProcessStartInfo startInfo = new ProcessStartInfo(MonoDevelopFile, string.Join(" ", args)); + process = Process.Start(startInfo); + } + else + { + Process.Start(MonoDevelopFile, string.Join(" ", args)); + } + } + + public MonoDevelopInstance(string solutionFile) + { + this.solutionFile = solutionFile; + } + + private static string MonoDevelopFile + { + get + { + return "monodevelop"; + } + } + } +} diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj new file mode 100644 index 0000000000..981083a3c2 --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid> + <OutputType>Library</OutputType> + <RootNamespace>GodotSharpTools</RootNamespace> + <AssemblyName>GodotSharpTools</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug</OutputPath> + <DefineConstants>DEBUG;</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>full</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="Microsoft.Build" /> + <Reference Include="Microsoft.Build.Framework" /> + </ItemGroup> + <ItemGroup> + <Compile Include="StringExtensions.cs" /> + <Compile Include="Build\BuildSystem.cs" /> + <Compile Include="Editor\MonoDevelopInstance.cs" /> + <Compile Include="Project\ProjectExtensions.cs" /> + <Compile Include="Project\ProjectGenerator.cs" /> + <Compile Include="Project\ProjectUtils.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> +</Project>
\ No newline at end of file diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.sln b/modules/mono/editor/GodotSharpTools/GodotSharpTools.sln new file mode 100644 index 0000000000..7eabcdff5d --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.sln @@ -0,0 +1,17 @@ +
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpTools", "GodotSharpTools.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.userprefs b/modules/mono/editor/GodotSharpTools/GodotSharpTools.userprefs new file mode 100644 index 0000000000..0cbafdc20d --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.userprefs @@ -0,0 +1,14 @@ +<Properties StartupItem="GodotSharpTools.csproj"> + <MonoDevelop.Ide.Workspace ActiveConfiguration="Debug" /> + <MonoDevelop.Ide.Workbench ActiveDocument="Build/BuildSystem.cs"> + <Files> + <File FileName="Build/ProjectExtensions.cs" Line="1" Column="1" /> + <File FileName="Build/ProjectGenerator.cs" Line="1" Column="1" /> + <File FileName="Build/BuildSystem.cs" Line="37" Column="14" /> + </Files> + </MonoDevelop.Ide.Workbench> + <MonoDevelop.Ide.DebuggingService.Breakpoints> + <BreakpointStore /> + </MonoDevelop.Ide.DebuggingService.Breakpoints> + <MonoDevelop.Ide.DebuggingService.PinnedWatches /> +</Properties>
\ No newline at end of file diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs new file mode 100644 index 0000000000..f00ec5a2ad --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs @@ -0,0 +1,52 @@ +using System; +using Microsoft.Build.Construction; + +namespace GodotSharpTools.Project +{ + public static class ProjectExtensions + { + public static bool HasItem(this ProjectRootElement root, string itemType, string include) + { + string includeNormalized = include.NormalizePath(); + + foreach (var itemGroup in root.ItemGroups) + { + if (itemGroup.Condition.Length != 0) + continue; + + foreach (var item in itemGroup.Items) + { + if (item.ItemType == itemType) + { + if (item.Include.NormalizePath() == includeNormalized) + return true; + } + } + } + + return false; + } + + public static bool AddItemChecked(this ProjectRootElement root, string itemType, string include) + { + if (!root.HasItem(itemType, include)) + { + root.AddItem(itemType, include); + return true; + } + + return false; + } + + public static Guid GetGuid(this ProjectRootElement root) + { + foreach (var property in root.Properties) + { + if (property.Name == "ProjectGuid") + return Guid.Parse(property.Value); + } + + return Guid.Empty; + } + } +} diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs new file mode 100644 index 0000000000..6bf54a0156 --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs @@ -0,0 +1,216 @@ +using System; +using System.IO; +using Microsoft.Build.Construction; + +namespace GodotSharpTools.Project +{ + public static class ProjectGenerator + { + public static string GenCoreApiProject(string dir, string[] compileItems) + { + string path = Path.Combine(dir, CoreApiProject + ".csproj"); + + ProjectPropertyGroupElement mainGroup; + var root = CreateLibraryProject(CoreApiProject, out mainGroup); + + mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml")); + mainGroup.SetProperty("RootNamespace", "Godot"); + + GenAssemblyInfoFile(root, dir, CoreApiProject, + new string[] { "[assembly: InternalsVisibleTo(\"" + EditorApiProject + "\")]" }, + new string[] { "System.Runtime.CompilerServices" }); + + foreach (var item in compileItems) + { + root.AddItem("Compile", item.RelativeToPath(dir).Replace("/", "\\")); + } + + root.Save(path); + + return root.GetGuid().ToString().ToUpper(); + } + + public static string GenEditorApiProject(string dir, string coreApiHintPath, string[] compileItems) + { + string path = Path.Combine(dir, EditorApiProject + ".csproj"); + + ProjectPropertyGroupElement mainGroup; + var root = CreateLibraryProject(EditorApiProject, out mainGroup); + + mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml")); + mainGroup.SetProperty("RootNamespace", "Godot"); + + GenAssemblyInfoFile(root, dir, EditorApiProject); + + foreach (var item in compileItems) + { + root.AddItem("Compile", item.RelativeToPath(dir).Replace("/", "\\")); + } + + var coreApiRef = root.AddItem("Reference", CoreApiProject); + coreApiRef.AddMetadata("HintPath", coreApiHintPath); + coreApiRef.AddMetadata("Private", "False"); + + root.Save(path); + + return root.GetGuid().ToString().ToUpper(); + } + + public static string GenGameProject(string dir, string name, string[] compileItems) + { + string path = Path.Combine(dir, name + ".csproj"); + + ProjectPropertyGroupElement mainGroup; + var root = CreateLibraryProject(name, out mainGroup); + + mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)")); + mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj")); + mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)")); + + var toolsGroup = root.AddPropertyGroup(); + toolsGroup.Condition = " '$(Configuration)|$(Platform)' == 'Tools|AnyCPU' "; + toolsGroup.AddProperty("DebugSymbols", "true"); + toolsGroup.AddProperty("DebugType", "full"); + toolsGroup.AddProperty("Optimize", "false"); + toolsGroup.AddProperty("DefineConstants", "DEBUG;TOOLS;"); + toolsGroup.AddProperty("ErrorReport", "prompt"); + toolsGroup.AddProperty("WarningLevel", "4"); + toolsGroup.AddProperty("ConsolePause", "false"); + + var coreApiRef = root.AddItem("Reference", CoreApiProject); + coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", CoreApiProject + ".dll")); + coreApiRef.AddMetadata("Private", "False"); + + var editorApiRef = root.AddItem("Reference", EditorApiProject); + editorApiRef.Condition = " '$(Configuration)' == 'Tools' "; + editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", EditorApiProject + ".dll")); + editorApiRef.AddMetadata("Private", "False"); + + GenAssemblyInfoFile(root, dir, name); + + foreach (var item in compileItems) + { + root.AddItem("Compile", item.RelativeToPath(dir).Replace("/", "\\")); + } + + root.Save(path); + + return root.GetGuid().ToString().ToUpper(); + } + + public static void GenAssemblyInfoFile(ProjectRootElement root, string dir, string name, string[] assemblyLines = null, string[] usingDirectives = null) + { + + string propertiesDir = Path.Combine(dir, "Properties"); + if (!Directory.Exists(propertiesDir)) + Directory.CreateDirectory(propertiesDir); + + string usingDirectivesText = string.Empty; + + if (usingDirectives != null) + { + foreach (var usingDirective in usingDirectives) + usingDirectivesText += "\nusing " + usingDirective + ";"; + } + + string assemblyLinesText = string.Empty; + + if (assemblyLines != null) + { + foreach (var assemblyLine in assemblyLines) + assemblyLinesText += string.Join("\n", assemblyLines) + "\n"; + } + + string content = string.Format(assemblyInfoTemplate, usingDirectivesText, name, assemblyLinesText); + + string assemblyInfoFile = Path.Combine(propertiesDir, "AssemblyInfo.cs"); + + File.WriteAllText(assemblyInfoFile, content); + + root.AddItem("Compile", assemblyInfoFile.RelativeToPath(dir).Replace("/", "\\")); + } + + public static ProjectRootElement CreateLibraryProject(string name, out ProjectPropertyGroupElement mainGroup) + { + var root = ProjectRootElement.Create(); + root.DefaultTargets = "Build"; + + mainGroup = root.AddPropertyGroup(); + mainGroup.AddProperty("Configuration", "Debug").Condition = " '$(Configuration)' == '' "; + mainGroup.AddProperty("Platform", "AnyCPU").Condition = " '$(Platform)' == '' "; + mainGroup.AddProperty("ProjectGuid", "{" + Guid.NewGuid().ToString().ToUpper() + "}"); + mainGroup.AddProperty("OutputType", "Library"); + mainGroup.AddProperty("OutputPath", Path.Combine("bin", "$(Configuration)")); + mainGroup.AddProperty("RootNamespace", name); + mainGroup.AddProperty("AssemblyName", name); + mainGroup.AddProperty("TargetFrameworkVersion", "v4.5"); + + var debugGroup = root.AddPropertyGroup(); + debugGroup.Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "; + debugGroup.AddProperty("DebugSymbols", "true"); + debugGroup.AddProperty("DebugType", "full"); + debugGroup.AddProperty("Optimize", "false"); + debugGroup.AddProperty("DefineConstants", "DEBUG;"); + debugGroup.AddProperty("ErrorReport", "prompt"); + debugGroup.AddProperty("WarningLevel", "4"); + debugGroup.AddProperty("ConsolePause", "false"); + + var releaseGroup = root.AddPropertyGroup(); + releaseGroup.Condition = " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "; + releaseGroup.AddProperty("DebugType", "full"); + releaseGroup.AddProperty("Optimize", "true"); + releaseGroup.AddProperty("ErrorReport", "prompt"); + releaseGroup.AddProperty("WarningLevel", "4"); + releaseGroup.AddProperty("ConsolePause", "false"); + + // References + var referenceGroup = root.AddItemGroup(); + referenceGroup.AddItem("Reference", "System"); + + root.AddImport(Path.Combine("$(MSBuildBinPath)", "Microsoft.CSharp.targets").Replace("/", "\\")); + + return root; + } + + private static void AddItems(ProjectRootElement elem, string groupName, params string[] items) + { + var group = elem.AddItemGroup(); + + foreach (var item in items) + { + group.AddItem(groupName, item); + } + } + + public const string CoreApiProject = "GodotSharp"; + public const string EditorApiProject = "GodotSharpEditor"; + + private const string assemblyInfoTemplate = +@"using System.Reflection;{0} + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle(""{1}"")] +[assembly: AssemblyDescription("""")] +[assembly: AssemblyConfiguration("""")] +[assembly: AssemblyCompany("""")] +[assembly: AssemblyProduct("""")] +[assembly: AssemblyCopyright("""")] +[assembly: AssemblyTrademark("""")] +[assembly: AssemblyCulture("""")] + +// The assembly version has the format ""{{Major}}.{{Minor}}.{{Build}}.{{Revision}}"". +// The form ""{{Major}}.{{Minor}}.*"" will automatically update the build and revision, +// and ""{{Major}}.{{Minor}}.{{Build}}.*"" will update just the revision. + +[assembly: AssemblyVersion(""1.0.*"")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("""")] +{2}"; + } +} diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs new file mode 100644 index 0000000000..6889ea715f --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs @@ -0,0 +1,17 @@ +using System; +using System.IO; +using Microsoft.Build.Construction; + +namespace GodotSharpTools.Project +{ + public static class ProjectUtils + { + public static void AddItemToProjectChecked(string projectPath, string itemType, string include) + { + var dir = Directory.GetParent(projectPath).FullName; + var root = ProjectRootElement.Open(projectPath); + if (root.AddItemChecked(itemType, include.RelativeToPath(dir).Replace("/", "\\"))) + root.Save(); + } + } +} diff --git a/modules/mono/editor/GodotSharpTools/Properties/AssemblyInfo.cs b/modules/mono/editor/GodotSharpTools/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..7115d8fc71 --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("GodotSharpTools")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("ignacio")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/modules/mono/editor/GodotSharpTools/StringExtensions.cs b/modules/mono/editor/GodotSharpTools/StringExtensions.cs new file mode 100644 index 0000000000..b66c86f8ce --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/StringExtensions.cs @@ -0,0 +1,52 @@ +using System; +using System.IO; + +namespace GodotSharpTools +{ + public static class StringExtensions + { + public static string RelativeToPath(this string path, string dir) + { + // Make sure the directory ends with a path separator + dir = Path.Combine(dir, " ").TrimEnd(); + + if (Path.DirectorySeparatorChar == '\\') + dir = dir.Replace("/", "\\") + "\\"; + + Uri fullPath = new Uri(Path.GetFullPath(path), UriKind.Absolute); + Uri relRoot = new Uri(Path.GetFullPath(dir), UriKind.Absolute); + + return relRoot.MakeRelativeUri(fullPath).ToString(); + } + + public static string NormalizePath(this string path) + { + bool rooted = path.IsAbsolutePath(); + + path = path.Replace('\\', '/'); + + string[] parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + + path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim(); + + return rooted ? Path.DirectorySeparatorChar.ToString() + path : path; + } + + private static readonly string driveRoot = Path.GetPathRoot(Environment.CurrentDirectory); + + public static bool IsAbsolutePath(this string path) + { + return path.StartsWith("/") || path.StartsWith("\\") || path.StartsWith(driveRoot); + } + + public static string CsvEscape(this string value, char delimiter = ',') + { + bool hasSpecialChar = value.IndexOfAny(new char[] { '\"', '\n', '\r', delimiter }) != -1; + + if (hasSpecialChar) + return "\"" + value.Replace("\"", "\"\"") + "\""; + + return value; + } + } +} diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp new file mode 100644 index 0000000000..eb504ec021 --- /dev/null +++ b/modules/mono/editor/bindings_generator.cpp @@ -0,0 +1,2158 @@ +/*************************************************************************/ +/* bindings_generator.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "bindings_generator.h" + +#ifdef DEBUG_METHODS_ENABLED + +#include "global_constants.h" +#include "io/compression.h" +#include "os/dir_access.h" +#include "os/file_access.h" +#include "os/os.h" +#include "project_settings.h" +#include "ucaps.h" + +#include "../glue/cs_compressed.gen.h" +#include "../godotsharp_defs.h" +#include "../mono_gd/gd_mono_marshal.h" +#include "../utils/path_utils.h" +#include "../utils/string_utils.h" +#include "csharp_project.h" +#include "net_solution.h" + +#define CS_INDENT " " + +#define INDENT1 CS_INDENT +#define INDENT2 INDENT1 INDENT1 +#define INDENT3 INDENT2 INDENT1 +#define INDENT4 INDENT3 INDENT1 +#define INDENT5 INDENT4 INDENT1 + +#define MEMBER_BEGIN "\n" INDENT2 + +#define OPEN_BLOCK "{\n" +#define CLOSE_BLOCK "}\n" + +#define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK INDENT3 +#define OPEN_BLOCK_L3 INDENT3 OPEN_BLOCK INDENT4 +#define OPEN_BLOCK_L4 INDENT4 OPEN_BLOCK INDENT5 +#define CLOSE_BLOCK_L2 INDENT2 CLOSE_BLOCK +#define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK +#define CLOSE_BLOCK_L4 INDENT4 CLOSE_BLOCK + +#define LOCAL_RET "ret" + +#define CS_CLASS_NATIVECALLS "NativeCalls" +#define CS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls" +#define CS_FIELD_MEMORYOWN "memoryOwn" +#define CS_PARAM_METHODBIND "method" +#define CS_PARAM_INSTANCE "ptr" +#define CS_SMETHOD_GETINSTANCE "GetPtr" +#define CS_FIELD_SINGLETON "instance" +#define CS_PROP_SINGLETON "Instance" +#define CS_CLASS_SIGNALAWAITER "SignalAwaiter" +#define CS_METHOD_CALL "Call" + +#define GLUE_HEADER_FILE "glue_header.h" +#define ICALL_PREFIX "godot_icall_" +#define SINGLETON_ICALL_SUFFIX "_get_singleton" +#define ICALL_GET_METHODBIND ICALL_PREFIX "ClassDB_get_method" +#define ICALL_CONNECT_SIGNAL_AWAITER ICALL_PREFIX "Object_connect_signal_awaiter" +#define ICALL_OBJECT_DTOR ICALL_PREFIX "Object_Dtor" +#define C_LOCAL_PTRCALL_ARGS "call_args" +#define C_MACRO_OBJECT_CONSTRUCT "GODOTSHARP_INSTANCE_OBJECT" + +#define C_NS_MONOUTILS "GDMonoUtils" +#define C_NS_MONOINTERNALS "GDMonoInternals" +#define C_METHOD_TIE_MANAGED_TO_UNMANAGED C_NS_MONOINTERNALS "::tie_managed_to_unmanaged" +#define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS "::unmanaged_get_managed" + +#define C_NS_MONOMARSHAL "GDMonoMarshal" +#define C_METHOD_MANAGED_TO_VARIANT C_NS_MONOMARSHAL "::mono_object_to_variant" +#define C_METHOD_MANAGED_FROM_VARIANT C_NS_MONOMARSHAL "::variant_to_mono_object" +#define C_METHOD_MONOSTR_TO_GODOT C_NS_MONOMARSHAL "::mono_string_to_godot" +#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot" +#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type +#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array" +#define C_METHOD_MANAGED_TO_DICT C_NS_MONOMARSHAL "::mono_object_to_Dictionary" +#define C_METHOD_MANAGED_FROM_DICT C_NS_MONOMARSHAL "::Dictionary_to_mono_object" + +const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n"; + +bool BindingsGenerator::verbose_output = false; + +static String snake_to_pascal_case(const String &p_identifier) { + + String ret; + Vector<String> parts = p_identifier.split("_", true); + + for (int i = 0; i < parts.size(); i++) { + String part = parts[i]; + + if (part.length()) { + part[0] = _find_upper(part[0]); + ret += part; + } else { + if (i == 0 || i == (parts.size() - 1)) { + // Preserve underscores at the beginning and end + ret += "_"; + } else { + // Preserve contiguous underscores + if (parts[i - 1].length()) { + ret += "__"; + } else { + ret += "_"; + } + } + } + } + + return ret; +} + +static String snake_to_camel_case(const String &p_identifier) { + + String ret; + Vector<String> parts = p_identifier.split("_", true); + + for (int i = 0; i < parts.size(); i++) { + String part = parts[i]; + + if (part.length()) { + if (i != 0) + part[0] = _find_upper(part[0]); + ret += part; + } else { + if (i == 0 || i == (parts.size() - 1)) { + // Preserve underscores at the beginning and end + ret += "_"; + } else { + // Preserve contiguous underscores + if (parts[i - 1].length()) { + ret += "__"; + } else { + ret += "_"; + } + } + } + } + + return ret; +} + +void BindingsGenerator::_generate_header_icalls() { + + core_custom_icalls.clear(); + + core_custom_icalls.push_back(InternalCall(ICALL_GET_METHODBIND, "IntPtr", "string type, string method")); + core_custom_icalls.push_back(InternalCall(ICALL_OBJECT_DTOR, "void", "IntPtr ptr")); + + core_custom_icalls.push_back(InternalCall(ICALL_CONNECT_SIGNAL_AWAITER, "Error", + "IntPtr source, string signal, IntPtr target, " CS_CLASS_SIGNALAWAITER " awaiter")); + + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "NodePath_Ctor", "IntPtr", "string path")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "NodePath_Dtor", "void", "IntPtr ptr")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "NodePath_operator_String", "string", "IntPtr ptr")); + + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "RID_Ctor", "IntPtr", "IntPtr from")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "RID_Dtor", "void", "IntPtr ptr")); + + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "String_md5_buffer", "byte[]", "string str")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "String_md5_text", "string", "string str")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "String_rfind", "int", "string str, string what, int from")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "String_rfindn", "int", "string str, string what, int from")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "String_sha256_buffer", "byte[]", "string str")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "String_sha256_text", "string", "string str")); + + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_bytes2var", "object", "byte[] bytes")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_convert", "object", "object what, int type")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_hash", "int", "object var")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_instance_from_id", "Object", "int instance_id")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_print", "void", "object[] what")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_printerr", "void", "object[] what")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_printraw", "void", "object[] what")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_prints", "void", "object[] what")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_printt", "void", "object[] what")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_seed", "void", "int seed")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_str", "string", "object[] what")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_str2var", "object", "string str")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_type_exists", "bool", "string type")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_var2bytes", "byte[]", "object what")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_var2str", "string", "object var")); + core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_weakref", "WeakRef", "IntPtr obj")); +} + +void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { + + for (const List<MethodInterface>::Element *E = p_itype.methods.front(); E; E = E->next()) { + const MethodInterface &imethod = E->get(); + + if (imethod.is_virtual) + continue; + + const TypeInterface *return_type = _get_type_by_name_or_placeholder(imethod.return_type); + + String im_sig = "IntPtr " CS_PARAM_METHODBIND ", IntPtr " CS_PARAM_INSTANCE; + String im_unique_sig = imethod.return_type + ",IntPtr,IntPtr"; + + // Get arguments information + int i = 0; + for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) { + const TypeInterface *arg_type = _get_type_by_name_or_placeholder(F->get().type); + + im_sig += ", "; + im_sig += arg_type->im_type_in; + im_sig += " arg"; + im_sig += itos(i + 1); + + im_unique_sig += ","; + im_unique_sig += get_unique_sig(*arg_type); + + i++; + } + + // godot_icall_{argc}_{icallcount} + String icall_method = ICALL_PREFIX + itos(imethod.arguments.size()) + "_" + itos(method_icalls.size()); + + InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, return_type->im_type_out, im_sig, im_unique_sig); + + List<InternalCall>::Element *match = method_icalls.find(im_icall); + + if (match) { + if (p_itype.api_type != ClassDB::API_EDITOR) + match->get().editor_only = false; + method_icalls_map.insert(&E->get(), &match->get()); + } else { + List<InternalCall>::Element *added = method_icalls.push_back(im_icall); + method_icalls_map.insert(&E->get(), &added->get()); + } + } +} + +Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bool p_verbose_output) { + + verbose_output = p_verbose_output; + + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); + + if (!DirAccess::exists(p_output_dir)) { + Error err = da->make_dir_recursive(p_output_dir); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + } + + da->change_dir(p_output_dir); + da->make_dir("Core"); + da->make_dir("ObjectType"); + + String core_dir = path_join(p_output_dir, "Core"); + String obj_type_dir = path_join(p_output_dir, "ObjectType"); + + Vector<String> compile_items; + + NETSolution solution(API_ASSEMBLY_NAME); + + if (!solution.set_path(p_output_dir)) + return ERR_FILE_NOT_FOUND; + + for (Map<String, TypeInterface>::Element *E = obj_types.front(); E; E = E->next()) { + const TypeInterface &itype = E->get(); + + if (itype.api_type == ClassDB::API_EDITOR) + continue; + + String output_file = path_join(obj_type_dir, E->get().proxy_name + ".cs"); + Error err = _generate_cs_type(E->get(), output_file); + + if (err == ERR_SKIP) + continue; + + if (err != OK) + return err; + + compile_items.push_back(output_file); + } + +#define GENERATE_BUILTIN_TYPE(m_name) \ + { \ + String output_file = path_join(core_dir, #m_name ".cs"); \ + Error err = _generate_cs_type(builtin_types[#m_name], output_file); \ + if (err != OK) \ + return err; \ + compile_items.push_back(output_file); \ + } + + GENERATE_BUILTIN_TYPE(NodePath); + GENERATE_BUILTIN_TYPE(RID); + +#undef GENERATE_BUILTIN_TYPE + + // Generate source for GlobalConstants + + String constants_source; + int global_constants_count = GlobalConstants::get_global_constant_count(); + + if (global_constants_count > 0) { + Map<String, DocData::ClassDoc>::Element *match = EditorHelp::get_doc_data()->class_list.find("@Global Scope"); + + ERR_EXPLAIN("Could not find `@Global Scope` in DocData"); + ERR_FAIL_COND_V(!match, ERR_BUG); + + const DocData::ClassDoc &global_scope_doc = match->value(); + + for (int i = 0; i < global_constants_count; i++) { + const DocData::ConstantDoc &const_doc = global_scope_doc.constants[i]; + + if (i > 0) + constants_source += MEMBER_BEGIN; + + if (const_doc.description.size()) { + constants_source += "/// <summary>\n"; + + Vector<String> description_lines = const_doc.description.split("\n"); + + for (int i = 0; i < description_lines.size(); i++) { + if (description_lines[i].size()) { + constants_source += INDENT2 "/// "; + constants_source += description_lines[i].strip_edges().xml_escape(); + constants_source += "\n"; + } + } + + constants_source += INDENT2 "/// </summary>" MEMBER_BEGIN; + } + + constants_source += "public const int "; + constants_source += GlobalConstants::get_global_constant_name(i); + constants_source += " = "; + constants_source += itos(GlobalConstants::get_global_constant_value(i)); + constants_source += ";"; + } + } + + // Generate sources from compressed files + + Map<String, CompressedFile> compressed_files; + get_compressed_files(compressed_files); + + for (Map<String, CompressedFile>::Element *E = compressed_files.front(); E; E = E->next()) { + const String &file_name = E->key(); + const CompressedFile &file_data = E->value(); + + String output_file = path_join(core_dir, file_name); + + Vector<uint8_t> data; + data.resize(file_data.uncompressed_size); + Compression::decompress(data.ptr(), file_data.uncompressed_size, file_data.data, file_data.compressed_size, Compression::MODE_DEFLATE); + + if (file_name.get_basename() == BINDINGS_GLOBAL_SCOPE_CLASS) { + // GD.cs must be formatted to include the generated global constants + String data_str = String::utf8(reinterpret_cast<const char *>(data.ptr()), data.size()); + + Dictionary format_keys; + format_keys["GodotGlobalConstants"] = constants_source; + data_str = data_str.format(format_keys, "/*{_}*/"); + + CharString data_utf8 = data_str.utf8(); + data.resize(data_utf8.length()); + copymem(data.ptr(), reinterpret_cast<const uint8_t *>(data_utf8.get_data()), data_utf8.length()); + } + + FileAccessRef file = FileAccess::open(output_file, FileAccess::WRITE); + ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE); + file->store_buffer(data.ptr(), data.size()); + file->close(); + + compile_items.push_back(output_file); + } + + List<String> cs_icalls_content; + + cs_icalls_content.push_back("using System;\n" + "using System.Runtime.CompilerServices;\n" + "using System.Collections.Generic;\n" + "\n"); + cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); + cs_icalls_content.push_back(INDENT1 "internal static class " CS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK); + +#define ADD_INTERNAL_CALL(m_icall) \ + if (!m_icall.editor_only) { \ + cs_icalls_content.push_back(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ + cs_icalls_content.push_back(INDENT2 "internal extern static "); \ + cs_icalls_content.push_back(m_icall.im_type_out + " "); \ + cs_icalls_content.push_back(m_icall.name + "("); \ + cs_icalls_content.push_back(m_icall.im_sig + ");\n"); \ + } + + for (const List<InternalCall>::Element *E = core_custom_icalls.front(); E; E = E->next()) + ADD_INTERNAL_CALL(E->get()); + for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) + ADD_INTERNAL_CALL(E->get()); + +#undef ADD_INTERNAL_CALL + + cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); + + String internal_methods_file = path_join(core_dir, CS_CLASS_NATIVECALLS ".cs"); + + Error err = _save_file(internal_methods_file, cs_icalls_content); + if (err != OK) + return err; + + compile_items.push_back(internal_methods_file); + + String guid = CSharpProject::generate_core_api_project(p_output_dir, compile_items); + + solution.add_new_project(API_ASSEMBLY_NAME, guid); + + Error sln_error = solution.save(); + if (sln_error != OK) { + ERR_PRINT("Could not to save .NET solution."); + return sln_error; + } + + if (verbose_output) + OS::get_singleton()->print("The solution and C# project for the Core API was generated successfully\n"); + + return OK; +} + +Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir, const String &p_core_dll_path, bool p_verbose_output) { + + verbose_output = p_verbose_output; + + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); + + if (!DirAccess::exists(p_output_dir)) { + Error err = da->make_dir_recursive(p_output_dir); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + } + + da->change_dir(p_output_dir); + da->make_dir("Core"); + da->make_dir("ObjectType"); + + String core_dir = path_join(p_output_dir, "Core"); + String obj_type_dir = path_join(p_output_dir, "ObjectType"); + + Vector<String> compile_items; + + NETSolution solution(EDITOR_API_ASSEMBLY_NAME); + + if (!solution.set_path(p_output_dir)) + return ERR_FILE_NOT_FOUND; + + for (Map<String, TypeInterface>::Element *E = obj_types.front(); E; E = E->next()) { + const TypeInterface &itype = E->get(); + + if (itype.api_type != ClassDB::API_EDITOR) + continue; + + String output_file = path_join(obj_type_dir, E->get().proxy_name + ".cs"); + Error err = _generate_cs_type(E->get(), output_file); + + if (err == ERR_SKIP) + continue; + + if (err != OK) + return err; + + compile_items.push_back(output_file); + } + + List<String> cs_icalls_content; + + cs_icalls_content.push_back("using System;\n" + "using System.Runtime.CompilerServices;\n" + "using System.Collections.Generic;\n" + "\n"); + cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); + cs_icalls_content.push_back(INDENT1 "internal static class " CS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK); + +#define ADD_INTERNAL_CALL(m_icall) \ + if (m_icall.editor_only) { \ + cs_icalls_content.push_back(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ + cs_icalls_content.push_back(INDENT2 "internal extern static "); \ + cs_icalls_content.push_back(m_icall.im_type_out + " "); \ + cs_icalls_content.push_back(m_icall.name + "("); \ + cs_icalls_content.push_back(m_icall.im_sig + ");\n"); \ + } + + for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next()) + ADD_INTERNAL_CALL(E->get()); + for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) + ADD_INTERNAL_CALL(E->get()); + +#undef ADD_INTERNAL_CALL + + cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); + + String internal_methods_file = path_join(core_dir, CS_CLASS_NATIVECALLS_EDITOR ".cs"); + + Error err = _save_file(internal_methods_file, cs_icalls_content); + if (err != OK) + return err; + + compile_items.push_back(internal_methods_file); + + String guid = CSharpProject::generate_editor_api_project(p_output_dir, p_core_dll_path, compile_items); + + solution.add_new_project(EDITOR_API_ASSEMBLY_NAME, guid); + + Error sln_error = solution.save(); + if (sln_error != OK) { + ERR_PRINT("Could not to save .NET solution."); + return sln_error; + } + + if (verbose_output) + OS::get_singleton()->print("The solution and C# project for the Editor API was generated successfully\n"); + + return OK; +} + +// TODO: there are constants that hide inherited members. must explicitly use `new` to avoid warnings +// e.g.: warning CS0108: 'SpriteBase3D.FLAG_MAX' hides inherited member 'GeometryInstance.FLAG_MAX'. Use the new keyword if hiding was intended. +Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const String &p_output_file) { + + bool is_derived_type = itype.base_name.length(); + + List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls; + + if (verbose_output) + OS::get_singleton()->print(String("Generating " + itype.proxy_name + ".cs...\n").utf8()); + + String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); + + List<String> output; + + output.push_back("using System;\n"); // IntPtr + + if (itype.requires_collections) + output.push_back("using System.Collections.Generic;\n"); // Dictionary + + output.push_back("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); + + const DocData::ClassDoc *class_doc = itype.class_doc; + + if (class_doc && class_doc->description.size()) { + output.push_back(INDENT1 "/// <summary>\n"); + + Vector<String> description_lines = class_doc->description.split("\n"); + + for (int i = 0; i < description_lines.size(); i++) { + if (description_lines[i].size()) { + output.push_back(INDENT1 "/// "); + output.push_back(description_lines[i].strip_edges().xml_escape()); + output.push_back("\n"); + } + } + + output.push_back(INDENT1 "/// </summary>\n"); + } + + output.push_back(INDENT1 "public "); + output.push_back(itype.is_singleton ? "static class " : "class "); + output.push_back(itype.proxy_name); + + if (itype.is_singleton || !itype.is_object_type) { + output.push_back("\n"); + } else if (!is_derived_type) { + output.push_back(" : IDisposable\n"); + } else if (obj_types.has(itype.base_name)) { + output.push_back(" : "); + output.push_back(obj_types[itype.base_name].proxy_name); + output.push_back("\n"); + } else { + ERR_PRINTS("Base type '" + itype.base_name + "' does not exist, for class " + itype.name); + return ERR_INVALID_DATA; + } + + output.push_back(INDENT1 "{"); + + if (class_doc) { + + // Add constants + + for (int i = 0; i < class_doc->constants.size(); i++) { + const DocData::ConstantDoc &const_doc = class_doc->constants[i]; + + if (const_doc.description.size()) { + output.push_back(MEMBER_BEGIN "/// <summary>\n"); + + Vector<String> description_lines = const_doc.description.split("\n"); + + for (int i = 0; i < description_lines.size(); i++) { + if (description_lines[i].size()) { + output.push_back(INDENT2 "/// "); + output.push_back(description_lines[i].strip_edges().xml_escape()); + output.push_back("\n"); + } + } + + output.push_back(INDENT2 "/// </summary>"); + } + + output.push_back(MEMBER_BEGIN "public const int "); + output.push_back(const_doc.name); + output.push_back(" = "); + output.push_back(const_doc.value); + output.push_back(";"); + } + + if (class_doc->constants.size()) + output.push_back("\n"); + + // Add properties + + const Vector<DocData::PropertyDoc> &properties = class_doc->properties; + + for (int i = 0; i < properties.size(); i++) { + const DocData::PropertyDoc &prop_doc = properties[i]; + Error prop_err = _generate_cs_property(itype, prop_doc, output); + if (prop_err != OK) { + ERR_EXPLAIN("Failed to generate property '" + prop_doc.name + "' for class '" + itype.name + "'"); + ERR_FAIL_V(prop_err); + } + } + + if (class_doc->properties.size()) + output.push_back("\n"); + } + + if (!itype.is_object_type) { + output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"" + itype.name + "\";\n"); + output.push_back(MEMBER_BEGIN "private bool disposed = false;\n"); + output.push_back(MEMBER_BEGIN "internal IntPtr " BINDINGS_PTR_FIELD ";\n"); + + output.push_back(MEMBER_BEGIN "internal static IntPtr " CS_SMETHOD_GETINSTANCE "("); + output.push_back(itype.proxy_name); + output.push_back(" instance)\n" OPEN_BLOCK_L2 "return instance == null ? IntPtr.Zero : instance." BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2); + + // Add Destructor + output.push_back(MEMBER_BEGIN "~"); + output.push_back(itype.proxy_name); + output.push_back("()\n" OPEN_BLOCK_L2 "Dispose(false);\n" CLOSE_BLOCK_L2); + + // Add the Dispose from IDisposable + output.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2); + + // Add the virtual Dispose + output.push_back(MEMBER_BEGIN "public virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2 + "if (disposed) return;\n" INDENT3 + "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3 "NativeCalls.godot_icall_"); + output.push_back(itype.proxy_name); + output.push_back("_Dtor(" BINDINGS_PTR_FIELD ");\n" INDENT5 BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" CLOSE_BLOCK_L3 INDENT3 + "GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2); + + output.push_back(MEMBER_BEGIN "internal "); + output.push_back(itype.proxy_name); + output.push_back("(IntPtr " BINDINGS_PTR_FIELD ")\n" OPEN_BLOCK_L2 "this." BINDINGS_PTR_FIELD " = " BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2); + + output.push_back(MEMBER_BEGIN "public IntPtr NativeInstance\n" OPEN_BLOCK_L2 + "get { return " BINDINGS_PTR_FIELD "; }\n" CLOSE_BLOCK_L2); + } else if (itype.is_singleton) { + // Add the type name and the singleton pointer as static fields + + output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \""); + output.push_back(itype.name); + output.push_back("\";\n"); + + output.push_back(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = "); + output.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS); + output.push_back("." ICALL_PREFIX); + output.push_back(itype.name); + output.push_back(SINGLETON_ICALL_SUFFIX "();\n"); + } else { + // Add member fields + + output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \""); + output.push_back(itype.name); + output.push_back("\";\n"); + + // Only the base class stores the pointer to the native object + // This pointer is expected to be and must be of type Object* + if (!is_derived_type) { + output.push_back(MEMBER_BEGIN "private bool disposed = false;\n"); + output.push_back(INDENT2 "internal IntPtr " BINDINGS_PTR_FIELD ";\n"); + output.push_back(INDENT2 "internal bool " CS_FIELD_MEMORYOWN ";\n"); + } + + // Add default constructor + if (itype.is_instantiable) { + output.push_back(MEMBER_BEGIN "public "); + output.push_back(itype.proxy_name); + output.push_back("() : this("); + output.push_back(itype.memory_own ? "true" : "false"); + + // The default constructor may also be called by the engine when instancing existing native objects + // The engine will initialize the pointer field of the managed side before calling the constructor + // This is why we only allocate a new native object from the constructor if the pointer field is not set + output.push_back(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = "); + output.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS); + output.push_back("." + ctor_method); + output.push_back("(this);\n" CLOSE_BLOCK_L2); + } else { + // Hide the constructor + output.push_back(MEMBER_BEGIN "internal "); + output.push_back(itype.proxy_name); + output.push_back("() {}\n"); + } + + // Add.. em.. trick constructor. Sort of. + output.push_back(MEMBER_BEGIN "internal "); + output.push_back(itype.proxy_name); + if (is_derived_type) { + output.push_back("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n"); + } else { + output.push_back("(bool " CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L2 + "this." CS_FIELD_MEMORYOWN " = " CS_FIELD_MEMORYOWN ";\n" CLOSE_BLOCK_L2); + } + + // Add methods + + if (!is_derived_type) { + output.push_back(MEMBER_BEGIN "public IntPtr NativeInstance\n" OPEN_BLOCK_L2 + "get { return " BINDINGS_PTR_FIELD "; }\n" CLOSE_BLOCK_L2); + + output.push_back(MEMBER_BEGIN "internal static IntPtr " CS_SMETHOD_GETINSTANCE "(Object instance)\n" OPEN_BLOCK_L2 + "return instance == null ? IntPtr.Zero : instance." BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2); + } + + if (!is_derived_type) { + // Add destructor + output.push_back(MEMBER_BEGIN "~"); + output.push_back(itype.proxy_name); + output.push_back("()\n" OPEN_BLOCK_L2 "Dispose(false);\n" CLOSE_BLOCK_L2); + + // Add the Dispose from IDisposable + output.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2); + + // Add the virtual Dispose + output.push_back(MEMBER_BEGIN "public virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2 + "if (disposed) return;\n" INDENT3 + "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3 + "if (" CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L4 CS_FIELD_MEMORYOWN + " = false;\n" INDENT5 CS_CLASS_NATIVECALLS "." ICALL_OBJECT_DTOR + "(" BINDINGS_PTR_FIELD ");\n" INDENT5 BINDINGS_PTR_FIELD + " = IntPtr.Zero;\n" CLOSE_BLOCK_L4 CLOSE_BLOCK_L3 INDENT3 + "GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2); + + Map<String, TypeInterface>::Element *array_itype = builtin_types.find("Array"); + + if (!array_itype) { + ERR_PRINT("BUG: Array type interface not found!"); + return ERR_BUG; + } + + Map<String, TypeInterface>::Element *object_itype = obj_types.find("Object"); + + if (!object_itype) { + ERR_PRINT("BUG: Object type interface not found!"); + return ERR_BUG; + } + + output.push_back(MEMBER_BEGIN "public " CS_CLASS_SIGNALAWAITER " ToSignal("); + output.push_back(object_itype->get().cs_type); + output.push_back(" source, string signal)\n" OPEN_BLOCK_L2 + "return new " CS_CLASS_SIGNALAWAITER "(source, signal, this);\n" CLOSE_BLOCK_L2); + } + } + + Map<String, String>::Element *extra_member = extra_members.find(itype.name); + if (extra_member) + output.push_back(extra_member->get()); + + int method_bind_count = 0; + for (const List<MethodInterface>::Element *E = itype.methods.front(); E; E = E->next()) { + const MethodInterface &imethod = E->get(); + Error method_err = _generate_cs_method(itype, imethod, method_bind_count, output); + if (method_err != OK) { + ERR_EXPLAIN("Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'"); + ERR_FAIL_V(method_err); + } + } + + if (itype.is_singleton) { + InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr"); + + if (!find_icall_by_name(singleton_icall.name, custom_icalls)) + custom_icalls.push_back(singleton_icall); + } + + if (itype.is_instantiable) { + InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj"); + + if (!find_icall_by_name(ctor_icall.name, custom_icalls)) + custom_icalls.push_back(ctor_icall); + } + + output.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); + + return _save_file(p_output_file, output); +} + +Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInterface &p_itype, const DocData::PropertyDoc &p_prop_doc, List<String> &p_output) { + + const MethodInterface *setter = p_itype.find_method_by_name(p_prop_doc.setter); + + // Search it in base types too + const TypeInterface *current_type = &p_itype; + while (!setter && current_type->base_name.length()) { + Map<String, TypeInterface>::Element *base_match = obj_types.find(current_type->base_name); + ERR_FAIL_NULL_V(base_match, ERR_BUG); + current_type = &base_match->get(); + setter = current_type->find_method_by_name(p_prop_doc.setter); + } + + const MethodInterface *getter = p_itype.find_method_by_name(p_prop_doc.getter); + + // Search it in base types too + current_type = &p_itype; + while (!getter && current_type->base_name.length()) { + Map<String, TypeInterface>::Element *base_match = obj_types.find(current_type->base_name); + ERR_FAIL_NULL_V(base_match, ERR_BUG); + current_type = &base_match->get(); + getter = current_type->find_method_by_name(p_prop_doc.getter); + } + + ERR_FAIL_COND_V(!setter && !getter, ERR_BUG); + + bool is_valid = false; + int prop_index = ClassDB::get_property_index(p_itype.name, p_prop_doc.name, &is_valid); + ERR_FAIL_COND_V(!is_valid, ERR_BUG); + + if (setter) { + int setter_argc = prop_index != -1 ? 2 : 1; + ERR_FAIL_COND_V(setter->arguments.size() != setter_argc, ERR_BUG); + } + + if (getter) { + int getter_argc = prop_index != -1 ? 1 : 0; + ERR_FAIL_COND_V(getter->arguments.size() != getter_argc, ERR_BUG); + } + + if (getter && setter) { + ERR_FAIL_COND_V(getter->return_type != setter->arguments.back()->get().type, ERR_BUG); + } + + // Let's not trust PropertyDoc::type + String proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type; + + const TypeInterface *prop_itype = _get_type_by_name_or_null(proptype_name); + if (!prop_itype) { + // Try with underscore prefix + prop_itype = _get_type_by_name_or_null("_" + proptype_name); + } + + ERR_FAIL_NULL_V(prop_itype, ERR_BUG); + + String prop_proxy_name = escape_csharp_keyword(snake_to_pascal_case(p_prop_doc.name)); + + // Prevent property and enclosing type from sharing the same name + if (prop_proxy_name == p_itype.proxy_name) { + if (verbose_output) { + WARN_PRINTS("Name of property `" + prop_proxy_name + "` is ambiguous with the name of its class `" + + p_itype.proxy_name + "`. Renaming property to `" + prop_proxy_name + "_`"); + } + + prop_proxy_name += "_"; + } + + if (p_prop_doc.description.size()) { + p_output.push_back(MEMBER_BEGIN "/// <summary>\n"); + + Vector<String> description_lines = p_prop_doc.description.split("\n"); + + for (int i = 0; i < description_lines.size(); i++) { + if (description_lines[i].size()) { + p_output.push_back(INDENT2 "/// "); + p_output.push_back(description_lines[i].strip_edges().xml_escape()); + p_output.push_back("\n"); + } + } + + p_output.push_back(INDENT2 "/// </summary>"); + } + + p_output.push_back(MEMBER_BEGIN "public "); + + if (p_itype.is_singleton) + p_output.push_back("static "); + + p_output.push_back(prop_itype->cs_type); + p_output.push_back(" "); + p_output.push_back(prop_proxy_name.replace("/", "__")); + p_output.push_back("\n" INDENT2 OPEN_BLOCK); + + if (getter) { + p_output.push_back(INDENT3 "get\n" OPEN_BLOCK_L3); + p_output.push_back("return "); + p_output.push_back(getter->proxy_name + "("); + if (prop_index != -1) + p_output.push_back(itos(prop_index)); + p_output.push_back(");\n" CLOSE_BLOCK_L3); + } + + if (setter) { + p_output.push_back(INDENT3 "set\n" OPEN_BLOCK_L3); + p_output.push_back(setter->proxy_name + "("); + if (prop_index != -1) + p_output.push_back(itos(prop_index) + ", "); + p_output.push_back("value);\n" CLOSE_BLOCK_L3); + } + + p_output.push_back(CLOSE_BLOCK_L2); + + return OK; +} + +Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output) { + + const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type); + + String method_bind_field = "method_bind_" + itos(p_method_bind_count); + + String icall_params = method_bind_field + ", " + sformat(p_itype.cs_in, "this"); + String arguments_sig; + String cs_in_statements; + + List<String> default_args_doc; + + // Retrieve information from the arguments + for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) { + const ArgumentInterface &iarg = F->get(); + const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type); + + // Add the current arguments to the signature + // If the argument has a default value which is not a constant, we will make it Nullable + { + if (F != p_imethod.arguments.front()) + arguments_sig += ", "; + + if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) + arguments_sig += "Nullable<"; + + arguments_sig += arg_type->cs_type; + + if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) + arguments_sig += "> "; + else + arguments_sig += " "; + + arguments_sig += iarg.name; + + if (iarg.default_argument.size()) { + if (iarg.def_param_mode != ArgumentInterface::CONSTANT) + arguments_sig += " = null"; + else + arguments_sig += " = " + sformat(iarg.default_argument, arg_type->cs_type); + } + } + + icall_params += ", "; + + if (iarg.default_argument.size() && iarg.def_param_mode != ArgumentInterface::CONSTANT) { + // The default value of an argument must be constant. Otherwise we make it Nullable and do the following: + // Type arg_in = arg.HasValue ? arg.Value : <non-const default value>; + String arg_in = iarg.name; + arg_in += "_in"; + + cs_in_statements += arg_type->cs_type; + cs_in_statements += " "; + cs_in_statements += arg_in; + cs_in_statements += " = "; + cs_in_statements += iarg.name; + + if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) + cs_in_statements += ".HasValue ? "; + else + cs_in_statements += " != null ? "; + + cs_in_statements += iarg.name; + + if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) + cs_in_statements += ".Value : "; + else + cs_in_statements += " : "; + + String def_arg = sformat(iarg.default_argument, arg_type->cs_type); + + cs_in_statements += def_arg; + cs_in_statements += ";\n" INDENT3; + + icall_params += arg_type->cs_in.empty() ? arg_in : sformat(arg_type->cs_in, arg_in); + + default_args_doc.push_back(INDENT2 "/// <param name=\"" + iarg.name + "\">If the param is null, then the default value is " + def_arg + "</param>\n"); + } else { + icall_params += arg_type->cs_in.empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name); + } + } + + // Generate method + { + if (!p_imethod.is_virtual && !p_imethod.requires_object_call) { + p_output.push_back(MEMBER_BEGIN "private "); + p_output.push_back(p_itype.is_singleton ? "static IntPtr " : "IntPtr "); + p_output.push_back(method_bind_field + " = " CS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); + p_output.push_back(p_imethod.name); + p_output.push_back("\");\n"); + } + + if (p_imethod.method_doc && p_imethod.method_doc->description.size()) { + p_output.push_back(MEMBER_BEGIN "/// <summary>\n"); + + Vector<String> description_lines = p_imethod.method_doc->description.split("\n"); + + for (int i = 0; i < description_lines.size(); i++) { + if (description_lines[i].size()) { + p_output.push_back(INDENT2 "/// "); + p_output.push_back(description_lines[i].strip_edges().xml_escape()); + p_output.push_back("\n"); + } + } + + for (List<String>::Element *E = default_args_doc.front(); E; E = E->next()) { + p_output.push_back(E->get().xml_escape()); + } + + p_output.push_back(INDENT2 "/// </summary>"); + } + + if (!p_imethod.is_internal) { + p_output.push_back(MEMBER_BEGIN "[GodotMethod(\""); + p_output.push_back(p_imethod.name); + p_output.push_back("\")]"); + } + + p_output.push_back(MEMBER_BEGIN); + p_output.push_back(p_imethod.is_internal ? "internal " : "public "); + + if (p_itype.is_singleton) { + p_output.push_back("static "); + } else if (p_imethod.is_virtual) { + p_output.push_back("virtual "); + } + + p_output.push_back(return_type->cs_type + " "); + p_output.push_back(p_imethod.proxy_name + "("); + p_output.push_back(arguments_sig + ")\n" OPEN_BLOCK_L2); + + if (p_imethod.is_virtual) { + // Godot virtual method must be overridden, therefore we return a default value by default. + + if (return_type->name == "void") { + p_output.push_back("return;\n" CLOSE_BLOCK_L2); + } else { + p_output.push_back("return default("); + p_output.push_back(return_type->cs_type); + p_output.push_back(");\n" CLOSE_BLOCK_L2); + } + + return OK; // Won't increment method bind count + } + + if (p_imethod.requires_object_call) { + // Fallback to Godot's object.Call(string, params) + + p_output.push_back(CS_METHOD_CALL "(\""); + p_output.push_back(p_imethod.name); + p_output.push_back("\""); + + for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) { + p_output.push_back(", "); + p_output.push_back(F->get().name); + } + + p_output.push_back(");\n" CLOSE_BLOCK_L2); + + return OK; // Won't increment method bind count + } + + const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod); + ERR_FAIL_NULL_V(match, ERR_BUG); + + const InternalCall *im_icall = match->value(); + + String im_call = im_icall->editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS; + im_call += "." + im_icall->name + "(" + icall_params + ");\n"; + + if (p_imethod.arguments.size()) + p_output.push_back(cs_in_statements); + + if (return_type->name == "void") { + p_output.push_back(im_call); + } else if (return_type->cs_out.empty()) { + p_output.push_back("return " + im_call); + } else { + p_output.push_back(return_type->im_type_out); + p_output.push_back(" " LOCAL_RET " = "); + p_output.push_back(im_call); + p_output.push_back(INDENT3); + p_output.push_back(sformat(return_type->cs_out, LOCAL_RET) + "\n"); + } + + p_output.push_back(CLOSE_BLOCK_L2); + } + + p_method_bind_count++; + return OK; +} + +Error BindingsGenerator::generate_glue(const String &p_output_dir) { + + verbose_output = true; + + bool dir_exists = DirAccess::exists(p_output_dir); + ERR_EXPLAIN("The output directory does not exist."); + ERR_FAIL_COND_V(!dir_exists, ERR_FILE_BAD_PATH); + + List<String> output; + + output.push_back("#include \"" GLUE_HEADER_FILE "\"\n" + "\n"); + + generated_icall_funcs.clear(); + + for (Map<String, TypeInterface>::Element *type_elem = obj_types.front(); type_elem; type_elem = type_elem->next()) { + const TypeInterface &itype = type_elem->get(); + + List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls; + + OS::get_singleton()->print(String("Generating " + itype.name + "...\n").utf8()); + + String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); + + for (const List<MethodInterface>::Element *E = itype.methods.front(); E; E = E->next()) { + const MethodInterface &imethod = E->get(); + Error method_err = _generate_glue_method(itype, imethod, output); + if (method_err != OK) { + ERR_EXPLAIN("Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'"); + ERR_FAIL_V(method_err); + } + } + + if (itype.is_singleton) { + String singleton_icall_name = ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX; + InternalCall singleton_icall = InternalCall(itype.api_type, singleton_icall_name, "IntPtr"); + + if (!find_icall_by_name(singleton_icall.name, custom_icalls)) + custom_icalls.push_back(singleton_icall); + + output.push_back("Object* "); + output.push_back(singleton_icall_name); + output.push_back("() " OPEN_BLOCK "\treturn ProjectSettings::get_singleton()->get_singleton_object(\""); + output.push_back(itype.proxy_name); + output.push_back("\");\n" CLOSE_BLOCK "\n"); + } + + if (itype.is_instantiable) { + InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj"); + + if (!find_icall_by_name(ctor_icall.name, custom_icalls)) + custom_icalls.push_back(ctor_icall); + + output.push_back("Object* "); + output.push_back(ctor_method); + output.push_back("(MonoObject* obj) " OPEN_BLOCK + "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \""); + output.push_back(itype.name); + output.push_back("\");\n" + "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n" + "\treturn instance;\n" CLOSE_BLOCK "\n"); + } + } + + output.push_back("namespace GodotSharpBindings\n" OPEN_BLOCK); + output.push_back("uint64_t get_core_api_hash() { return "); + output.push_back(itos(GDMono::get_singleton()->get_api_core_hash()) + "; }\n"); + output.push_back("#ifdef TOOLS_ENABLED\n" + "uint64_t get_editor_api_hash() { return "); + output.push_back(itos(GDMono::get_singleton()->get_api_editor_hash()) + + "; }\n#endif // TOOLS_ENABLED\n"); + output.push_back("void register_generated_icalls() " OPEN_BLOCK); + +#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \ + { \ + output.push_back("\tmono_add_internal_call("); \ + output.push_back("\"" BINDINGS_NAMESPACE "."); \ + output.push_back(m_icall.editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS); \ + output.push_back("::"); \ + output.push_back(m_icall.name); \ + output.push_back("\", (void*)"); \ + output.push_back(m_icall.name); \ + output.push_back(");\n"); \ + } + + bool tools_sequence = false; + for (const List<InternalCall>::Element *E = core_custom_icalls.front(); E; E = E->next()) { + + if (tools_sequence) { + if (!E->get().editor_only) { + tools_sequence = false; + output.push_back("#endif\n"); + } + } else { + if (E->get().editor_only) { + output.push_back("#ifdef TOOLS_ENABLED\n"); + tools_sequence = true; + } + } + + ADD_INTERNAL_CALL_REGISTRATION(E->get()); + } + + if (tools_sequence) { + tools_sequence = false; + output.push_back("#endif\n"); + } + + output.push_back("#ifdef TOOLS_ENABLED\n"); + for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next()) + ADD_INTERNAL_CALL_REGISTRATION(E->get()); + output.push_back("#endif // TOOLS_ENABLED\n"); + + for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) { + if (tools_sequence) { + if (!E->get().editor_only) { + tools_sequence = false; + output.push_back("#endif\n"); + } + } else { + if (E->get().editor_only) { + output.push_back("#ifdef TOOLS_ENABLED\n"); + tools_sequence = true; + } + } + + ADD_INTERNAL_CALL_REGISTRATION(E->get()); + } + + if (tools_sequence) { + tools_sequence = false; + output.push_back("#endif\n"); + } + +#undef ADD_INTERNAL_CALL_REGISTRATION + + output.push_back(CLOSE_BLOCK "}\n"); + + Error save_err = _save_file(path_join(p_output_dir, "mono_glue.gen.cpp"), output); + if (save_err != OK) + return save_err; + + OS::get_singleton()->print("Mono glue generated successfully\n"); + + return OK; +} + +Error BindingsGenerator::_save_file(const String &p_path, const List<String> &p_content) { + + FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE); + + ERR_EXPLAIN("Cannot open file: " + p_path); + ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE); + + for (const List<String>::Element *E = p_content.front(); E; E = E->next()) { + file->store_string(E->get()); + } + + file->close(); + + return OK; +} + +Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, List<String> &p_output) { + + if (p_imethod.is_virtual) + return OK; // Ignore + + bool ret_void = p_imethod.return_type == "void"; + + const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type); + + String argc_str = itos(p_imethod.arguments.size()); + + String c_func_sig = "MethodBind* " CS_PARAM_METHODBIND ", " + p_itype.c_type_in + " " CS_PARAM_INSTANCE; + String c_in_statements; + String c_args_var_content; + + // Get arguments information + int i = 0; + for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) { + const ArgumentInterface &iarg = F->get(); + const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type); + + String c_param_name = "arg" + itos(i + 1); + + if (p_imethod.is_vararg) { + if (i < p_imethod.arguments.size() - 1) { + c_in_statements += sformat(arg_type->c_in.size() ? arg_type->c_in : TypeInterface::DEFAULT_VARARG_C_IN, "Variant", c_param_name); + c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(0, "; + c_in_statements += sformat("&%s_in", c_param_name); + c_in_statements += ");\n"; + } + } else { + if (i > 0) + c_args_var_content += ", "; + if (arg_type->c_in.size()) + c_in_statements += sformat(arg_type->c_in, arg_type->c_type, c_param_name); + c_args_var_content += sformat(arg_type->c_arg_in, c_param_name); + } + + c_func_sig += ", "; + c_func_sig += arg_type->c_type_in; + c_func_sig += " "; + c_func_sig += c_param_name; + + i++; + } + + const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod); + ERR_FAIL_NULL_V(match, ERR_BUG); + + const InternalCall *im_icall = match->value(); + String icall_method = im_icall->name; + + if (!generated_icall_funcs.find(im_icall)) { + generated_icall_funcs.push_back(im_icall); + + if (im_icall->editor_only) + p_output.push_back("#ifdef TOOLS_ENABLED\n"); + + // Generate icall function + + p_output.push_back(ret_void ? "void " : return_type->c_type_out + " "); + p_output.push_back(icall_method); + p_output.push_back("("); + p_output.push_back(c_func_sig); + p_output.push_back(") " OPEN_BLOCK); + + String fail_ret = ret_void ? "" : ", " + (return_type->c_type_out.ends_with("*") ? "NULL" : return_type->c_type_out + "()"); + + if (!ret_void) { + String ptrcall_return_type; + String initialization; + + if (return_type->is_object_type) { + ptrcall_return_type = return_type->is_reference ? "Ref<Reference>" : return_type->c_type; + initialization = return_type->is_reference ? "" : " = NULL"; + } else { + ptrcall_return_type = return_type->c_type; + } + + p_output.push_back("\t" + ptrcall_return_type); + p_output.push_back(" " LOCAL_RET); + p_output.push_back(initialization + ";\n"); + p_output.push_back("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE); + p_output.push_back(fail_ret); + p_output.push_back(");\n"); + } else { + p_output.push_back("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n"); + } + + if (p_imethod.arguments.size()) { + if (p_imethod.is_vararg) { + String err_fail_macro = ret_void ? "ERR_FAIL_COND" : "ERR_FAIL_COND_V"; + String vararg_arg = "arg" + argc_str; + String real_argc_str = itos(p_imethod.arguments.size() - 1); // Arguments count without vararg + + p_output.push_back("\tVector<Variant> varargs;\n" + "\tint vararg_length = mono_array_length("); + p_output.push_back(vararg_arg); + p_output.push_back(");\n\tint total_length = "); + p_output.push_back(real_argc_str); + p_output.push_back(" + vararg_length;\n\t"); + p_output.push_back(err_fail_macro); + p_output.push_back("(varargs.resize(vararg_length) != OK"); + p_output.push_back(fail_ret); + p_output.push_back(");\n\tVector<Variant*> " C_LOCAL_PTRCALL_ARGS ";\n\t"); + p_output.push_back(err_fail_macro); + p_output.push_back("(call_args.resize(total_length) != OK"); + p_output.push_back(fail_ret); + p_output.push_back(");\n"); + p_output.push_back(c_in_statements); + p_output.push_back("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK + "\t\tMonoObject* elem = mono_array_get("); + p_output.push_back(vararg_arg); + p_output.push_back(", MonoObject*, i);\n" + "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n" + "\t\t" C_LOCAL_PTRCALL_ARGS ".set("); + p_output.push_back(real_argc_str); + p_output.push_back(" + i, &varargs[i]);\n\t" CLOSE_BLOCK); + } else { + p_output.push_back(c_in_statements); + p_output.push_back("\tconst void* " C_LOCAL_PTRCALL_ARGS "["); + p_output.push_back(argc_str + "] = { "); + p_output.push_back(c_args_var_content + " };\n"); + } + } + + if (p_imethod.is_vararg) { + p_output.push_back("\tVariant::CallError vcall_error;\n\t"); + + if (!ret_void) + p_output.push_back(LOCAL_RET " = "); + + p_output.push_back(CS_PARAM_METHODBIND "->call(" CS_PARAM_INSTANCE ", "); + p_output.push_back(p_imethod.arguments.size() ? "(const Variant**)" C_LOCAL_PTRCALL_ARGS ".ptr()" : "NULL"); + p_output.push_back(", total_length, vcall_error);\n"); + } else { + p_output.push_back("\t" CS_PARAM_METHODBIND "->ptrcall(" CS_PARAM_INSTANCE ", "); + p_output.push_back(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "NULL, "); + p_output.push_back(!ret_void ? "&" LOCAL_RET ");\n" : "NULL);\n"); + } + + if (!ret_void) { + if (return_type->c_out.empty()) + p_output.push_back("\treturn " LOCAL_RET ";\n"); + else + p_output.push_back(sformat(return_type->c_out, return_type->c_type_out, LOCAL_RET, return_type->name)); + } + + p_output.push_back(CLOSE_BLOCK "\n"); + + if (im_icall->editor_only) + p_output.push_back("#endif // TOOLS_ENABLED\n"); + } + + return OK; +} + +const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_null(const String &p_name) { + + const Map<String, TypeInterface>::Element *match = builtin_types.find(p_name); + + if (match) + return &match->get(); + + match = obj_types.find(p_name); + + if (match) + return &match->get(); + + return NULL; +} + +const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_placeholder(const String &p_name) { + + const TypeInterface *found = _get_type_by_name_or_null(p_name); + + if (found) + return found; + + ERR_PRINTS(String() + "Type not found. Creating placeholder: " + p_name); + + const Map<String, TypeInterface>::Element *match = placeholder_types.find(p_name); + + if (match) + return &match->get(); + + TypeInterface placeholder; + TypeInterface::create_placeholder_type(placeholder, p_name); + + return &placeholder_types.insert(placeholder.name, placeholder)->get(); +} + +void BindingsGenerator::_populate_object_type_interfaces() { + + obj_types.clear(); + + List<StringName> class_list; + ClassDB::get_class_list(&class_list); + class_list.sort_custom<StringName::AlphCompare>(); + + StringName refclass_name = String("Reference"); + + while (class_list.size()) { + StringName type_cname = class_list.front()->get(); + + ClassDB::APIType api_type = ClassDB::get_api_type(type_cname); + + if (api_type == ClassDB::API_NONE) { + class_list.pop_front(); + continue; + } + + TypeInterface itype = TypeInterface::create_object_type(type_cname, api_type); + + itype.base_name = ClassDB::get_parent_class(type_cname); + itype.is_singleton = ProjectSettings::get_singleton()->has_singleton(itype.proxy_name); + itype.is_instantiable = ClassDB::can_instance(type_cname) && !itype.is_singleton; + itype.is_reference = ClassDB::is_parent_class(type_cname, refclass_name); + itype.memory_own = itype.is_reference; + + if (!ClassDB::is_class_exposed(type_cname)) { + if (verbose_output) + WARN_PRINTS("Ignoring type " + String(type_cname) + " because it's not exposed"); + class_list.pop_front(); + continue; + } + + itype.c_out = "\treturn "; + itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED; + itype.c_out += itype.is_reference ? "(%1.ptr());\n" : "(%1);\n"; + + itype.cs_in = itype.is_singleton ? BINDINGS_PTR_FIELD : "Object." CS_SMETHOD_GETINSTANCE "(%0)"; + + itype.c_type = "Object*"; + itype.c_type_in = itype.c_type; + itype.c_type_out = "MonoObject*"; + itype.cs_type = itype.proxy_name; + itype.im_type_in = "IntPtr"; + itype.im_type_out = itype.proxy_name; + + List<MethodInfo> virtual_method_list; + ClassDB::get_virtual_methods(type_cname, &virtual_method_list, true); + + List<MethodInfo> method_list; + ClassDB::get_method_list(type_cname, &method_list, true); + method_list.sort(); + + for (List<MethodInfo>::Element *E = method_list.front(); E; E = E->next()) { + const MethodInfo &method_info = E->get(); + + int argc = method_info.arguments.size(); + + if (method_info.name.empty()) + continue; + + MethodInterface imethod; + imethod.name = method_info.name; + + if (method_info.flags & METHOD_FLAG_VIRTUAL) + imethod.is_virtual = true; + + PropertyInfo return_info = method_info.return_val; + + MethodBind *m = imethod.is_virtual ? NULL : ClassDB::get_method(type_cname, method_info.name); + + imethod.is_vararg = m && m->is_vararg(); + + if (!m && !imethod.is_virtual) { + if (virtual_method_list.find(method_info)) { + // A virtual method without the virtual flag. This is a special case. + + // This type of method can only be found in Object derived types. + ERR_FAIL_COND(!itype.is_object_type); + + // There is no method bind, so let's fallback to Godot's object.Call(string, params) + imethod.requires_object_call = true; + + // The method Object.free is registered as a virtual method, but without the virtual flag. + // This is because this method is not supposed to be overridden, but called. + // We assume the return type is void. + imethod.return_type = "void"; + + // Actually, more methods like this may be added in the future, + // which could actually will return something differnet. + // Let's put this to notify us if that ever happens. + if (itype.name != "Object" || imethod.name != "free") { + if (verbose_output) { + WARN_PRINTS("Notification: New unexpected virtual non-overridable method found.\n" + "We only expected Object.free, but found " + + itype.name + "." + imethod.name); + } + } + } else { + ERR_PRINTS("Missing MethodBind for non-virtual method: " + itype.name + "." + imethod.name); + } + } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { + //imethod.return_type = return_info.class_name; + imethod.return_type = "int"; + } else if (return_info.class_name != StringName()) { + imethod.return_type = return_info.class_name; + } else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) { + imethod.return_type = return_info.hint_string; + } else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { + imethod.return_type = "Variant"; + } else if (return_info.type == Variant::NIL) { + imethod.return_type = "void"; + } else { + imethod.return_type = Variant::get_type_name(return_info.type); + } + + if (!itype.requires_collections && imethod.return_type == "Dictionary") + itype.requires_collections = true; + + for (int i = 0; i < argc; i++) { + PropertyInfo arginfo = method_info.arguments[i]; + + ArgumentInterface iarg; + iarg.name = arginfo.name; + + if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { + //iarg.type = arginfo.class_name; + iarg.type = "int"; + } else if (arginfo.class_name != StringName()) { + iarg.type = arginfo.class_name; + } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { + iarg.type = arginfo.hint_string; + } else if (arginfo.type == Variant::NIL) { + iarg.type = "Variant"; + } else { + iarg.type = Variant::get_type_name(arginfo.type); + } + + iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name)); + + if (!itype.requires_collections && iarg.type == "Dictionary") + itype.requires_collections = true; + + if (m && m->has_default_argument(i)) { + _default_argument_from_variant(m->get_default_argument(i), iarg); + } + + imethod.add_argument(iarg); + } + + if (imethod.is_vararg) { + ArgumentInterface ivararg; + ivararg.type = "VarArg"; + ivararg.name = "@args"; + imethod.add_argument(ivararg); + } + + imethod.proxy_name = escape_csharp_keyword(snake_to_pascal_case(imethod.name)); + + // Prevent naming the property and its enclosing type from sharing the same name + if (imethod.proxy_name == itype.proxy_name) { + if (verbose_output) { + WARN_PRINTS("Name of method `" + imethod.proxy_name + "` is ambiguous with the name of its class `" + + itype.proxy_name + "`. Renaming method to `" + imethod.proxy_name + "_`"); + } + + imethod.proxy_name += "_"; + } + + if (itype.class_doc) { + for (int i = 0; i < itype.class_doc->methods.size(); i++) { + if (itype.class_doc->methods[i].name == imethod.name) { + imethod.method_doc = &itype.class_doc->methods[i]; + break; + } + } + } + + if (!imethod.is_virtual && imethod.name[0] == '_') { + const Vector<DocData::PropertyDoc> &properties = itype.class_doc->properties; + + for (int i = 0; i < properties.size(); i++) { + const DocData::PropertyDoc &prop_doc = properties[i]; + + if (prop_doc.getter == imethod.name || prop_doc.setter == imethod.name) { + imethod.is_internal = true; + itype.methods.push_back(imethod); + break; + } + } + } else { + itype.methods.push_back(imethod); + } + } + + obj_types.insert(itype.name, itype); + + class_list.pop_front(); + } +} + +void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, ArgumentInterface &r_iarg) { + + r_iarg.default_argument = p_val; + + switch (p_val.get_type()) { + case Variant::NIL: + if (ClassDB::class_exists(r_iarg.type)) { + // Object type + r_iarg.default_argument = "null"; + } else { + // Variant + r_iarg.default_argument = "null"; + } + break; + // Atomic types + case Variant::BOOL: + r_iarg.default_argument = bool(p_val) ? "true" : "false"; + break; + case Variant::INT: + break; // Keep it + case Variant::REAL: +#ifndef REAL_T_IS_DOUBLE + r_iarg.default_argument += "f"; +#endif + break; + case Variant::STRING: + case Variant::NODE_PATH: + r_iarg.default_argument = "\"" + r_iarg.default_argument + "\""; + break; + case Variant::TRANSFORM: + if (p_val.operator Transform() == Transform()) + r_iarg.default_argument.clear(); + r_iarg.default_argument = "new %s(" + r_iarg.default_argument + ")"; + r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; + break; + case Variant::PLANE: + case Variant::RECT3: + case Variant::COLOR: + r_iarg.default_argument = "new Color(1, 1, 1, 1)"; + r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; + break; + case Variant::VECTOR2: + case Variant::RECT2: + case Variant::VECTOR3: + r_iarg.default_argument = "new %s" + r_iarg.default_argument; + r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; + break; + case Variant::OBJECT: + if (p_val.is_zero()) { + r_iarg.default_argument = "null"; + break; + } + case Variant::DICTIONARY: + case Variant::_RID: + r_iarg.default_argument = "new %s()"; + r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF; + break; + case Variant::ARRAY: + case Variant::POOL_BYTE_ARRAY: + case Variant::POOL_INT_ARRAY: + case Variant::POOL_REAL_ARRAY: + case Variant::POOL_STRING_ARRAY: + case Variant::POOL_VECTOR2_ARRAY: + case Variant::POOL_VECTOR3_ARRAY: + case Variant::POOL_COLOR_ARRAY: + r_iarg.default_argument = "new %s {}"; + r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF; + break; + case Variant::TRANSFORM2D: + case Variant::BASIS: + case Variant::QUAT: + r_iarg.default_argument = Variant::get_type_name(p_val.get_type()) + ".Identity"; + r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; + break; + default: {} + } + + if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type == "Variant" && r_iarg.default_argument != "null") + r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF; +} + +void BindingsGenerator::_populate_builtin_type_interfaces() { + + builtin_types.clear(); + + TypeInterface itype; + +#define INSERT_STRUCT_TYPE(m_type, m_type_in) \ + { \ + itype = TypeInterface::create_value_type(#m_type); \ + itype.c_in = "\tMARSHALLED_IN(" #m_type ", %1, %1_in);\n"; \ + itype.c_out = "\tMARSHALLED_OUT(" #m_type ", %1, ret_out)\n" \ + "\treturn mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(%2), ret_out);\n"; \ + itype.c_arg_in = "&%s_in"; \ + itype.c_type_in = m_type_in; \ + itype.cs_in = "ref %s"; \ + itype.cs_out = "return (" #m_type ")%0;"; \ + itype.im_type_out = "object"; \ + builtin_types.insert(#m_type, itype); \ + } + + INSERT_STRUCT_TYPE(Vector2, "real_t*") + INSERT_STRUCT_TYPE(Rect2, "real_t*") + INSERT_STRUCT_TYPE(Transform2D, "real_t*") + INSERT_STRUCT_TYPE(Vector3, "real_t*") + INSERT_STRUCT_TYPE(Basis, "real_t*") + INSERT_STRUCT_TYPE(Quat, "real_t*") + INSERT_STRUCT_TYPE(Transform, "real_t*") + INSERT_STRUCT_TYPE(Rect3, "real_t*") + INSERT_STRUCT_TYPE(Color, "real_t*") + INSERT_STRUCT_TYPE(Plane, "real_t*") + +#undef INSERT_STRUCT_TYPE + +#define INSERT_PRIMITIVE_TYPE(m_type) \ + { \ + itype = TypeInterface::create_value_type(#m_type); \ + itype.c_arg_in = "&%s"; \ + itype.c_type_in = #m_type; \ + itype.c_type_out = #m_type; \ + itype.im_type_in = #m_type; \ + itype.im_type_out = #m_type; \ + builtin_types.insert(#m_type, itype); \ + } + + INSERT_PRIMITIVE_TYPE(bool) + //INSERT_PRIMITIVE_TYPE(int) + + // int + itype = TypeInterface::create_value_type("int"); + itype.c_arg_in = "&%s_in"; + //* ptrcall only supports int64_t and uint64_t + itype.c_in = "\t%0 %1_in = (%0)%1;\n"; + itype.c_out = "\treturn (%0)%1;\n"; + itype.c_type = "int64_t"; + //*/ + itype.c_type_in = itype.name; + itype.c_type_out = itype.name; + itype.im_type_in = itype.name; + itype.im_type_out = itype.name; + builtin_types.insert(itype.name, itype); + +#undef INSERT_PRIMITIVE_TYPE + + // real_t + itype = TypeInterface(); +#ifdef REAL_T_IS_DOUBLE + itype.name = "double"; +#else + itype.name = "float"; +#endif + itype.proxy_name = itype.name; + itype.c_arg_in = "&%s_in"; + //* ptrcall only supports double + itype.c_in = "\t%0 %1_in = (%0)%1;\n"; + itype.c_out = "\treturn (%0)%1;\n"; + itype.c_type = "double"; + //*/ + itype.c_type_in = "real_t"; + itype.c_type_out = "real_t"; + itype.cs_type = itype.proxy_name; + itype.im_type_in = itype.proxy_name; + itype.im_type_out = itype.proxy_name; + builtin_types.insert(itype.name, itype); + + // String + itype = TypeInterface(); + itype.name = "String"; + itype.proxy_name = "string"; + itype.c_in = "\t%0 %1_in = " C_METHOD_MONOSTR_TO_GODOT "(%1);\n"; + itype.c_out = "\treturn " C_METHOD_MONOSTR_FROM_GODOT "(%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = itype.name; + itype.c_type_in = "MonoString*"; + itype.c_type_out = "MonoString*"; + itype.cs_type = itype.proxy_name; + itype.im_type_in = itype.proxy_name; + itype.im_type_out = itype.proxy_name; + builtin_types.insert(itype.name, itype); + + // NodePath + itype = TypeInterface(); + itype.name = "NodePath"; + itype.proxy_name = "NodePath"; + itype.c_out = "\treturn memnew(NodePath(%1));\n"; + itype.c_type = itype.name; + itype.c_type_in = itype.c_type + "*"; + itype.c_type_out = itype.c_type + "*"; + itype.cs_type = itype.proxy_name; + itype.cs_in = "NodePath." CS_SMETHOD_GETINSTANCE "(%0)"; + itype.cs_out = "return new NodePath(%0);"; + itype.im_type_in = "IntPtr"; + itype.im_type_out = "IntPtr"; + _populate_builtin_type(itype, Variant::NODE_PATH); + extra_members.insert(itype.name, MEMBER_BEGIN "public NodePath() : this(string.Empty) {}\n" MEMBER_BEGIN "public NodePath(string path)\n" OPEN_BLOCK_L2 + "this." BINDINGS_PTR_FIELD " = NativeCalls.godot_icall_NodePath_Ctor(path);\n" CLOSE_BLOCK_L2 + MEMBER_BEGIN "public static implicit operator NodePath(string from)\n" OPEN_BLOCK_L2 "return new NodePath(from);\n" CLOSE_BLOCK_L2 + MEMBER_BEGIN "public static implicit operator string(NodePath from)\n" OPEN_BLOCK_L2 + "return NativeCalls." ICALL_PREFIX "NodePath_operator_String(NodePath." CS_SMETHOD_GETINSTANCE "(from));\n" CLOSE_BLOCK_L2); + builtin_types.insert(itype.name, itype); + + // RID + itype = TypeInterface(); + itype.name = "RID"; + itype.proxy_name = "RID"; + itype.c_out = "\treturn memnew(RID(%1));\n"; + itype.c_type = itype.name; + itype.c_type_in = itype.c_type + "*"; + itype.c_type_out = itype.c_type + "*"; + itype.cs_type = itype.proxy_name; + itype.cs_in = "RID." CS_SMETHOD_GETINSTANCE "(%0)"; + itype.cs_out = "return new RID(%0);"; + itype.im_type_in = "IntPtr"; + itype.im_type_out = "IntPtr"; + _populate_builtin_type(itype, Variant::_RID); + extra_members.insert(itype.name, MEMBER_BEGIN "internal RID()\n" OPEN_BLOCK_L2 + "this." BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" CLOSE_BLOCK_L2); + builtin_types.insert(itype.name, itype); + + // Variant + itype = TypeInterface(); + itype.name = "Variant"; + itype.proxy_name = "object"; + itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_VARIANT "(%1);\n"; + itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_VARIANT "(%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = itype.name; + itype.c_type_in = "MonoObject*"; + itype.c_type_out = "MonoObject*"; + itype.cs_type = itype.proxy_name; + itype.im_type_in = "object"; + itype.im_type_out = itype.proxy_name; + builtin_types.insert(itype.name, itype); + + // VarArg (fictitious type to represent variable arguments) + itype = TypeInterface(); + itype.name = "VarArg"; + itype.proxy_name = "object[]"; + itype.c_in = "\t%0 %1_in = " C_METHOD_MONOARRAY_TO(Array) "(%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "Array"; + itype.c_type_in = "MonoArray*"; + itype.cs_type = "params object[]"; + itype.im_type_in = "object[]"; + builtin_types.insert(itype.name, itype); + +#define INSERT_ARRAY_FULL(m_name, m_type, m_proxy_t) \ + { \ + itype = TypeInterface(); \ + itype.name = #m_name; \ + itype.proxy_name = #m_proxy_t "[]"; \ + itype.c_in = "\t%0 %1_in = " C_METHOD_MONOARRAY_TO(m_type) "(%1);\n"; \ + itype.c_out = "\treturn " C_METHOD_MONOARRAY_FROM(m_type) "(%1);\n"; \ + itype.c_arg_in = "&%s_in"; \ + itype.c_type = #m_type; \ + itype.c_type_in = "MonoArray*"; \ + itype.c_type_out = "MonoArray*"; \ + itype.cs_type = itype.proxy_name; \ + itype.im_type_in = itype.proxy_name; \ + itype.im_type_out = itype.proxy_name; \ + builtin_types.insert(itype.name, itype); \ + } + +#define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t) + + INSERT_ARRAY(Array, object); + INSERT_ARRAY(PoolIntArray, int); + INSERT_ARRAY_FULL(PoolByteArray, PoolByteArray, byte); + +#ifdef REAL_T_IS_DOUBLE + INSERT_ARRAY(PoolRealArray, double); +#else + INSERT_ARRAY(PoolRealArray, float); +#endif + + INSERT_ARRAY(PoolStringArray, string); + + INSERT_ARRAY(PoolColorArray, Color); + INSERT_ARRAY(PoolVector2Array, Vector2); + INSERT_ARRAY(PoolVector3Array, Vector3); + +#undef INSERT_ARRAY + + // Dictionary + itype = TypeInterface(); + itype.name = "Dictionary"; + itype.proxy_name = "Dictionary<object, object>"; + itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_DICT "(%1);\n"; + itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_DICT "(%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = itype.name; + itype.c_type_in = "MonoObject*"; + itype.c_type_out = "MonoObject*"; + itype.cs_type = itype.proxy_name; + itype.im_type_in = itype.proxy_name; + itype.im_type_out = itype.proxy_name; + builtin_types.insert(itype.name, itype); + + // void (fictitious type to represent the return type of methods that do not return anything) + itype = TypeInterface(); + itype.name = "void"; + itype.proxy_name = itype.name; + itype.c_type = itype.name; + itype.c_type_in = itype.c_type; + itype.c_type_out = itype.c_type; + itype.cs_type = itype.proxy_name; + itype.im_type_in = itype.proxy_name; + itype.im_type_out = itype.proxy_name; + builtin_types.insert(itype.name, itype); + + // Error + itype = TypeInterface(); + itype.name = "Error"; + itype.proxy_name = "Error"; + itype.c_type = itype.name; + itype.c_type_in = itype.c_type; + itype.c_type_out = itype.c_type; + itype.cs_type = itype.proxy_name; + itype.cs_in = "(int)%0"; + itype.cs_out = "return (Error)%s;"; + itype.im_type_in = "int"; + itype.im_type_out = "int"; + builtin_types.insert(itype.name, itype); +} + +void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant::Type vtype) { + + Variant::CallError cerror; + Variant v = Variant::construct(vtype, NULL, 0, cerror); + + List<MethodInfo> method_list; + v.get_method_list(&method_list); + method_list.sort(); + + for (List<MethodInfo>::Element *E = method_list.front(); E; E = E->next()) { + MethodInfo &mi = E->get(); + MethodInterface imethod; + + imethod.name = mi.name; + imethod.proxy_name = mi.name; + + for (int i = 0; i < mi.arguments.size(); i++) { + ArgumentInterface iarg; + PropertyInfo pi = mi.arguments[i]; + + iarg.name = pi.name; + + if (pi.type == Variant::NIL) + iarg.type = "Variant"; + else + iarg.type = Variant::get_type_name(pi.type); + + if (!r_itype.requires_collections && iarg.type == "Dictionary") + r_itype.requires_collections = true; + + if ((mi.default_arguments.size() - mi.arguments.size() + i) >= 0) + _default_argument_from_variant(Variant::construct(pi.type, NULL, 0, cerror), iarg); + + imethod.add_argument(iarg); + } + + if (mi.return_val.type == Variant::NIL) { + if (mi.return_val.name != "") + imethod.return_type = "Variant"; + } else { + imethod.return_type = Variant::get_type_name(mi.return_val.type); + } + + if (!r_itype.requires_collections && imethod.return_type == "Dictionary") + r_itype.requires_collections = true; + + if (r_itype.class_doc) { + for (int i = 0; i < r_itype.class_doc->methods.size(); i++) { + if (r_itype.class_doc->methods[i].name == imethod.name) { + imethod.method_doc = &r_itype.class_doc->methods[i]; + break; + } + } + } + + r_itype.methods.push_back(imethod); + } +} + +BindingsGenerator::BindingsGenerator() { + + EditorHelp::generate_doc(); + + _populate_object_type_interfaces(); + _populate_builtin_type_interfaces(); + _generate_header_icalls(); + + for (Map<String, TypeInterface>::Element *E = obj_types.front(); E; E = E->next()) + _generate_method_icalls(E->get()); + + _generate_method_icalls(builtin_types["NodePath"]); + _generate_method_icalls(builtin_types["RID"]); +} + +void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) { + + const int NUM_OPTIONS = 3; + int options_left = NUM_OPTIONS; + + String mono_glue_option = "--generate-mono-glue"; + String cs_core_api_option = "--generate-cs-core-api"; + String cs_editor_api_option = "--generate-cs-editor-api"; + + verbose_output = true; + + const List<String>::Element *elem = p_cmdline_args.front(); + + while (elem && options_left) { + + if (elem->get() == mono_glue_option) { + + const List<String>::Element *path_elem = elem->next(); + + if (path_elem) { + if (get_singleton().generate_glue(path_elem->get()) != OK) + ERR_PRINT("Mono glue generation failed"); + elem = elem->next(); + } else { + ERR_PRINTS("--generate-mono-glue: No output directory specified"); + } + + --options_left; + + } else if (elem->get() == cs_core_api_option) { + + const List<String>::Element *path_elem = elem->next(); + + if (path_elem) { + if (get_singleton().generate_cs_core_project(path_elem->get()) != OK) + ERR_PRINT("Generation of solution and C# project for the Core API failed"); + elem = elem->next(); + } else { + ERR_PRINTS(cs_core_api_option + ": No output directory specified"); + } + + --options_left; + + } else if (elem->get() == cs_editor_api_option) { + + const List<String>::Element *path_elem = elem->next(); + + if (path_elem) { + if (path_elem->next()) { + if (get_singleton().generate_cs_editor_project(path_elem->get(), path_elem->next()->get()) != OK) + ERR_PRINT("Generation of solution and C# project for the Editor API failed"); + elem = path_elem->next(); + } else { + ERR_PRINTS(cs_editor_api_option + ": No hint path for the Core API dll specified"); + } + } else { + ERR_PRINTS(cs_editor_api_option + ": No output directory specified"); + } + + --options_left; + } + + elem = elem->next(); + } + + verbose_output = false; + + if (options_left != NUM_OPTIONS) + exit(0); +} + +#endif diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h new file mode 100644 index 0000000000..dfa3aa9911 --- /dev/null +++ b/modules/mono/editor/bindings_generator.h @@ -0,0 +1,436 @@ +/*************************************************************************/ +/* bindings_generator.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 BINDINGS_GENERATOR_H +#define BINDINGS_GENERATOR_H + +#include "class_db.h" +#include "editor/doc/doc_data.h" +#include "editor/editor_help.h" + +#ifdef DEBUG_METHODS_ENABLED + +#include "ustring.h" + +class BindingsGenerator { + struct ArgumentInterface { + enum DefaultParamMode { + CONSTANT, + NULLABLE_VAL, + NULLABLE_REF + }; + + String type; + String name; + String default_argument; + DefaultParamMode def_param_mode; + + ArgumentInterface() { + def_param_mode = CONSTANT; + } + }; + + struct MethodInterface { + String name; + + /** + * Name of the C# method + */ + String proxy_name; + + /** + * [TypeInterface::name] of the return type + */ + String return_type; + + /** + * Determines if the method has a variable number of arguments (VarArg) + */ + bool is_vararg; + + /** + * Virtual methods ("virtual" as defined by the Godot API) are methods that by default do nothing, + * but can be overridden by the user to add custom functionality. + * e.g.: _ready, _process, etc. + */ + bool is_virtual; + + /** + * Determines if the call should fallback to Godot's object.Call(string, params) in C#. + */ + bool requires_object_call; + + /** + * Determines if the method visibility is `internal` (visible only to files in the same assembly). + * Currently, we only use this for methods that are not meant to be exposed, + * but are required by properties as getters or setters. + * Methods that are not meant to be exposed are those that begin with underscore and are not virtual. + */ + bool is_internal; + + List<ArgumentInterface> arguments; + + const DocData::MethodDoc *method_doc; + + void add_argument(const ArgumentInterface &argument) { + arguments.push_back(argument); + } + + MethodInterface() { + return_type = "void"; + is_vararg = false; + is_virtual = false; + requires_object_call = false; + is_internal = false; + method_doc = NULL; + } + }; + + struct TypeInterface { + /** + * Identifier name for this type. + * Also used to format [c_out]. + */ + String name; + + /** + * Identifier name of the base class. + */ + String base_name; + + /** + * Name of the C# class + */ + String proxy_name; + + ClassDB::APIType api_type; + + bool is_object_type; + bool is_singleton; + bool is_reference; + + /** + * Used only by Object-derived types. + * Determines if this type is not virtual (incomplete). + * e.g.: CanvasItem cannot be instantiated. + */ + bool is_instantiable; + + /** + * Used only by Object-derived types. + * Determines if the C# class owns the native handle and must free it somehow when disposed. + * e.g.: Reference types must notify when the C# instance is disposed, for proper refcounting. + */ + bool memory_own; + + /** + * Determines if the file must have a using directive for System.Collections.Generic + * e.g.: When the generated class makes use of Dictionary + */ + bool requires_collections; + + // !! The comments of the following fields make reference to other fields via square brackets, e.g.: [field_name] + // !! When renaming those fields, make sure to rename their references in the comments + + // --- C INTERFACE --- + + static const char *DEFAULT_VARARG_C_IN; + + /** + * One or more statements that manipulate the parameter before being passed as argument of a ptrcall. + * If the statement adds a local that must be passed as the argument instead of the parameter, + * the name of that local must be specified with [c_arg_in]. + * For variadic methods, this field is required and, if empty, [DEFAULT_VARARG_C_IN] is used instead. + * Formatting elements: + * %0: [c_type] of the parameter + * %1: name of the parameter + */ + String c_in; + + /** + * Determines the name of the variable that will be passed as argument to a ptrcall. + * By default the value equals the name of the parameter, + * this varies for types that require special manipulation via [c_in]. + * Formatting elements: + * %0 or %s: name of the parameter + */ + String c_arg_in; + + /** + * One or more statements that determine how a variable of this type is returned from a function. + * It must contain the return statement(s). + * Formatting elements: + * %0: [c_type_out] of the return type + * %1: name of the variable to be returned + * %2: [name] of the return type + */ + String c_out; + + /** + * The actual expected type, as seen (in most cases) in Variant copy constructors + * Used for the type of the return variable and to format [c_in]. + * The value must be the following depending of the type: + * Object-derived types: Object* + * Other types: [name] + * -- Exceptions -- + * VarArg (fictitious type to represent variable arguments): Array + * float: double (because ptrcall only supports double) + * int: int64_t (because ptrcall only supports int64_t and uint64_t) + * Reference types override this for the type of the return variable: Ref<Reference> + */ + String c_type; + + /** + * Determines the type used for parameters in function signatures. + */ + String c_type_in; + + /** + * Determines the return type used for function signatures. + * Also used to construct a default value to return in case of errors, + * and to format [c_out]. + */ + String c_type_out; + + // --- C# INTERFACE --- + + /** + * An expression that overrides the way the parameter is passed to the internal call. + * If empty, the parameter is passed as is. + * Formatting elements: + * %0 or %s: name of the parameter + */ + String cs_in; + + /** + * One or more statements that determine how a variable of this type is returned from a method. + * It must contain the return statement(s). + * Formatting elements: + * %0 or %s: name of the variable to be returned + */ + String cs_out; + + /** + * Type used for method signatures, both for parameters and the return type. + * Same as [proxy_name] except for variable arguments (VarArg). + */ + String cs_type; + + /** + * Type used for parameters of internal call methods. + */ + String im_type_in; + + /** + * Type used for the return type of internal call methods. + * If [cs_out] is not empty and the method return type is not void, + * it is also used for the type of the return variable. + */ + String im_type_out; + + const DocData::ClassDoc *class_doc; + + List<MethodInterface> methods; + + const MethodInterface *find_method_by_name(const String &p_name) const { + + for (const List<MethodInterface>::Element *E = methods.front(); E; E = E->next()) { + if (E->get().name == p_name) + return &E->get(); + } + + return NULL; + } + + static TypeInterface create_value_type(const String &p_name) { + TypeInterface itype; + + itype.name = p_name; + itype.proxy_name = p_name; + + itype.c_type = itype.name; + itype.c_type_in = "void*"; + itype.c_type_out = "MonoObject*"; + itype.cs_type = itype.proxy_name; + itype.im_type_in = "ref " + itype.proxy_name; + itype.im_type_out = itype.proxy_name; + itype.class_doc = &EditorHelp::get_doc_data()->class_list[itype.proxy_name]; + + return itype; + } + + static TypeInterface create_object_type(const String &p_name, ClassDB::APIType p_api_type) { + TypeInterface itype; + + itype.name = p_name; + itype.proxy_name = p_name.begins_with("_") ? p_name.substr(1, p_name.length()) : p_name; + itype.api_type = p_api_type; + itype.is_object_type = true; + itype.class_doc = &EditorHelp::get_doc_data()->class_list[itype.proxy_name]; + + return itype; + } + + static void create_placeholder_type(TypeInterface &r_itype, const String &p_name) { + r_itype.name = p_name; + r_itype.proxy_name = p_name; + + r_itype.c_type = r_itype.name; + r_itype.c_type_in = "MonoObject*"; + r_itype.c_type_out = "MonoObject*"; + r_itype.cs_type = r_itype.proxy_name; + r_itype.im_type_in = r_itype.proxy_name; + r_itype.im_type_out = r_itype.proxy_name; + } + + TypeInterface() { + + api_type = ClassDB::API_NONE; + + is_object_type = false; + is_singleton = false; + is_reference = false; + is_instantiable = false; + + memory_own = false; + requires_collections = false; + + c_arg_in = "%s"; + + class_doc = NULL; + } + }; + + struct InternalCall { + String name; + String im_type_out; // Return type for the C# method declaration. Also used as companion of [unique_siq] + String im_sig; // Signature for the C# method declaration + String unique_sig; // Unique signature to avoid duplicates in containers + bool editor_only; + + InternalCall() {} + + InternalCall(const String &p_name, const String &p_im_type_out, const String &p_im_sig = String(), const String &p_unique_sig = String()) { + name = p_name; + im_type_out = p_im_type_out; + im_sig = p_im_sig; + unique_sig = p_unique_sig; + editor_only = false; + } + + InternalCall(ClassDB::APIType api_type, const String &p_name, const String &p_im_type_out, const String &p_im_sig = String(), const String &p_unique_sig = String()) { + name = p_name; + im_type_out = p_im_type_out; + im_sig = p_im_sig; + unique_sig = p_unique_sig; + editor_only = api_type == ClassDB::API_EDITOR; + } + + inline bool operator==(const InternalCall &p_a) const { + return p_a.unique_sig == unique_sig; + } + }; + + static bool verbose_output; + + Map<String, TypeInterface> placeholder_types; + Map<String, TypeInterface> builtin_types; + Map<String, TypeInterface> obj_types; + + Map<String, String> extra_members; + + List<InternalCall> method_icalls; + Map<const MethodInterface *, const InternalCall *> method_icalls_map; + + List<const InternalCall *> generated_icall_funcs; + + List<InternalCall> core_custom_icalls; + List<InternalCall> editor_custom_icalls; + + const List<InternalCall>::Element *find_icall_by_name(const String &p_name, const List<InternalCall> &p_list) { + + const List<InternalCall>::Element *it = p_list.front(); + while (it) { + if (it->get().name == p_name) return it; + it = it->next(); + } + return NULL; + } + + inline String get_unique_sig(const TypeInterface &p_type) { + if (p_type.is_reference) + return "Ref"; + else if (p_type.is_object_type) + return "Obj"; + + return p_type.name; + } + + void _generate_header_icalls(); + void _generate_method_icalls(const TypeInterface &p_itype); + + const TypeInterface *_get_type_by_name_or_null(const String &p_name); + const TypeInterface *_get_type_by_name_or_placeholder(const String &p_name); + + void _default_argument_from_variant(const Variant &p_var, ArgumentInterface &r_iarg); + void _populate_builtin_type(TypeInterface &r_type, Variant::Type vtype); + + void _populate_object_type_interfaces(); + void _populate_builtin_type_interfaces(); + + Error _generate_cs_type(const TypeInterface &itype, const String &p_output_file); + + Error _generate_cs_property(const TypeInterface &p_itype, const DocData::PropertyDoc &p_prop_doc, List<String> &p_output); + Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output); + + Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, List<String> &p_output); + + Error _save_file(const String &path, const List<String> &content); + + BindingsGenerator(); + + BindingsGenerator(const BindingsGenerator &); + BindingsGenerator &operator=(const BindingsGenerator &); + +public: + Error generate_cs_core_project(const String &p_output_dir, bool p_verbose_output = true); + Error generate_cs_editor_project(const String &p_output_dir, const String &p_core_dll_path, bool p_verbose_output = true); + Error generate_glue(const String &p_output_dir); + + static BindingsGenerator &get_singleton() { + static BindingsGenerator singleton; + return singleton; + } + + static void handle_cmdline_args(const List<String> &p_cmdline_args); +}; + +#endif + +#endif // BINDINGS_GENERATOR_H diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp new file mode 100644 index 0000000000..bde5f0fd0b --- /dev/null +++ b/modules/mono/editor/csharp_project.cpp @@ -0,0 +1,120 @@ +/*************************************************************************/ +/* csharp_project.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "csharp_project.h" + +#include "os/os.h" +#include "project_settings.h" + +#include "../mono_gd/gd_mono_class.h" +#include "../mono_gd/gd_mono_marshal.h" + +namespace CSharpProject { + +String generate_core_api_project(const String &p_dir, const Vector<String> &p_files) { + + _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) + + GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator"); + + Variant dir = p_dir; + Variant compile_items = p_files; + const Variant *args[2] = { &dir, &compile_items }; + MonoObject *ex = NULL; + MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &ex); + + if (ex) { + mono_print_unhandled_exception(ex); + ERR_FAIL_V(String()); + } + + return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : ""; +} + +String generate_editor_api_project(const String &p_dir, const String &p_core_dll_path, const Vector<String> &p_files) { + + _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) + + GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator"); + + Variant dir = p_dir; + Variant core_dll_path = p_core_dll_path; + Variant compile_items = p_files; + const Variant *args[3] = { &dir, &core_dll_path, &compile_items }; + MonoObject *ex = NULL; + MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &ex); + + if (ex) { + mono_print_unhandled_exception(ex); + ERR_FAIL_V(String()); + } + + return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : ""; +} + +String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files) { + + _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) + + GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator"); + + Variant dir = p_dir; + Variant name = p_name; + Variant compile_items = p_files; + const Variant *args[3] = { &dir, &name, &compile_items }; + MonoObject *ex = NULL; + MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &ex); + + if (ex) { + mono_print_unhandled_exception(ex); + ERR_FAIL_V(String()); + } + + return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : ""; +} + +void add_item(const String &p_project_path, const String &p_item_type, const String &p_include) { + + _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) + + GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils"); + + Variant project_path = p_project_path; + Variant item_type = p_item_type; + Variant include = p_include; + const Variant *args[3] = { &project_path, &item_type, &include }; + MonoObject *ex = NULL; + klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &ex); + + if (ex) { + mono_print_unhandled_exception(ex); + ERR_FAIL(); + } +} +} // CSharpProject diff --git a/modules/mono/editor/csharp_project.h b/modules/mono/editor/csharp_project.h new file mode 100644 index 0000000000..4832d2251e --- /dev/null +++ b/modules/mono/editor/csharp_project.h @@ -0,0 +1,44 @@ +/*************************************************************************/ +/* csharp_project.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 CSHARP_PROJECT_H +#define CSHARP_PROJECT_H + +#include "ustring.h" + +namespace CSharpProject { + +String generate_core_api_project(const String &p_dir, const Vector<String> &p_files = Vector<String>()); +String generate_editor_api_project(const String &p_dir, const String &p_core_dll_path, const Vector<String> &p_files = Vector<String>()); +String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files = Vector<String>()); + +void add_item(const String &p_project_path, const String &p_item_type, const String &p_include); +} + +#endif // CSHARP_PROJECT_H diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp new file mode 100644 index 0000000000..dbe0cc294c --- /dev/null +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -0,0 +1,511 @@ +/*************************************************************************/ +/* godotsharp_builds.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "godotsharp_builds.h" + +#include "main/main.h" + +#include "../godotsharp_dirs.h" +#include "../mono_gd/gd_mono.h" +#include "../mono_gd/gd_mono_class.h" +#include "../mono_gd/gd_mono_marshal.h" +#include "../utils/path_utils.h" +#include "bindings_generator.h" +#include "godotsharp_editor.h" + +void godot_icall_BuildInstance_ExitCallback(MonoString *p_solution, MonoString *p_config, int p_exit_code) { + + String solution = GDMonoMarshal::mono_string_to_godot(p_solution); + String config = GDMonoMarshal::mono_string_to_godot(p_config); + GodotSharpBuilds::get_singleton()->build_exit_callback(MonoBuildInfo(solution, config), p_exit_code); +} + +#ifdef UNIX_ENABLED +String _find_build_engine_on_unix(const String &p_name) { + String ret = path_which(p_name); + + if (ret.length()) + return ret; + + const char *locations[] = { +#ifdef OSX_ENABLED + "/Library/Frameworks/Mono.framework/Versions/Current/bin/", +#endif + "/opt/novell/mono/bin/" + }; + + for (int i = 0; i < sizeof(locations) / sizeof(const char *); i++) { + String hint_path = locations[i] + p_name; + + if (FileAccess::exists(hint_path)) { + return hint_path; + } + } + + return String(); +} +#endif + +void godot_icall_BuildInstance_get_MSBuildInfo(MonoString **r_msbuild_path, MonoString **r_framework_path) { + + GodotSharpBuilds::BuildTool build_tool = GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool"))); + +#if defined(WINDOWS_ENABLED) + switch (build_tool) { + case GodotSharpBuilds::MSBUILD: { + static String msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path(); + + if (msbuild_tools_path.length()) { + if (!msbuild_tools_path.ends_with("\\")) + msbuild_tools_path += "\\"; + + // FrameworkPathOverride + const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info(); + if (mono_reg_info.assembly_dir.length()) { + *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe"); + + String framework_path = path_join(mono_reg_info.assembly_dir, "mono", "4.5"); + *r_framework_path = GDMonoMarshal::mono_string_from_godot(framework_path); + } else { + ERR_PRINT("Cannot find Mono's assemblies directory in the registry"); + } + + return; + } + + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->print("Cannot find System's MSBuild. Trying with Mono's...\n"); + } // fall through + case GodotSharpBuilds::MSBUILD_MONO: { + String msbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("msbuild.bat"); + + if (!FileAccess::exists(msbuild_path)) { + WARN_PRINTS("Cannot find msbuild ('mono/builds/build_tool'). Tried with path: " + msbuild_path); + } + + *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(msbuild_path); + + return; + } break; + default: + ERR_EXPLAIN("You don't deserve to live"); + CRASH_NOW(); + } +#elif defined(UNIX_ENABLED) + static String msbuild_path = _find_build_engine_on_unix("msbuild"); + static String xbuild_path = _find_build_engine_on_unix("xbuild"); + + if (build_tool != GodotSharpBuilds::XBUILD) { + if (msbuild_path.empty()) { + WARN_PRINT("Cannot find msbuild ('mono/builds/build_tool')."); + return; + } + } else { + if (xbuild_path.empty()) { + WARN_PRINT("Cannot find xbuild ('mono/builds/build_tool')."); + return; + } + } + + *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path); + + return; +#else + ERR_PRINT("Not implemented on this platform"); + return; +#endif +} + +void GodotSharpBuilds::_register_internal_calls() { + + mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback); + mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildInfo", (void *)godot_icall_BuildInstance_get_MSBuildInfo); +} + +void GodotSharpBuilds::show_build_error_dialog(const String &p_message) { + + GodotSharpEditor::get_singleton()->show_error_dialog(p_message, "Build error"); + MonoBottomPanel::get_singleton()->show_build_tab(); +} + +bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config) { + + String api_sln_file = p_api_sln_dir.plus_file(p_name + ".sln"); + String api_assembly_dir = p_api_sln_dir.plus_file("bin").plus_file(p_config); + String api_assembly_file = api_assembly_dir.plus_file(p_name + ".dll"); + + if (!FileAccess::exists(api_assembly_file)) { + MonoBuildInfo api_build_info(api_sln_file, p_config); + api_build_info.custom_props.push_back("NoWarn=1591"); // Ignore missing documentation warnings + + if (!GodotSharpBuilds::get_singleton()->build(api_build_info)) { + show_build_error_dialog("Failed to build " + p_name + " solution."); + return false; + } + } + + return true; +} + +bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name) { + + String assembly_file = p_assembly_name + ".dll"; + String assembly_src = p_src_dir.plus_file(assembly_file); + String assembly_dst = p_dst_dir.plus_file(assembly_file); + + if (!FileAccess::exists(assembly_dst) || FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst)) { + DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + + String xml_file = p_assembly_name + ".xml"; + if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK) + WARN_PRINTS("Failed to copy " + xml_file); + + String pdb_file = p_assembly_name + ".pdb"; + if (da->copy(p_src_dir.plus_file(pdb_file), p_dst_dir.plus_file(pdb_file)) != OK) + WARN_PRINTS("Failed to copy " + pdb_file); + + Error err = da->copy(assembly_src, assembly_dst); + + memdelete(da); + + if (err != OK) { + show_build_error_dialog("Failed to copy " API_ASSEMBLY_NAME ".dll"); + return false; + } + } + + return true; +} + +bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) { + + String api_name = p_api_type == API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME; + String api_build_config = "Release"; + + EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 4); + + pr.step("Generating " + api_name + " solution"); + + uint64_t core_hash = GDMono::get_singleton()->get_api_core_hash(); + uint64_t editor_hash = GDMono::get_singleton()->get_api_editor_hash(); + + String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(API_ASSEMBLY_NAME "_" + itos(core_hash)); + String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(EDITOR_API_ASSEMBLY_NAME "_" + itos(editor_hash)); + + String api_sln_dir = p_api_type == API_CORE ? core_api_sln_dir : editor_api_sln_dir; + String api_sln_file = api_sln_dir.plus_file(api_name + ".sln"); + + if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) { + String core_api_assembly; + + if (p_api_type == API_EDITOR) { + core_api_assembly = core_api_sln_dir.plus_file("bin") + .plus_file(api_build_config) + .plus_file(API_ASSEMBLY_NAME ".dll"); + } + +#ifndef DEBUG_METHODS_ENABLED +#error "How am I supposed to generate the bindings?" +#endif + + BindingsGenerator &gen = BindingsGenerator::get_singleton(); + bool gen_verbose = OS::get_singleton()->is_stdout_verbose(); + + Error err = p_api_type == API_CORE ? + gen.generate_cs_core_project(api_sln_dir, gen_verbose) : + gen.generate_cs_editor_project(api_sln_dir, core_api_assembly, gen_verbose); + + if (err != OK) { + show_build_error_dialog("Failed to generate " + api_name + " solution. Error: " + itos(err)); + return false; + } + } + + pr.step("Building " + api_name + " solution"); + + if (!GodotSharpBuilds::build_api_sln(api_name, api_sln_dir, api_build_config)) + return false; + + pr.step("Copying " + api_name + " assembly"); + + String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir(); + + // Create assemblies directory if needed + if (!DirAccess::exists(res_assemblies_dir)) { + DirAccess *da = DirAccess::create_for_path(res_assemblies_dir); + Error err = da->make_dir_recursive(res_assemblies_dir); + memdelete(da); + + if (err != OK) { + show_build_error_dialog("Failed to create assemblies directory. Error: " + itos(err)); + return false; + } + } + + // Copy the built assembly to the assemblies directory + String api_assembly_dir = api_sln_dir.plus_file("bin").plus_file(api_build_config); + if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name)) + return false; + + pr.step("Done"); + + return true; +} + +bool GodotSharpBuilds::build_project_blocking() { + + if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path())) + return true; // No solution to build + + if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE)) + return false; + + if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR)) + return false; + + EditorProgress pr("mono_project_debug_build", "Building project solution...", 2); + + pr.step("Building project solution"); + + MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), "Tools"); + if (!GodotSharpBuilds::get_singleton()->build(build_info)) { + GodotSharpBuilds::show_build_error_dialog("Failed to build project solution"); + return false; + } + + pr.step("Done"); + + return true; +} + +GodotSharpBuilds *GodotSharpBuilds::singleton = NULL; + +void GodotSharpBuilds::build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code) { + + BuildProcess *match = builds.getptr(p_build_info); + ERR_FAIL_COND(!match); + + BuildProcess &bp = *match; + bp.on_exit(p_exit_code); +} + +void GodotSharpBuilds::restart_build(MonoBuildTab *p_build_tab) { +} + +void GodotSharpBuilds::stop_build(MonoBuildTab *p_build_tab) { +} + +bool GodotSharpBuilds::build(const MonoBuildInfo &p_build_info) { + + BuildProcess *match = builds.getptr(p_build_info); + + if (match) { + BuildProcess &bp = *match; + bp.start(true); + return bp.exit_code == 0; + } else { + BuildProcess bp = BuildProcess(p_build_info); + bp.start(true); + builds.set(p_build_info, bp); + return bp.exit_code == 0; + } +} + +bool GodotSharpBuilds::build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) { + + BuildProcess *match = builds.getptr(p_build_info); + + if (match) { + BuildProcess &bp = *match; + bp.start(); + return !bp.exited; // failed to start + } else { + BuildProcess bp = BuildProcess(p_build_info, p_callback); + bp.start(); + builds.set(p_build_info, bp); + return !bp.exited; // failed to start + } +} + +GodotSharpBuilds::GodotSharpBuilds() { + + singleton = this; + + EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::build_project_blocking); + + // Build tool settings + EditorSettings *ed_settings = EditorSettings::get_singleton(); + if (!ed_settings->has_setting("mono/builds/build_tool")) { + ed_settings->set_setting("mono/builds/build_tool", +#ifdef WINDOWS_ENABLED + // TODO: Default to MSBUILD_MONO if its csc.exe issue is fixed in the installed mono version + MSBUILD +#else + MSBUILD_MONO +#endif + ); + } + ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM, +#ifdef WINDOWS_ENABLED + "MSBuild (Mono),MSBuild (System)" +#else + "MSBuild (Mono),xbuild (Deprecated)" +#endif + )); +} + +GodotSharpBuilds::~GodotSharpBuilds() { + + singleton = NULL; +} + +void GodotSharpBuilds::BuildProcess::on_exit(int p_exit_code) { + + exited = true; + exit_code = p_exit_code; + build_tab->on_build_exit(p_exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR); + build_instance.unref(); + + if (exit_callback) + exit_callback(exit_code); +} + +void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { + + _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) + + exit_code = -1; + + String logs_dir = GodotSharpDirs::get_build_logs_dir().plus_file(build_info.solution.md5_text() + "_" + build_info.configuration); + + if (build_tab) { + build_tab->on_build_start(); + } else { + build_tab = memnew(MonoBuildTab(build_info, logs_dir)); + MonoBottomPanel::get_singleton()->add_build_tab(build_tab); + } + + if (p_blocking) { + // Required in order to update the build tasks list + Main::iteration(); + } + + if (!exited) { + exited = true; + String message = "Tried to start build process, but it is already running"; + build_tab->on_build_exec_failed(message); + ERR_EXPLAIN(message); + ERR_FAIL(); + } + + exited = false; + + // Remove old issues file + + String issues_file = "msbuild_issues.csv"; + DirAccessRef d = DirAccess::create_for_path(logs_dir); + if (d->file_exists(issues_file)) { + Error err = d->remove(issues_file); + if (err != OK) { + exited = true; + String file_path = ProjectSettings::get_singleton()->localize_path(logs_dir).plus_file(issues_file); + String message = "Cannot remove issues file: " + file_path; + build_tab->on_build_exec_failed(message); + ERR_EXPLAIN(message); + ERR_FAIL(); + } + } + + GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Build", "BuildInstance"); + + MonoObject *mono_object = mono_object_new(mono_domain_get(), klass->get_raw()); + + // Construct + + Variant solution = build_info.solution; + Variant config = build_info.configuration; + + const Variant *ctor_args[2] = { &solution, &config }; + + MonoObject *ex = NULL; + GDMonoMethod *ctor = klass->get_method(".ctor", 2); + ctor->invoke(mono_object, ctor_args, &ex); + + if (ex) { + exited = true; + String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex); + build_tab->on_build_exec_failed(message); + ERR_EXPLAIN(message); + ERR_FAIL(); + } + + // Call Build + + Variant logger_assembly = OS::get_singleton()->get_executable_path().get_base_dir().plus_file(EDITOR_TOOLS_ASSEMBLY_NAME) + ".dll"; + Variant logger_output_dir = logs_dir; + Variant custom_props = build_info.custom_props; + + const Variant *args[3] = { &logger_assembly, &logger_output_dir, &custom_props }; + + ex = NULL; + GDMonoMethod *build_method = klass->get_method(p_blocking ? "Build" : "BuildAsync", 3); + build_method->invoke(mono_object, args, &ex); + + if (ex) { + exited = true; + String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex); + build_tab->on_build_exec_failed(message); + ERR_EXPLAIN(message); + ERR_FAIL(); + } + + // Build returned + + if (p_blocking) { + exited = true; + exit_code = klass->get_field("exitCode")->get_int_value(mono_object); + + if (exit_code != 0 && OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->print(String("MSBuild finished with exit code " + itos(exit_code) + "\n").utf8()); + + build_tab->on_build_exit(exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR); + } else { + build_instance = MonoGCHandle::create_strong(mono_object); + exited = false; + } +} + +GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) { + + build_info = p_build_info; + build_tab = NULL; + exit_callback = p_callback; + exited = true; + exit_code = -1; +} diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h new file mode 100644 index 0000000000..7d2f38a774 --- /dev/null +++ b/modules/mono/editor/godotsharp_builds.h @@ -0,0 +1,101 @@ +/*************************************************************************/ +/* godotsharp_builds.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GODOTSHARP_BUILDS_H +#define GODOTSHARP_BUILDS_H + +#include "mono_bottom_panel.h" +#include "mono_build_info.h" + +typedef void (*GodotSharpBuild_ExitCallback)(int); + +class GodotSharpBuilds { + +private: + struct BuildProcess { + Ref<MonoGCHandle> build_instance; + MonoBuildInfo build_info; + MonoBuildTab *build_tab; + GodotSharpBuild_ExitCallback exit_callback; + bool exited; + int exit_code; + + void on_exit(int p_exit_code); + void start(bool p_blocking = false); + + BuildProcess() {} + BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL); + }; + + HashMap<MonoBuildInfo, BuildProcess, MonoBuildInfo::Hasher> builds; + + static GodotSharpBuilds *singleton; + + friend class GDMono; + static void _register_internal_calls(); + +public: + enum APIType { + API_CORE, + API_EDITOR + }; + + enum BuildTool { + MSBUILD_MONO, +#ifdef WINDOWS_ENABLED + MSBUILD +#else + XBUILD // Deprecated +#endif + }; + + _FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; } + + static void show_build_error_dialog(const String &p_message); + + void build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code); + + void restart_build(MonoBuildTab *p_build_tab); + void stop_build(MonoBuildTab *p_build_tab); + + bool build(const MonoBuildInfo &p_build_info); + bool build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL); + + static bool build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config); + static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name); + + static bool make_api_sln(APIType p_api_type); + + static bool build_project_blocking(); + + GodotSharpBuilds(); + ~GodotSharpBuilds(); +}; + +#endif // GODOTSHARP_BUILDS_H diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp new file mode 100644 index 0000000000..837dbfde66 --- /dev/null +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -0,0 +1,286 @@ +/*************************************************************************/ +/* godotsharp_editor.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "godotsharp_editor.h" + +#include "core/os/os.h" +#include "core/project_settings.h" +#include "scene/gui/control.h" +#include "scene/main/node.h" + +#include "../csharp_script.h" +#include "../godotsharp_dirs.h" +#include "../mono_gd/gd_mono.h" +#include "../utils/path_utils.h" +#include "bindings_generator.h" +#include "csharp_project.h" +#include "net_solution.h" + +#ifdef WINDOWS_ENABLED +#include "../utils/mono_reg_utils.h" +#endif + +GodotSharpEditor *GodotSharpEditor::singleton = NULL; + +bool GodotSharpEditor::_create_project_solution() { + + EditorProgress pr("create_csharp_solution", "Generating solution...", 2); + + pr.step("Generating C# project..."); + + String path = OS::get_singleton()->get_resource_dir(); + String name = ProjectSettings::get_singleton()->get("application/config/name"); + if (name.empty()) { + name = "UnnamedProject"; + } + + String guid = CSharpProject::generate_game_project(path, name); + + if (guid.length()) { + + NETSolution solution(name); + + if (!solution.set_path(path)) { + show_error_dialog("Failed to create solution."); + return false; + } + + Vector<String> extra_configs; + extra_configs.push_back("Tools"); + + solution.add_new_project(name, guid, extra_configs); + + Error sln_error = solution.save(); + + if (sln_error != OK) { + show_error_dialog("Failed to save solution."); + return false; + } + + if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE)) + return false; + + if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR)) + return false; + + pr.step("Done"); + + // Here, after all calls to progress_task_step + call_deferred("_remove_create_sln_menu_option"); + + } else { + show_error_dialog("Failed to create C# project."); + } + + return true; +} + +void GodotSharpEditor::_remove_create_sln_menu_option() { + + menu_popup->remove_item(menu_popup->get_item_index(MENU_CREATE_SLN)); + + if (menu_popup->get_item_count() == 0) + menu_button->hide(); + + bottom_panel_btn->show(); +} + +void GodotSharpEditor::_menu_option_pressed(int p_id) { + + switch (p_id) { + case MENU_CREATE_SLN: { + + _create_project_solution(); + } break; + default: + ERR_FAIL(); + } +} + +void GodotSharpEditor::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_create_project_solution"), &GodotSharpEditor::_create_project_solution); + ClassDB::bind_method(D_METHOD("_remove_create_sln_menu_option"), &GodotSharpEditor::_remove_create_sln_menu_option); + ClassDB::bind_method(D_METHOD("_menu_option_pressed", "id"), &GodotSharpEditor::_menu_option_pressed); +} + +void GodotSharpEditor::show_error_dialog(const String &p_message, const String &p_title) { + + error_dialog->set_title(p_title); + error_dialog->set_text(p_message); + error_dialog->popup_centered_minsize(); +} + +Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { + + ExternalEditor editor = ExternalEditor(int(EditorSettings::get_singleton()->get("mono/editor/external_editor"))); + + switch (editor) { + case EDITOR_CODE: { + List<String> args; + args.push_back(ProjectSettings::get_singleton()->get_resource_path()); + + String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path()); + + if (p_line >= 0) { + args.push_back("-g"); + args.push_back(script_path + ":" + itos(p_line) + ":" + itos(p_col)); + } else { + args.push_back(script_path); + } + + static String program = path_which("code"); + + Error err = OS::get_singleton()->execute(program.length() ? program : "code", args, false); + + if (err != OK) { + ERR_PRINT("GodotSharp: Could not execute external editor"); + return err; + } + } break; + case EDITOR_MONODEVELOP: { + if (!monodevel_instance) + monodevel_instance = memnew(MonoDevelopInstance(GodotSharpDirs::get_project_sln_path())); + + String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path()); + monodevel_instance->execute(script_path); + } break; + default: + return ERR_UNAVAILABLE; + } + + return OK; +} + +bool GodotSharpEditor::overrides_external_editor() { + + return ExternalEditor(int(EditorSettings::get_singleton()->get("mono/editor/external_editor"))) != EDITOR_NONE; +} + +GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { + + singleton = this; + + monodevel_instance = NULL; + + editor = p_editor; + + error_dialog = memnew(AcceptDialog); + editor->get_gui_base()->add_child(error_dialog); + + bottom_panel_btn = editor->add_bottom_panel_item("Mono", memnew(MonoBottomPanel(editor))); + + godotsharp_builds = memnew(GodotSharpBuilds); + + editor->add_child(memnew(MonoReloadNode)); + + menu_button = memnew(MenuButton); + menu_button->set_text("Mono"); + menu_popup = menu_button->get_popup(); + + String sln_path = GodotSharpDirs::get_project_sln_path(); + String csproj_path = GodotSharpDirs::get_project_csproj_path(); + + if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) { + bottom_panel_btn->hide(); + menu_popup->add_item("Create C# solution", MENU_CREATE_SLN); + } + + menu_popup->connect("id_pressed", this, "_menu_option_pressed"); + + if (menu_popup->get_item_count() == 0) + menu_button->hide(); + + editor->get_menu_hb()->add_child(menu_button); + + // External editor settings + EditorSettings *ed_settings = EditorSettings::get_singleton(); + if (!ed_settings->has_setting("mono/editor/external_editor")) { + ed_settings->set_setting("mono/editor/external_editor", EDITOR_NONE); + } + ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, "None,MonoDevelop,Visual Studio Code")); +} + +GodotSharpEditor::~GodotSharpEditor() { + + singleton = NULL; + + memdelete(godotsharp_builds); + + if (monodevel_instance) { + memdelete(monodevel_instance); + monodevel_instance = NULL; + } +} + +MonoReloadNode *MonoReloadNode::singleton = NULL; + +void MonoReloadNode::_reload_timer_timeout() { + + CSharpLanguage::get_singleton()->reload_assemblies_if_needed(false); +} + +void MonoReloadNode::restart_reload_timer() { + + reload_timer->stop(); + reload_timer->start(); +} + +void MonoReloadNode::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_reload_timer_timeout"), &MonoReloadNode::_reload_timer_timeout); +} + +void MonoReloadNode::_notification(int p_what) { + switch (p_what) { + case MainLoop::NOTIFICATION_WM_FOCUS_IN: { + restart_reload_timer(); + CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true); + } break; + default: { + } break; + }; +} + +MonoReloadNode::MonoReloadNode() { + + singleton = this; + + reload_timer = memnew(Timer); + add_child(reload_timer); + reload_timer->set_one_shot(false); + reload_timer->set_wait_time(EDITOR_DEF("mono/assembly_watch_interval_sec", 0.5)); + reload_timer->connect("timeout", this, "_reload_timer_timeout"); + reload_timer->start(); +} + +MonoReloadNode::~MonoReloadNode() { + + singleton = NULL; +} diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h new file mode 100644 index 0000000000..0f2c163582 --- /dev/null +++ b/modules/mono/editor/godotsharp_editor.h @@ -0,0 +1,109 @@ +/*************************************************************************/ +/* godotsharp_editor.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GODOTSHARP_EDITOR_H +#define GODOTSHARP_EDITOR_H + +#include "godotsharp_builds.h" + +#include "monodevelop_instance.h" + +class GodotSharpEditor : public Node { + GDCLASS(GodotSharpEditor, Object) + + EditorNode *editor; + + MenuButton *menu_button; + PopupMenu *menu_popup; + + AcceptDialog *error_dialog; + + ToolButton *bottom_panel_btn; + + GodotSharpBuilds *godotsharp_builds; + + MonoDevelopInstance *monodevel_instance; + + bool _create_project_solution(); + + void _remove_create_sln_menu_option(); + + void _menu_option_pressed(int p_id); + + static GodotSharpEditor *singleton; + +protected: + static void _bind_methods(); + +public: + enum MenuOptions { + MENU_CREATE_SLN + }; + + enum ExternalEditor { + EDITOR_NONE, + EDITOR_MONODEVELOP, + EDITOR_CODE, + }; + + _FORCE_INLINE_ static GodotSharpEditor *get_singleton() { return singleton; } + + void show_error_dialog(const String &p_message, const String &p_title = "Error"); + + Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col); + bool overrides_external_editor(); + + GodotSharpEditor(EditorNode *p_editor); + ~GodotSharpEditor(); +}; + +class MonoReloadNode : public Node { + GDCLASS(MonoReloadNode, Node) + + Timer *reload_timer; + + void _reload_timer_timeout(); + + static MonoReloadNode *singleton; + +protected: + static void _bind_methods(); + + void _notification(int p_what); + +public: + _FORCE_INLINE_ static MonoReloadNode *get_singleton() { return singleton; } + + void restart_reload_timer(); + + MonoReloadNode(); + ~MonoReloadNode(); +}; + +#endif // GODOTSHARP_EDITOR_H diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp new file mode 100644 index 0000000000..31dc09856a --- /dev/null +++ b/modules/mono/editor/mono_bottom_panel.cpp @@ -0,0 +1,458 @@ +/*************************************************************************/ +/* mono_bottom_panel.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "mono_bottom_panel.h" + +#include "../csharp_script.h" +#include "godotsharp_editor.h" + +MonoBottomPanel *MonoBottomPanel::singleton = NULL; + +void MonoBottomPanel::_update_build_tabs_list() { + + build_tabs_list->clear(); + + int current_tab = build_tabs->get_current_tab(); + + bool no_current_tab = current_tab < 0 || current_tab >= build_tabs->get_tab_count(); + + for (int i = 0; i < build_tabs->get_child_count(); i++) { + + MonoBuildTab *tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(i)); + + if (tab) { + String item_name = tab->build_info.solution.get_file().get_basename(); + item_name += " [" + tab->build_info.configuration + "]"; + + build_tabs_list->add_item(item_name, tab->get_icon_texture()); + + String item_tooltip = String("Solution: ") + tab->build_info.solution; + item_tooltip += String("\nConfiguration: ") + tab->build_info.configuration; + item_tooltip += String("\nStatus: "); + + if (tab->build_exited) { + item_tooltip += tab->build_result == MonoBuildTab::RESULT_SUCCESS ? "Succeeded" : "Errored"; + } else { + item_tooltip += "Running"; + } + + if (!tab->build_exited || !tab->build_result == MonoBuildTab::RESULT_SUCCESS) { + item_tooltip += "\nErrors: " + itos(tab->error_count); + } + + item_tooltip += "\nWarnings: " + itos(tab->warning_count); + + build_tabs_list->set_item_tooltip(i, item_tooltip); + + if (no_current_tab || current_tab == i) { + build_tabs_list->select(i); + _build_tab_item_selected(i); + } + } + } +} + +void MonoBottomPanel::add_build_tab(MonoBuildTab *p_build_tab) { + + build_tabs->add_child(p_build_tab); + raise_build_tab(p_build_tab); +} + +void MonoBottomPanel::raise_build_tab(MonoBuildTab *p_build_tab) { + + ERR_FAIL_COND(p_build_tab->get_parent() != build_tabs); + build_tabs->move_child(p_build_tab, 0); + _update_build_tabs_list(); +} + +void MonoBottomPanel::show_build_tab() { + + for (int i = 0; i < panel_tabs->get_tab_count(); i++) { + if (panel_tabs->get_tab_control(i) == panel_builds_tab) { + panel_tabs->set_current_tab(i); + editor->make_bottom_panel_item_visible(this); + return; + } + } + + ERR_PRINT("Builds tab not found"); +} + +void MonoBottomPanel::_build_tab_item_selected(int p_idx) { + + ERR_FAIL_INDEX(p_idx, build_tabs->get_tab_count()); + build_tabs->set_current_tab(p_idx); +} + +void MonoBottomPanel::_build_tab_changed(int p_idx) { + + if (p_idx < 0 || p_idx >= build_tabs->get_tab_count()) { + warnings_btn->set_visible(false); + errors_btn->set_visible(false); + } else { + warnings_btn->set_visible(true); + errors_btn->set_visible(true); + } +} + +void MonoBottomPanel::_warnings_toggled(bool p_pressed) { + + int current_tab = build_tabs->get_current_tab(); + ERR_FAIL_INDEX(current_tab, build_tabs->get_tab_count()); + MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(current_tab)); + build_tab->warnings_visible = p_pressed; + build_tab->_update_issues_list(); +} + +void MonoBottomPanel::_errors_toggled(bool p_pressed) { + + int current_tab = build_tabs->get_current_tab(); + ERR_FAIL_INDEX(current_tab, build_tabs->get_tab_count()); + MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(current_tab)); + build_tab->errors_visible = p_pressed; + build_tab->_update_issues_list(); +} + +void MonoBottomPanel::_build_project_pressed() { + + GodotSharpBuilds::get_singleton()->build_project_blocking(); + + MonoReloadNode::get_singleton()->restart_reload_timer(); + CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true); +} + +void MonoBottomPanel::_notification(int p_what) { + + switch (p_what) { + + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + panel_tabs->add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles")); + panel_tabs->add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles")); + panel_tabs->add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles")); + } break; + } +} + +void MonoBottomPanel::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_build_project_pressed"), &MonoBottomPanel::_build_project_pressed); + ClassDB::bind_method(D_METHOD("_warnings_toggled", "pressed"), &MonoBottomPanel::_warnings_toggled); + ClassDB::bind_method(D_METHOD("_errors_toggled", "pressed"), &MonoBottomPanel::_errors_toggled); + ClassDB::bind_method(D_METHOD("_build_tab_item_selected", "idx"), &MonoBottomPanel::_build_tab_item_selected); + ClassDB::bind_method(D_METHOD("_build_tab_changed", "idx"), &MonoBottomPanel::_build_tab_changed); +} + +MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) { + + singleton = this; + + editor = p_editor; + + set_v_size_flags(SIZE_EXPAND_FILL); + set_anchors_and_margins_preset(Control::PRESET_WIDE); + + panel_tabs = memnew(TabContainer); + panel_tabs->set_tab_align(TabContainer::ALIGN_LEFT); + panel_tabs->add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles")); + panel_tabs->add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles")); + panel_tabs->add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles")); + panel_tabs->set_custom_minimum_size(Size2(0, 228) * EDSCALE); + panel_tabs->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(panel_tabs); + + { // Builds + panel_builds_tab = memnew(VBoxContainer); + panel_builds_tab->set_name(TTR("Builds")); + panel_builds_tab->set_h_size_flags(SIZE_EXPAND_FILL); + panel_tabs->add_child(panel_builds_tab); + + HBoxContainer *toolbar_hbc = memnew(HBoxContainer); + toolbar_hbc->set_h_size_flags(SIZE_EXPAND_FILL); + panel_builds_tab->add_child(toolbar_hbc); + + ToolButton *build_project_btn = memnew(ToolButton); + build_project_btn->set_text("Build Project"); + build_project_btn->set_focus_mode(FOCUS_NONE); + build_project_btn->connect("pressed", this, "_build_project_pressed"); + toolbar_hbc->add_child(build_project_btn); + + toolbar_hbc->add_spacer(); + + warnings_btn = memnew(ToolButton); + warnings_btn->set_text("Warnings"); + warnings_btn->set_toggle_mode(true); + warnings_btn->set_pressed(true); + warnings_btn->set_visible(false); + warnings_btn->set_focus_mode(FOCUS_NONE); + warnings_btn->connect("toggled", this, "_warnings_toggled"); + toolbar_hbc->add_child(warnings_btn); + + errors_btn = memnew(ToolButton); + errors_btn->set_text("Errors"); + errors_btn->set_toggle_mode(true); + errors_btn->set_pressed(true); + errors_btn->set_visible(false); + errors_btn->set_focus_mode(FOCUS_NONE); + errors_btn->connect("toggled", this, "_errors_toggled"); + toolbar_hbc->add_child(errors_btn); + + HSplitContainer *hsc = memnew(HSplitContainer); + hsc->set_h_size_flags(SIZE_EXPAND_FILL); + hsc->set_v_size_flags(SIZE_EXPAND_FILL); + panel_builds_tab->add_child(hsc); + + build_tabs_list = memnew(ItemList); + build_tabs_list->set_h_size_flags(SIZE_EXPAND_FILL); + build_tabs_list->connect("item_selected", this, "_build_tab_item_selected"); + hsc->add_child(build_tabs_list); + + build_tabs = memnew(TabContainer); + build_tabs->set_tab_align(TabContainer::ALIGN_LEFT); + build_tabs->set_h_size_flags(SIZE_EXPAND_FILL); + build_tabs->set_tabs_visible(false); + build_tabs->connect("tab_changed", this, "_build_tab_changed"); + hsc->add_child(build_tabs); + } +} + +MonoBottomPanel::~MonoBottomPanel() { + + singleton = NULL; +} + +void MonoBuildTab::_load_issues_from_file(const String &p_csv_file) { + + FileAccessRef f = FileAccess::open(p_csv_file, FileAccess::READ); + + if (!f) + return; + + while (!f->eof_reached()) { + Vector<String> csv_line = f->get_csv_line(); + + if (csv_line.size() == 1 && csv_line[0].empty()) + return; + + ERR_CONTINUE(csv_line.size() != 7); + + BuildIssue issue; + issue.warning = csv_line[0] == "warning"; + issue.file = csv_line[1]; + issue.line = csv_line[2].to_int(); + issue.column = csv_line[3].to_int(); + issue.code = csv_line[4]; + issue.message = csv_line[5]; + issue.project_file = csv_line[6]; + + if (issue.warning) + warning_count += 1; + else + error_count += 1; + + issues.push_back(issue); + } +} + +void MonoBuildTab::_update_issues_list() { + + issues_list->clear(); + + Ref<Texture> warning_icon = get_icon("Warning", "EditorIcons"); + Ref<Texture> error_icon = get_icon("Error", "EditorIcons"); + + for (int i = 0; i < issues.size(); i++) { + + const BuildIssue &issue = issues[i]; + + if (!(issue.warning ? warnings_visible : errors_visible)) + continue; + + String tooltip; + tooltip += String("Message: ") + issue.message; + + if (issue.code.length()) { + tooltip += String("\nCode: ") + issue.code; + } + + tooltip += String("\nType: ") + (issue.warning ? "warning" : "error"); + + String text; + + if (issue.file.length()) { + String sline = String::num_int64(issue.line); + String scolumn = String::num_int64(issue.column); + + text += issue.file + "("; + text += sline + ","; + text += scolumn + "): "; + + tooltip += "\nFile: " + issue.file; + tooltip += "\nLine: " + sline; + tooltip += "\nColumn: " + scolumn; + } + + if (issue.project_file.length()) { + tooltip += "\nProject: " + issue.project_file; + } + + text += issue.message; + + int line_break_idx = text.find("\n"); + issues_list->add_item(line_break_idx == -1 ? text : text.substr(0, line_break_idx), + issue.warning ? warning_icon : error_icon); + int index = issues_list->get_item_count() - 1; + issues_list->set_item_tooltip(index, tooltip); + issues_list->set_item_metadata(index, i); + } +} + +Ref<Texture> MonoBuildTab::get_icon_texture() const { + + // FIXME these icons were removed... find something better + + if (build_exited) { + if (build_result == RESULT_ERROR) { + return get_icon("DependencyChangedHl", "EditorIcons"); + } else { + return get_icon("DependencyOkHl", "EditorIcons"); + } + } else { + return get_icon("GraphTime", "EditorIcons"); + } +} + +MonoBuildInfo MonoBuildTab::get_build_info() { + + return build_info; +} + +void MonoBuildTab::on_build_start() { + + build_exited = false; + + issues.clear(); + warning_count = 0; + error_count = 0; + _update_issues_list(); + + MonoBottomPanel::get_singleton()->raise_build_tab(this); +} + +void MonoBuildTab::on_build_exit(BuildResult result) { + + build_exited = true; + build_result = result; + + _load_issues_from_file(logs_dir.plus_file("msbuild_issues.csv")); + _update_issues_list(); + + MonoBottomPanel::get_singleton()->raise_build_tab(this); +} + +void MonoBuildTab::on_build_exec_failed(const String &p_cause) { + + build_exited = true; + build_result = RESULT_ERROR; + + issues_list->clear(); + + BuildIssue issue; + issue.message = p_cause; + issue.warning = false; + + error_count += 1; + issues.push_back(issue); + + _update_issues_list(); + + MonoBottomPanel::get_singleton()->raise_build_tab(this); +} + +void MonoBuildTab::restart_build() { + + ERR_FAIL_COND(!build_exited); + GodotSharpBuilds::get_singleton()->restart_build(this); +} + +void MonoBuildTab::stop_build() { + + ERR_FAIL_COND(build_exited); + GodotSharpBuilds::get_singleton()->stop_build(this); +} + +void MonoBuildTab::_issue_activated(int p_idx) { + + ERR_FAIL_INDEX(p_idx, issues.size()); + + const BuildIssue &issue = issues[p_idx]; + + if (issue.project_file.empty() && issue.file.empty()) + return; + + String project_dir = issue.project_file.length() ? issue.project_file.get_base_dir() : build_info.solution.get_base_dir(); + + String file = project_dir.simplify_path().plus_file(issue.file.simplify_path()); + + if (!FileAccess::exists(file)) + return; + + file = ProjectSettings::get_singleton()->localize_path(file); + + if (file.begins_with("res://")) { + Ref<Script> script = ResourceLoader::load(file, CSharpLanguage::get_singleton()->get_type()); + + if (script.is_valid() && ScriptEditor::get_singleton()->edit(script, issue.line, issue.column)) { + EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT); + } + } +} + +void MonoBuildTab::_bind_methods() { + + ClassDB::bind_method("_issue_activated", &MonoBuildTab::_issue_activated); +} + +MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) { + + build_info = p_build_info; + logs_dir = p_logs_dir; + + build_exited = false; + + issues_list = memnew(ItemList); + issues_list->set_v_size_flags(SIZE_EXPAND_FILL); + issues_list->connect("item_activated", this, "_issue_activated"); + add_child(issues_list); + + error_count = 0; + warning_count = 0; + + errors_visible = true; + warnings_visible = true; +} diff --git a/modules/mono/editor/mono_bottom_panel.h b/modules/mono/editor/mono_bottom_panel.h new file mode 100644 index 0000000000..5cc4aa3240 --- /dev/null +++ b/modules/mono/editor/mono_bottom_panel.h @@ -0,0 +1,147 @@ +/*************************************************************************/ +/* mono_bottom_panel.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 MONO_BOTTOM_PANEL_H +#define MONO_BOTTOM_PANEL_H + +#include "editor/editor_node.h" +#include "scene/gui/control.h" + +#include "mono_build_info.h" + +class MonoBuildTab; + +class MonoBottomPanel : public VBoxContainer { + + GDCLASS(MonoBottomPanel, VBoxContainer) + + EditorNode *editor; + + TabContainer *panel_tabs; + + VBoxContainer *panel_builds_tab; + + ItemList *build_tabs_list; + TabContainer *build_tabs; + + Button *warnings_btn; + Button *errors_btn; + + void _update_build_tabs_list(); + + void _build_tab_item_selected(int p_idx); + void _build_tab_changed(int p_idx); + + void _warnings_toggled(bool p_pressed); + void _errors_toggled(bool p_pressed); + + void _build_project_pressed(); + + static MonoBottomPanel *singleton; + +protected: + void _notification(int p_what); + + static void _bind_methods(); + +public: + _FORCE_INLINE_ static MonoBottomPanel *get_singleton() { return singleton; } + + void add_build_tab(MonoBuildTab *p_build_tab); + void raise_build_tab(MonoBuildTab *p_build_tab); + + void show_build_tab(); + + MonoBottomPanel(EditorNode *p_editor = NULL); + ~MonoBottomPanel(); +}; + +class MonoBuildTab : public VBoxContainer { + + GDCLASS(MonoBuildTab, VBoxContainer) + +public: + enum BuildResult { + RESULT_ERROR, + RESULT_SUCCESS + }; + + struct BuildIssue { + bool warning; + String file; + int line; + int column; + String code; + String message; + String project_file; + }; + +private: + friend class MonoBottomPanel; + + bool build_exited; + BuildResult build_result; + + Vector<BuildIssue> issues; + ItemList *issues_list; + + int error_count; + int warning_count; + + bool errors_visible; + bool warnings_visible; + + String logs_dir; + + MonoBuildInfo build_info; + + void _load_issues_from_file(const String &p_csv_file); + void _update_issues_list(); + + void _issue_activated(int p_idx); + +protected: + static void _bind_methods(); + +public: + Ref<Texture> get_icon_texture() const; + + MonoBuildInfo get_build_info(); + + void on_build_start(); + void on_build_exit(BuildResult result); + void on_build_exec_failed(const String &p_cause); + + void restart_build(); + void stop_build(); + + MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir); +}; + +#endif // MONO_BOTTOM_PANEL_H diff --git a/modules/mono/editor/mono_build_info.h b/modules/mono/editor/mono_build_info.h new file mode 100644 index 0000000000..f3b3e43b6d --- /dev/null +++ b/modules/mono/editor/mono_build_info.h @@ -0,0 +1,64 @@ +/*************************************************************************/ +/* mono_build_info.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 MONO_BUILD_INFO_H +#define MONO_BUILD_INFO_H + +#include "../mono_gd/gd_mono_utils.h" + +struct MonoBuildInfo { + + struct Hasher { + static _FORCE_INLINE_ uint32_t hash(const MonoBuildInfo &p_key) { + uint32_t hash = 0; + + GDMonoUtils::hash_combine(hash, p_key.solution.hash()); + GDMonoUtils::hash_combine(hash, p_key.configuration.hash()); + + return hash; + } + }; + + String solution; + String configuration; + Vector<String> custom_props; + + MonoBuildInfo() {} + + MonoBuildInfo(const String &p_solution, const String &p_config) { + solution = p_solution; + configuration = p_config; + } + + bool operator==(const MonoBuildInfo &p_b) const { + return p_b.solution == solution && p_b.configuration == configuration; + } +}; + +#endif // MONO_BUILD_INFO_H diff --git a/modules/mono/editor/monodevelop_instance.cpp b/modules/mono/editor/monodevelop_instance.cpp new file mode 100644 index 0000000000..a34d82ffcb --- /dev/null +++ b/modules/mono/editor/monodevelop_instance.cpp @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* monodevelop_instance.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "monodevelop_instance.h" + +#include "../mono_gd/gd_mono.h" +#include "../mono_gd/gd_mono_class.h" + +void MonoDevelopInstance::execute(const Vector<String> &p_files) { + + ERR_FAIL_NULL(execute_method); + ERR_FAIL_COND(gc_handle.is_null()); + + MonoObject *ex = NULL; + + Variant files = p_files; + const Variant *args[1] = { &files }; + execute_method->invoke(gc_handle->get_target(), args, &ex); + + if (ex) { + mono_print_unhandled_exception(ex); + ERR_FAIL(); + } +} + +void MonoDevelopInstance::execute(const String &p_file) { + + Vector<String> files; + files.push_back(p_file); + execute(files); +} + +MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) { + + _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) + + GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Editor", "MonoDevelopInstance"); + + MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_raw()); + + GDMonoMethod *ctor = klass->get_method(".ctor", 1); + MonoObject *ex = NULL; + + Variant solution = p_solution; + const Variant *args[1] = { &solution }; + ctor->invoke(obj, args, &ex); + + if (ex) { + mono_print_unhandled_exception(ex); + ERR_FAIL(); + } + + gc_handle = MonoGCHandle::create_strong(obj); + execute_method = klass->get_method("Execute", 1); +} diff --git a/modules/mono/editor/monodevelop_instance.h b/modules/mono/editor/monodevelop_instance.h new file mode 100644 index 0000000000..9eb154eba1 --- /dev/null +++ b/modules/mono/editor/monodevelop_instance.h @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* monodevelop_instance.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 MONODEVELOP_INSTANCE_H +#define MONODEVELOP_INSTANCE_H + +#include "reference.h" + +#include "../mono_gc_handle.h" +#include "../mono_gd/gd_mono_method.h" + +class MonoDevelopInstance { + + Ref<MonoGCHandle> gc_handle; + GDMonoMethod *execute_method; + +public: + void execute(const Vector<String> &p_files); + void execute(const String &p_files); + + MonoDevelopInstance(const String &p_solution); +}; + +#endif // MONODEVELOP_INSTANCE_H diff --git a/modules/mono/editor/net_solution.cpp b/modules/mono/editor/net_solution.cpp new file mode 100644 index 0000000000..fa60c310db --- /dev/null +++ b/modules/mono/editor/net_solution.cpp @@ -0,0 +1,130 @@ +/*************************************************************************/ +/* net_solution.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "net_solution.h" + +#include "os/dir_access.h" +#include "os/file_access.h" + +#include "../utils/path_utils.h" +#include "../utils/string_utils.h" +#include "csharp_project.h" + +#define SOLUTION_TEMPLATE \ + "Microsoft Visual Studio Solution File, Format Version 12.00\n" \ + "# Visual Studio 2012\n" \ + "%0\n" \ + "Global\n" \ + "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n" \ + "%1\n" \ + "\tEndGlobalSection\n" \ + "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n" \ + "%2\n" \ + "\tEndGlobalSection\n" \ + "EndGlobal\n" + +#define PROJECT_DECLARATION "Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"%0\", \"%1\", \"{%2}\"\nEndProject" + +#define SOLUTION_PLATFORMS_CONFIG "\t\%0|Any CPU = %0|Any CPU" + +#define PROJECT_PLATFORMS_CONFIG \ + "\t\t{%0}.%1|Any CPU.ActiveCfg = %1|Any CPU\n" \ + "\t\t{%0}.%1|Any CPU.Build.0 = %1|Any CPU" + +void NETSolution::add_new_project(const String &p_name, const String &p_guid, const Vector<String> &p_extra_configs) { + if (projects.has(p_name)) + WARN_PRINT("Overriding existing project."); + + ProjectInfo procinfo; + procinfo.guid = p_guid; + + procinfo.configs.push_back("Debug"); + procinfo.configs.push_back("Release"); + + for (int i = 0; i < p_extra_configs.size(); i++) { + procinfo.configs.push_back(p_extra_configs[i]); + } + + projects[p_name] = procinfo; +} + +Error NETSolution::save() { + bool dir_exists = DirAccess::exists(path); + ERR_EXPLAIN("The directory does not exist."); + ERR_FAIL_COND_V(!dir_exists, ERR_FILE_BAD_PATH); + + String projs_decl; + String sln_platform_cfg; + String proj_platform_cfg; + + for (Map<String, ProjectInfo>::Element *E = projects.front(); E; E = E->next()) { + const String &name = E->key(); + const ProjectInfo &procinfo = E->value(); + + projs_decl += sformat(PROJECT_DECLARATION, name, name + ".csproj", procinfo.guid); + + for (int i = 0; i < procinfo.configs.size(); i++) { + const String &config = procinfo.configs[i]; + + if (i != 0) { + sln_platform_cfg += "\n"; + proj_platform_cfg += "\n"; + } + + sln_platform_cfg += sformat(SOLUTION_PLATFORMS_CONFIG, config); + proj_platform_cfg += sformat(PROJECT_PLATFORMS_CONFIG, procinfo.guid, config); + } + } + + String content = sformat(SOLUTION_TEMPLATE, projs_decl, sln_platform_cfg, proj_platform_cfg); + + FileAccessRef file = FileAccess::open(path_join(path, name + ".sln"), FileAccess::WRITE); + ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE); + file->store_string(content); + file->close(); + + return OK; +} + +bool NETSolution::set_path(const String &p_existing_path) { + if (p_existing_path.is_abs_path()) { + path = p_existing_path; + } else { + String abspath; + if (!rel_path_to_abs(p_existing_path, abspath)) + return false; + path = abspath; + } + + return true; +} + +NETSolution::NETSolution(const String &p_name) { + name = p_name; +} diff --git a/modules/mono/editor/net_solution.h b/modules/mono/editor/net_solution.h new file mode 100644 index 0000000000..d7ccebb7df --- /dev/null +++ b/modules/mono/editor/net_solution.h @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* net_solution.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 NET_SOLUTION_H +#define NET_SOLUTION_H + +#include "map.h" +#include "ustring.h" + +struct NETSolution { + String name; + + void add_new_project(const String &p_name, const String &p_guid, const Vector<String> &p_extra_configs = Vector<String>()); + + Error save(); + + bool set_path(const String &p_existing_path); + + NETSolution(const String &p_name); + +private: + struct ProjectInfo { + String guid; + Vector<String> configs; + }; + + String path; + Map<String, ProjectInfo> projects; +}; + +#endif // NET_SOLUTION_H diff --git a/modules/mono/glue/cs_files/Basis.cs b/modules/mono/glue/cs_files/Basis.cs new file mode 100644 index 0000000000..c50e783349 --- /dev/null +++ b/modules/mono/glue/cs_files/Basis.cs @@ -0,0 +1,520 @@ +using System; +using System.Runtime.InteropServices; + +namespace Godot +{ + [StructLayout(LayoutKind.Sequential)] + public struct Basis : IEquatable<Basis> + { + private static readonly Basis identity = new Basis + ( + new Vector3(1f, 0f, 0f), + new Vector3(0f, 1f, 0f), + new Vector3(0f, 0f, 1f) + ); + + private static readonly Basis[] orthoBases = new Basis[24] + { + new Basis(1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f), + new Basis(0f, -1f, 0f, 1f, 0f, 0f, 0f, 0f, 1f), + new Basis(-1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f), + new Basis(0f, 1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f), + new Basis(1f, 0f, 0f, 0f, 0f, -1f, 0f, 1f, 0f), + new Basis(0f, 0f, 1f, 1f, 0f, 0f, 0f, 1f, 0f), + new Basis(-1f, 0f, 0f, 0f, 0f, 1f, 0f, 1f, 0f), + new Basis(0f, 0f, -1f, -1f, 0f, 0f, 0f, 1f, 0f), + new Basis(1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, -1f), + new Basis(0f, 1f, 0f, 1f, 0f, 0f, 0f, 0f, -1f), + new Basis(-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, -1f), + new Basis(0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, -1f), + new Basis(1f, 0f, 0f, 0f, 0f, 1f, 0f, -1f, 0f), + new Basis(0f, 0f, -1f, 1f, 0f, 0f, 0f, -1f, 0f), + new Basis(-1f, 0f, 0f, 0f, 0f, -1f, 0f, -1f, 0f), + new Basis(0f, 0f, 1f, -1f, 0f, 0f, 0f, -1f, 0f), + new Basis(0f, 0f, 1f, 0f, 1f, 0f, -1f, 0f, 0f), + new Basis(0f, -1f, 0f, 0f, 0f, 1f, -1f, 0f, 0f), + new Basis(0f, 0f, -1f, 0f, -1f, 0f, -1f, 0f, 0f), + new Basis(0f, 1f, 0f, 0f, 0f, -1f, -1f, 0f, 0f), + new Basis(0f, 0f, 1f, 0f, -1f, 0f, 1f, 0f, 0f), + new Basis(0f, 1f, 0f, 0f, 0f, 1f, 1f, 0f, 0f), + new Basis(0f, 0f, -1f, 0f, 1f, 0f, 1f, 0f, 0f), + new Basis(0f, -1f, 0f, 0f, 0f, -1f, 1f, 0f, 0f) + }; + + public Vector3 x; + public Vector3 y; + public Vector3 z; + + public static Basis Identity + { + get { return identity; } + } + + public Vector3 Scale + { + get + { + return new Vector3 + ( + new Vector3(this[0, 0], this[1, 0], this[2, 0]).length(), + new Vector3(this[0, 1], this[1, 1], this[2, 1]).length(), + new Vector3(this[0, 2], this[1, 2], this[2, 2]).length() + ); + } + } + + public Vector3 this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + case 2: + z = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + public float this[int index, int axis] + { + get + { + switch (index) + { + case 0: + return x[axis]; + case 1: + return y[axis]; + case 2: + return z[axis]; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x[axis] = value; + return; + case 1: + y[axis] = value; + return; + case 2: + z[axis] = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + internal static Basis create_from_axes(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis) + { + return new Basis + ( + new Vector3(xAxis.x, yAxis.x, zAxis.x), + new Vector3(xAxis.y, yAxis.y, zAxis.y), + new Vector3(xAxis.z, yAxis.z, zAxis.z) + ); + } + + public float determinant() + { + return this[0, 0] * (this[1, 1] * this[2, 2] - this[2, 1] * this[1, 2]) - + this[1, 0] * (this[0, 1] * this[2, 2] - this[2, 1] * this[0, 2]) + + this[2, 0] * (this[0, 1] * this[1, 2] - this[1, 1] * this[0, 2]); + } + + public Vector3 get_axis(int axis) + { + return new Vector3(this[0, axis], this[1, axis], this[2, axis]); + } + + public Vector3 get_euler() + { + Basis m = this.orthonormalized(); + + Vector3 euler; + euler.z = 0.0f; + + float mxy = m.y[2]; + + + if (mxy < 1.0f) + { + if (mxy > -1.0f) + { + euler.x = Mathf.asin(-mxy); + euler.y = Mathf.atan2(m.x[2], m.z[2]); + euler.z = Mathf.atan2(m.y[0], m.y[1]); + } + else + { + euler.x = Mathf.PI * 0.5f; + euler.y = -Mathf.atan2(-m.x[1], m.x[0]); + } + } + else + { + euler.x = -Mathf.PI * 0.5f; + euler.y = -Mathf.atan2(m.x[1], m.x[0]); + } + + return euler; + } + + public int get_orthogonal_index() + { + Basis orth = this; + + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + float v = orth[i, j]; + + if (v > 0.5f) + v = 1.0f; + else if (v < -0.5f) + v = -1.0f; + else + v = 0f; + + orth[i, j] = v; + } + } + + for (int i = 0; i < 24; i++) + { + if (orthoBases[i] == orth) + return i; + } + + return 0; + } + + public Basis inverse() + { + Basis inv = this; + + float[] co = new float[3] + { + inv[1, 1] * inv[2, 2] - inv[1, 2] * inv[2, 1], + inv[1, 2] * inv[2, 0] - inv[1, 0] * inv[2, 2], + inv[1, 0] * inv[2, 1] - inv[1, 1] * inv[2, 0] + }; + + float det = inv[0, 0] * co[0] + inv[0, 1] * co[1] + inv[0, 2] * co[2]; + + if (det == 0) + { + return new Basis + ( + float.NaN, float.NaN, float.NaN, + float.NaN, float.NaN, float.NaN, + float.NaN, float.NaN, float.NaN + ); + } + + float s = 1.0f / det; + + inv = new Basis + ( + co[0] * s, + inv[0, 2] * inv[2, 1] - inv[0, 1] * inv[2, 2] * s, + inv[0, 1] * inv[1, 2] - inv[0, 2] * inv[1, 1] * s, + co[1] * s, + inv[0, 0] * inv[2, 2] - inv[0, 2] * inv[2, 0] * s, + inv[0, 2] * inv[1, 0] - inv[0, 0] * inv[1, 2] * s, + co[2] * s, + inv[0, 1] * inv[2, 0] - inv[0, 0] * inv[2, 1] * s, + inv[0, 0] * inv[1, 1] - inv[0, 1] * inv[1, 0] * s + ); + + return inv; + } + + public Basis orthonormalized() + { + Vector3 xAxis = get_axis(0); + Vector3 yAxis = get_axis(1); + Vector3 zAxis = get_axis(2); + + xAxis.normalize(); + yAxis = (yAxis - xAxis * (xAxis.dot(yAxis))); + yAxis.normalize(); + zAxis = (zAxis - xAxis * (xAxis.dot(zAxis)) - yAxis * (yAxis.dot(zAxis))); + zAxis.normalize(); + + return Basis.create_from_axes(xAxis, yAxis, zAxis); + } + + public Basis rotated(Vector3 axis, float phi) + { + return new Basis(axis, phi) * this; + } + + public Basis scaled(Vector3 scale) + { + Basis m = this; + + m[0, 0] *= scale.x; + m[0, 1] *= scale.x; + m[0, 2] *= scale.x; + m[1, 0] *= scale.y; + m[1, 1] *= scale.y; + m[1, 2] *= scale.y; + m[2, 0] *= scale.z; + m[2, 1] *= scale.z; + m[2, 2] *= scale.z; + + return m; + } + + public float tdotx(Vector3 with) + { + return this[0, 0] * with[0] + this[1, 0] * with[1] + this[2, 0] * with[2]; + } + + public float tdoty(Vector3 with) + { + return this[0, 1] * with[0] + this[1, 1] * with[1] + this[2, 1] * with[2]; + } + + public float tdotz(Vector3 with) + { + return this[0, 2] * with[0] + this[1, 2] * with[1] + this[2, 2] * with[2]; + } + + public Basis transposed() + { + Basis tr = this; + + float temp = this[0, 1]; + this[0, 1] = this[1, 0]; + this[1, 0] = temp; + + temp = this[0, 2]; + this[0, 2] = this[2, 0]; + this[2, 0] = temp; + + temp = this[1, 2]; + this[1, 2] = this[2, 1]; + this[2, 1] = temp; + + return tr; + } + + public Vector3 xform(Vector3 v) + { + return new Vector3 + ( + this[0].dot(v), + this[1].dot(v), + this[2].dot(v) + ); + } + + public Vector3 xform_inv(Vector3 v) + { + return new Vector3 + ( + (this[0, 0] * v.x) + (this[1, 0] * v.y) + (this[2, 0] * v.z), + (this[0, 1] * v.x) + (this[1, 1] * v.y) + (this[2, 1] * v.z), + (this[0, 2] * v.x) + (this[1, 2] * v.y) + (this[2, 2] * v.z) + ); + } + + public Quat Quat() { + float trace = x[0] + y[1] + z[2]; + + if (trace > 0.0f) { + float s = Mathf.sqrt(trace + 1.0f) * 2f; + float inv_s = 1f / s; + return new Quat( + (z[1] - y[2]) * inv_s, + (x[2] - z[0]) * inv_s, + (y[0] - x[1]) * inv_s, + s * 0.25f + ); + } else if (x[0] > y[1] && x[0] > z[2]) { + float s = Mathf.sqrt(x[0] - y[1] - z[2] + 1.0f) * 2f; + float inv_s = 1f / s; + return new Quat( + s * 0.25f, + (x[1] + y[0]) * inv_s, + (x[2] + z[0]) * inv_s, + (z[1] - y[2]) * inv_s + ); + } else if (y[1] > z[2]) { + float s = Mathf.sqrt(-x[0] + y[1] - z[2] + 1.0f) * 2f; + float inv_s = 1f / s; + return new Quat( + (x[1] + y[0]) * inv_s, + s * 0.25f, + (y[2] + z[1]) * inv_s, + (x[2] - z[0]) * inv_s + ); + } else { + float s = Mathf.sqrt(-x[0] - y[1] + z[2] + 1.0f) * 2f; + float inv_s = 1f / s; + return new Quat( + (x[2] + z[0]) * inv_s, + (y[2] + z[1]) * inv_s, + s * 0.25f, + (y[0] - x[1]) * inv_s + ); + } + } + + public Basis(Quat quat) + { + float s = 2.0f / quat.length_squared(); + + float xs = quat.x * s; + float ys = quat.y * s; + float zs = quat.z * s; + float wx = quat.w * xs; + float wy = quat.w * ys; + float wz = quat.w * zs; + float xx = quat.x * xs; + float xy = quat.x * ys; + float xz = quat.x * zs; + float yy = quat.y * ys; + float yz = quat.y * zs; + float zz = quat.z * zs; + + this.x = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy); + this.y = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx); + this.z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy)); + } + + public Basis(Vector3 axis, float phi) + { + Vector3 axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z); + + float cosine = Mathf.cos(phi); + float sine = Mathf.sin(phi); + + this.x = new Vector3 + ( + axis_sq.x + cosine * (1.0f - axis_sq.x), + axis.x * axis.y * (1.0f - cosine) - axis.z * sine, + axis.z * axis.x * (1.0f - cosine) + axis.y * sine + ); + + this.y = new Vector3 + ( + axis.x * axis.y * (1.0f - cosine) + axis.z * sine, + axis_sq.y + cosine * (1.0f - axis_sq.y), + axis.y * axis.z * (1.0f - cosine) - axis.x * sine + ); + + this.z = new Vector3 + ( + axis.z * axis.x * (1.0f - cosine) - axis.y * sine, + axis.y * axis.z * (1.0f - cosine) + axis.x * sine, + axis_sq.z + cosine * (1.0f - axis_sq.z) + ); + } + + public Basis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis) + { + this.x = xAxis; + this.y = yAxis; + this.z = zAxis; + } + + public Basis(float xx, float xy, float xz, float yx, float yy, float yz, float zx, float zy, float zz) + { + this.x = new Vector3(xx, xy, xz); + this.y = new Vector3(yx, yy, yz); + this.z = new Vector3(zx, zy, zz); + } + + public static Basis operator *(Basis left, Basis right) + { + return new Basis + ( + right.tdotx(left[0]), right.tdoty(left[0]), right.tdotz(left[0]), + right.tdotx(left[1]), right.tdoty(left[1]), right.tdotz(left[1]), + right.tdotx(left[2]), right.tdoty(left[2]), right.tdotz(left[2]) + ); + } + + public static bool operator ==(Basis left, Basis right) + { + return left.Equals(right); + } + + public static bool operator !=(Basis left, Basis right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Basis) + { + return Equals((Basis)obj); + } + + return false; + } + + public bool Equals(Basis other) + { + return x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z); + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1}, {2})", new object[] + { + this.x.ToString(), + this.y.ToString(), + this.z.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1}, {2})", new object[] + { + this.x.ToString(format), + this.y.ToString(format), + this.z.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/cs_files/Color.cs b/modules/mono/glue/cs_files/Color.cs new file mode 100644 index 0000000000..df88a46832 --- /dev/null +++ b/modules/mono/glue/cs_files/Color.cs @@ -0,0 +1,590 @@ +using System;
+
+namespace Godot
+{
+ public struct Color : IEquatable<Color>
+ {
+ public float r;
+ public float g;
+ public float b;
+ public float a;
+
+ public int r8
+ {
+ get
+ {
+ return (int)(r * 255.0f);
+ }
+ }
+
+ public int g8
+ {
+ get
+ {
+ return (int)(g * 255.0f);
+ }
+ }
+
+ public int b8
+ {
+ get
+ {
+ return (int)(b * 255.0f);
+ }
+ }
+
+ public int a8
+ {
+ get
+ {
+ return (int)(a * 255.0f);
+ }
+ }
+
+ public float h
+ {
+ get
+ {
+ float max = Mathf.max(r, Mathf.max(g, b));
+ float min = Mathf.min(r, Mathf.min(g, b));
+
+ float delta = max - min;
+
+ if (delta == 0)
+ return 0;
+
+ float h;
+
+ if (r == max)
+ h = (g - b) / delta; // Between yellow & magenta
+ else if (g == max)
+ h = 2 + (b - r) / delta; // Between cyan & yellow
+ else
+ h = 4 + (r - g) / delta; // Between magenta & cyan
+
+ h /= 6.0f;
+
+ if (h < 0)
+ h += 1.0f;
+
+ return h;
+ }
+ set
+ {
+ this = from_hsv(value, s, v);
+ }
+ }
+
+ public float s
+ {
+ get
+ {
+ float max = Mathf.max(r, Mathf.max(g, b));
+ float min = Mathf.min(r, Mathf.min(g, b));
+
+ float delta = max - min;
+
+ return max != 0 ? delta / max : 0;
+ }
+ set
+ {
+ this = from_hsv(h, value, v);
+ }
+ }
+
+ public float v
+ {
+ get
+ {
+ return Mathf.max(r, Mathf.max(g, b));
+ }
+ set
+ {
+ this = from_hsv(h, s, value);
+ }
+ }
+
+ private static readonly Color black = new Color(0f, 0f, 0f, 1.0f);
+
+ public Color Black
+ {
+ get
+ {
+ return black;
+ }
+ }
+
+ public float this [int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0:
+ return r;
+ case 1:
+ return g;
+ case 2:
+ return b;
+ case 3:
+ return a;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ set
+ {
+ switch (index)
+ {
+ case 0:
+ r = value;
+ return;
+ case 1:
+ g = value;
+ return;
+ case 2:
+ b = value;
+ return;
+ case 3:
+ a = value;
+ return;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ }
+
+ public static void to_hsv(Color color, out float hue, out float saturation, out float value)
+ {
+ int max = Mathf.max(color.r8, Mathf.max(color.g8, color.b8));
+ int min = Mathf.min(color.r8, Mathf.min(color.g8, color.b8));
+
+ float delta = max - min;
+
+ if (delta == 0)
+ {
+ hue = 0;
+ }
+ else
+ {
+ if (color.r == max)
+ hue = (color.g - color.b) / delta; // Between yellow & magenta
+ else if (color.g == max)
+ hue = 2 + (color.b - color.r) / delta; // Between cyan & yellow
+ else
+ hue = 4 + (color.r - color.g) / delta; // Between magenta & cyan
+
+ hue /= 6.0f;
+
+ if (hue < 0)
+ hue += 1.0f;
+ }
+
+ saturation = (max == 0) ? 0 : 1f - (1f * min / max);
+ value = max / 255f;
+ }
+
+ public static Color from_hsv(float hue, float saturation, float value, float alpha = 1.0f)
+ {
+ if (saturation == 0)
+ {
+ // acp_hromatic (grey)
+ return new Color(value, value, value, alpha);
+ }
+
+ int i;
+ float f, p, q, t;
+
+ hue *= 6.0f;
+ hue %= 6f;
+ i = (int)hue;
+
+ f = hue - i;
+ p = value * (1 - saturation);
+ q = value * (1 - saturation * f);
+ t = value * (1 - saturation * (1 - f));
+
+ switch (i)
+ {
+ case 0: // Red is the dominant color
+ return new Color(value, t, p, alpha);
+ case 1: // Green is the dominant color
+ return new Color(q, value, p, alpha);
+ case 2:
+ return new Color(p, value, t, alpha);
+ case 3: // Blue is the dominant color
+ return new Color(p, q, value, alpha);
+ case 4:
+ return new Color(t, p, value, alpha);
+ default: // (5) Red is the dominant color
+ return new Color(value, p, q, alpha);
+ }
+ }
+
+ public Color blend(Color over)
+ {
+ Color res;
+
+ float sa = 1.0f - over.a;
+ res.a = a * sa + over.a;
+
+ if (res.a == 0)
+ {
+ return new Color(0, 0, 0, 0);
+ }
+ else
+ {
+ res.r = (r * a * sa + over.r * over.a) / res.a;
+ res.g = (g * a * sa + over.g * over.a) / res.a;
+ res.b = (b * a * sa + over.b * over.a) / res.a;
+ }
+
+ return res;
+ }
+
+ public Color contrasted()
+ {
+ return new Color(
+ (r + 0.5f) % 1.0f,
+ (g + 0.5f) % 1.0f,
+ (b + 0.5f) % 1.0f
+ );
+ }
+
+ public float gray()
+ {
+ return (r + g + b) / 3.0f;
+ }
+
+ public Color inverted()
+ {
+ return new Color(
+ 1.0f - r,
+ 1.0f - g,
+ 1.0f - b
+ );
+ }
+
+ public Color linear_interpolate(Color b, float t)
+ {
+ Color res = this;
+
+ res.r += (t * (b.r - this.r));
+ res.g += (t * (b.g - this.g));
+ res.b += (t * (b.b - this.b));
+ res.a += (t * (b.a - this.a));
+
+ return res;
+ }
+
+ public int to_32()
+ {
+ int c = (byte)(a * 255);
+ c <<= 8;
+ c |= (byte)(r * 255);
+ c <<= 8;
+ c |= (byte)(g * 255);
+ c <<= 8;
+ c |= (byte)(b * 255);
+
+ return c;
+ }
+
+ public int to_ARGB32()
+ {
+ int c = (byte)(a * 255);
+ c <<= 8;
+ c |= (byte)(r * 255);
+ c <<= 8;
+ c |= (byte)(g * 255);
+ c <<= 8;
+ c |= (byte)(b * 255);
+
+ return c;
+ }
+
+ public string to_html(bool include_alpha = true)
+ {
+ String txt = string.Empty;
+
+ txt += _to_hex(r);
+ txt += _to_hex(g);
+ txt += _to_hex(b);
+
+ if (include_alpha)
+ txt = _to_hex(a) + txt;
+
+ return txt;
+ }
+
+ public Color(float r, float g, float b, float a = 1.0f)
+ {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a;
+ }
+
+ public Color(int rgba)
+ {
+ this.a = (rgba & 0xFF) / 255.0f;
+ rgba >>= 8;
+ this.b = (rgba & 0xFF) / 255.0f;
+ rgba >>= 8;
+ this.g = (rgba & 0xFF) / 255.0f;
+ rgba >>= 8;
+ this.r = (rgba & 0xFF) / 255.0f;
+ }
+
+ private static float _parse_col(string str, int ofs)
+ {
+ int ig = 0;
+
+ for (int i = 0; i < 2; i++)
+ {
+ int c = str[i + ofs];
+ int v = 0;
+
+ if (c >= '0' && c <= '9')
+ {
+ v = c - '0';
+ }
+ else if (c >= 'a' && c <= 'f')
+ {
+ v = c - 'a';
+ v += 10;
+ }
+ else if (c >= 'A' && c <= 'F')
+ {
+ v = c - 'A';
+ v += 10;
+ }
+ else
+ {
+ return -1;
+ }
+
+ if (i == 0)
+ ig += v * 16;
+ else
+ ig += v;
+ }
+
+ return ig;
+ }
+
+ private String _to_hex(float val)
+ {
+ int v = (int)Mathf.clamp(val * 255.0f, 0, 255);
+
+ string ret = string.Empty;
+
+ for (int i = 0; i < 2; i++)
+ {
+ char[] c = { (char)0, (char)0 };
+ int lv = v & 0xF;
+
+ if (lv < 10)
+ c[0] = (char)('0' + lv);
+ else
+ c[0] = (char)('a' + lv - 10);
+
+ v >>= 4;
+ ret = c + ret;
+ }
+
+ return ret;
+ }
+
+ internal static bool html_is_valid(string color)
+ {
+ if (color.Length == 0)
+ return false;
+
+ if (color[0] == '#')
+ color = color.Substring(1, color.Length - 1);
+
+ bool alpha = false;
+
+ if (color.Length == 8)
+ alpha = true;
+ else if (color.Length == 6)
+ alpha = false;
+ else
+ return false;
+
+ if (alpha)
+ {
+ if ((int)_parse_col(color, 0) < 0)
+ return false;
+ }
+
+ int from = alpha ? 2 : 0;
+
+ if ((int)_parse_col(color, from + 0) < 0)
+ return false;
+ if ((int)_parse_col(color, from + 2) < 0)
+ return false;
+ if ((int)_parse_col(color, from + 4) < 0)
+ return false;
+
+ return true;
+ }
+
+ public static Color Color8(byte r8, byte g8, byte b8, byte a8)
+ {
+ return new Color((float)r8 / 255f, (float)g8 / 255f, (float)b8 / 255f, (float)a8 / 255f);
+ }
+
+ public Color(string rgba)
+ {
+ if (rgba.Length == 0)
+ {
+ r = 0f;
+ g = 0f;
+ b = 0f;
+ a = 1.0f;
+ return;
+ }
+
+ if (rgba[0] == '#')
+ rgba = rgba.Substring(1);
+
+ bool alpha = false;
+
+ if (rgba.Length == 8)
+ {
+ alpha = true;
+ }
+ else if (rgba.Length == 6)
+ {
+ alpha = false;
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba);
+ }
+
+ if (alpha)
+ {
+ a = _parse_col(rgba, 0);
+
+ if (a < 0)
+ throw new ArgumentOutOfRangeException("Invalid color code. Alpha is " + a + " but zero or greater is expected: " + rgba);
+ }
+ else
+ {
+ a = 1.0f;
+ }
+
+ int from = alpha ? 2 : 0;
+
+ r = _parse_col(rgba, from + 0);
+
+ if (r < 0)
+ throw new ArgumentOutOfRangeException("Invalid color code. Red is " + r + " but zero or greater is expected: " + rgba);
+
+ g = _parse_col(rgba, from + 2);
+
+ if (g < 0)
+ throw new ArgumentOutOfRangeException("Invalid color code. Green is " + g + " but zero or greater is expected: " + rgba);
+
+ b = _parse_col(rgba, from + 4);
+
+ if (b < 0)
+ throw new ArgumentOutOfRangeException("Invalid color code. Blue is " + b + " but zero or greater is expected: " + rgba);
+ }
+
+ public static bool operator ==(Color left, Color right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Color left, Color right)
+ {
+ return !left.Equals(right);
+ }
+
+ public static bool operator <(Color left, Color right)
+ {
+ if (left.r == right.r)
+ {
+ if (left.g == right.g)
+ {
+ if (left.b == right.b)
+ return (left.a < right.a);
+ else
+ return (left.b < right.b);
+ }
+ else
+ {
+ return left.g < right.g;
+ }
+ }
+
+ return left.r < right.r;
+ }
+
+ public static bool operator >(Color left, Color right)
+ {
+ if (left.r == right.r)
+ {
+ if (left.g == right.g)
+ {
+ if (left.b == right.b)
+ return (left.a > right.a);
+ else
+ return (left.b > right.b);
+ }
+ else
+ {
+ return left.g > right.g;
+ }
+ }
+
+ return left.r > right.r;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is Color)
+ {
+ return Equals((Color)obj);
+ }
+
+ return false;
+ }
+
+ public bool Equals(Color other)
+ {
+ return r == other.r && g == other.g && b == other.b && a == other.a;
+ }
+
+ public override int GetHashCode()
+ {
+ return r.GetHashCode() ^ g.GetHashCode() ^ b.GetHashCode() ^ a.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return String.Format("{0},{1},{2},{3}", new object[]
+ {
+ this.r.ToString(),
+ this.g.ToString(),
+ this.b.ToString(),
+ this.a.ToString()
+ });
+ }
+
+ public string ToString(string format)
+ {
+ return String.Format("{0},{1},{2},{3}", new object[]
+ {
+ this.r.ToString(format),
+ this.g.ToString(format),
+ this.b.ToString(format),
+ this.a.ToString(format)
+ });
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/Error.cs b/modules/mono/glue/cs_files/Error.cs new file mode 100644 index 0000000000..3f4a92603d --- /dev/null +++ b/modules/mono/glue/cs_files/Error.cs @@ -0,0 +1,48 @@ +namespace Godot +{ + public enum Error : int + { + OK = 0, + FAILED = 1, + ERR_UNAVAILABLE = 2, + ERR_UNCONFIGURED = 3, + ERR_UNAUTHORIZED = 4, + ERR_PARAMETER_RANGE_ERROR = 5, + ERR_OUT_OF_MEMORY = 6, + ERR_FILE_NOT_FOUND = 7, + ERR_FILE_BAD_DRIVE = 8, + ERR_FILE_BAD_PATH = 9, + ERR_FILE_NO_PERMISSION = 10, + ERR_FILE_ALREADY_IN_USE = 11, + ERR_FILE_CANT_OPEN = 12, + ERR_FILE_CANT_WRITE = 13, + ERR_FILE_CANT_READ = 14, + ERR_FILE_UNRECOGNIZED = 15, + ERR_FILE_CORRUPT = 16, + ERR_FILE_MISSING_DEPENDENCIES = 17, + ERR_FILE_EOF = 18, + ERR_CANT_OPEN = 19, + ERR_CANT_CREATE = 20, + ERR_PARSE_ERROR = 43, + ERROR_QUERY_FAILED = 21, + ERR_ALREADY_IN_USE = 22, + ERR_LOCKED = 23, + ERR_TIMEOUT = 24, + ERR_CANT_AQUIRE_RESOURCE = 28, + ERR_INVALID_DATA = 30, + ERR_INVALID_PARAMETER = 31, + ERR_ALREADY_EXISTS = 32, + ERR_DOES_NOT_EXIST = 33, + ERR_DATABASE_CANT_READ = 34, + ERR_DATABASE_CANT_WRITE = 35, + ERR_COMPILATION_FAILED = 36, + ERR_METHOD_NOT_FOUND = 37, + ERR_LINK_FAILED = 38, + ERR_SCRIPT_FAILED = 39, + ERR_CYCLIC_LINK = 40, + ERR_BUSY = 44, + ERR_HELP = 46, + ERR_BUG = 47, + ERR_WTF = 49 + } +} diff --git a/modules/mono/glue/cs_files/ExportAttribute.cs b/modules/mono/glue/cs_files/ExportAttribute.cs new file mode 100644 index 0000000000..a4e7d447dd --- /dev/null +++ b/modules/mono/glue/cs_files/ExportAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public class ExportAttribute : Attribute + { + private int hint; + private string hint_string; + + public ExportAttribute(int hint = GD.PROPERTY_HINT_NONE, string hint_string = "") + { + this.hint = hint; + this.hint_string = hint_string; + } + } +} diff --git a/modules/mono/glue/cs_files/GD.cs b/modules/mono/glue/cs_files/GD.cs new file mode 100644 index 0000000000..40a42d23b4 --- /dev/null +++ b/modules/mono/glue/cs_files/GD.cs @@ -0,0 +1,191 @@ +using System; + +namespace Godot +{ + public static class GD + { + /*{GodotGlobalConstants}*/ + + public static object bytes2var(byte[] bytes) + { + return NativeCalls.godot_icall_Godot_bytes2var(bytes); + } + + public static object convert(object what, int type) + { + return NativeCalls.godot_icall_Godot_convert(what, type); + } + + public static float db2linear(float db) + { + return (float)Math.Exp(db * 0.11512925464970228420089957273422); + } + + public static float dectime(float value, float amount, float step) + { + float sgn = value < 0 ? -1.0f : 1.0f; + float val = Mathf.abs(value); + val -= amount * step; + if (val < 0.0f) + val = 0.0f; + return val * sgn; + } + + public static FuncRef funcref(Object instance, string funcname) + { + var ret = new FuncRef(); + ret.SetInstance(instance); + ret.SetFunction(funcname); + return ret; + } + + public static int hash(object var) + { + return NativeCalls.godot_icall_Godot_hash(var); + } + + public static Object instance_from_id(int instance_id) + { + return NativeCalls.godot_icall_Godot_instance_from_id(instance_id); + } + + public static double linear2db(double linear) + { + return Math.Log(linear) * 8.6858896380650365530225783783321; + } + + public static Resource load(string path) + { + return ResourceLoader.Load(path); + } + + public static void print(params object[] what) + { + NativeCalls.godot_icall_Godot_print(what); + } + + public static void print_stack() + { + print(System.Environment.StackTrace); + } + + public static void printerr(params object[] what) + { + NativeCalls.godot_icall_Godot_printerr(what); + } + + public static void printraw(params object[] what) + { + NativeCalls.godot_icall_Godot_printraw(what); + } + + public static void prints(params object[] what) + { + NativeCalls.godot_icall_Godot_prints(what); + } + + public static void printt(params object[] what) + { + NativeCalls.godot_icall_Godot_printt(what); + } + + public static int[] range(int length) + { + int[] ret = new int[length]; + + for (int i = 0; i < length; i++) + { + ret[i] = i; + } + + return ret; + } + + public static int[] range(int from, int to) + { + if (to < from) + return new int[0]; + + int[] ret = new int[to - from]; + + for (int i = from; i < to; i++) + { + ret[i - from] = i; + } + + return ret; + } + + public static int[] range(int from, int to, int increment) + { + if (to < from && increment > 0) + return new int[0]; + if (to > from && increment < 0) + return new int[0]; + + // Calculate count + int count = 0; + + if (increment > 0) + count = ((to - from - 1) / increment) + 1; + else + count = ((from - to - 1) / -increment) + 1; + + int[] ret = new int[count]; + + if (increment > 0) + { + int idx = 0; + for (int i = from; i < to; i += increment) + { + ret[idx++] = i; + } + } + else + { + int idx = 0; + for (int i = from; i > to; i += increment) + { + ret[idx++] = i; + } + } + + return ret; + } + + public static void seed(int seed) + { + NativeCalls.godot_icall_Godot_seed(seed); + } + + public static string str(params object[] what) + { + return NativeCalls.godot_icall_Godot_str(what); + } + + public static object str2var(string str) + { + return NativeCalls.godot_icall_Godot_str2var(str); + } + + public static bool type_exists(string type) + { + return NativeCalls.godot_icall_Godot_type_exists(type); + } + + public static byte[] var2bytes(object var) + { + return NativeCalls.godot_icall_Godot_var2bytes(var); + } + + public static string var2str(object var) + { + return NativeCalls.godot_icall_Godot_var2str(var); + } + + public static WeakRef weakref(Object obj) + { + return NativeCalls.godot_icall_Godot_weakref(Object.GetPtr(obj)); + } + } +} diff --git a/modules/mono/glue/cs_files/GodotMethodAttribute.cs b/modules/mono/glue/cs_files/GodotMethodAttribute.cs new file mode 100644 index 0000000000..21333c8dab --- /dev/null +++ b/modules/mono/glue/cs_files/GodotMethodAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Method, Inherited = true)] + internal class GodotMethodAttribute : Attribute + { + private string methodName; + + public string MethodName { get { return methodName; } } + + public GodotMethodAttribute(string methodName) + { + this.methodName = methodName; + } + } +} diff --git a/modules/mono/glue/cs_files/GodotSynchronizationContext.cs b/modules/mono/glue/cs_files/GodotSynchronizationContext.cs new file mode 100644 index 0000000000..eb4d0bed1c --- /dev/null +++ b/modules/mono/glue/cs_files/GodotSynchronizationContext.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; + +namespace Godot +{ + public class GodotSynchronizationContext : SynchronizationContext + { + private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> queue = new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>(); + + public override void Post(SendOrPostCallback d, object state) + { + queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state)); + } + + public void ExecutePendingContinuations() + { + KeyValuePair<SendOrPostCallback, object> workItem; + while (queue.TryTake(out workItem)) + { + workItem.Key(workItem.Value); + } + } + } +} diff --git a/modules/mono/glue/cs_files/GodotTaskScheduler.cs b/modules/mono/glue/cs_files/GodotTaskScheduler.cs new file mode 100644 index 0000000000..f587645a49 --- /dev/null +++ b/modules/mono/glue/cs_files/GodotTaskScheduler.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Godot +{ + public class GodotTaskScheduler : TaskScheduler + { + private GodotSynchronizationContext Context { get; set; } + private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); + + public GodotTaskScheduler() + { + Context = new GodotSynchronizationContext(); + } + + protected sealed override void QueueTask(Task task) + { + lock (_tasks) + { + _tasks.AddLast(task); + } + } + + protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + if (SynchronizationContext.Current != Context) + { + return false; + } + + if (taskWasPreviouslyQueued) + { + TryDequeue(task); + } + + return base.TryExecuteTask(task); + } + + protected sealed override bool TryDequeue(Task task) + { + lock (_tasks) + { + return _tasks.Remove(task); + } + } + + protected sealed override IEnumerable<Task> GetScheduledTasks() + { + lock (_tasks) + { + return _tasks.ToArray(); + } + } + + public void Activate() + { + SynchronizationContext.SetSynchronizationContext(Context); + ExecuteQueuedTasks(); + Context.ExecutePendingContinuations(); + } + + private void ExecuteQueuedTasks() + { + while (true) + { + Task task; + + lock (_tasks) + { + if (_tasks.Any()) + { + task = _tasks.First.Value; + _tasks.RemoveFirst(); + } + else + { + break; + } + } + + if (task != null) + { + if (!TryExecuteTask(task)) + { + throw new InvalidOperationException(); + } + } + } + } + } +} diff --git a/modules/mono/glue/cs_files/IAwaitable.cs b/modules/mono/glue/cs_files/IAwaitable.cs new file mode 100644 index 0000000000..0397957d00 --- /dev/null +++ b/modules/mono/glue/cs_files/IAwaitable.cs @@ -0,0 +1,12 @@ +namespace Godot +{ + public interface IAwaitable + { + IAwaiter GetAwaiter(); + } + + public interface IAwaitable<out TResult> + { + IAwaiter<TResult> GetAwaiter(); + } +} diff --git a/modules/mono/glue/cs_files/IAwaiter.cs b/modules/mono/glue/cs_files/IAwaiter.cs new file mode 100644 index 0000000000..73c71b5634 --- /dev/null +++ b/modules/mono/glue/cs_files/IAwaiter.cs @@ -0,0 +1,19 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public interface IAwaiter : INotifyCompletion + { + bool IsCompleted { get; } + + void GetResult(); + } + + public interface IAwaiter<out TResult> : INotifyCompletion + { + bool IsCompleted { get; } + + TResult GetResult(); + } +} diff --git a/modules/mono/glue/cs_files/MarshalUtils.cs b/modules/mono/glue/cs_files/MarshalUtils.cs new file mode 100644 index 0000000000..5d40111339 --- /dev/null +++ b/modules/mono/glue/cs_files/MarshalUtils.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace Godot +{ + internal static class MarshalUtils + { + private static Dictionary<object, object> ArraysToDictionary(object[] keys, object[] values) + { + Dictionary<object, object> ret = new Dictionary<object, object>(); + + for (int i = 0; i < keys.Length; i++) + { + ret.Add(keys[i], values[i]); + } + + return ret; + } + + private static void DictionaryToArrays(Dictionary<object, object> from, out object[] keysTo, out object[] valuesTo) + { + Dictionary<object, object>.KeyCollection keys = from.Keys; + keysTo = new object[keys.Count]; + keys.CopyTo(keysTo, 0); + + Dictionary<object, object>.ValueCollection values = from.Values; + valuesTo = new object[values.Count]; + values.CopyTo(valuesTo, 0); + } + + private static Type GetDictionaryType() + { + return typeof(Dictionary<object, object>); + } + } +} diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs new file mode 100644 index 0000000000..cb0eb1acdd --- /dev/null +++ b/modules/mono/glue/cs_files/Mathf.cs @@ -0,0 +1,234 @@ +using System; + +namespace Godot +{ + public static class Mathf + { + public const float PI = 3.14159274f; + public const float Epsilon = 1e-06f; + + private const float Deg2RadConst = 0.0174532924f; + private const float Rad2DegConst = 57.29578f; + + public static float abs(float s) + { + return Math.Abs(s); + } + + public static float acos(float s) + { + return (float)Math.Acos(s); + } + + public static float asin(float s) + { + return (float)Math.Asin(s); + } + + public static float atan(float s) + { + return (float)Math.Atan(s); + } + + public static float atan2(float x, float y) + { + return (float)Math.Atan2(x, y); + } + + public static float ceil(float s) + { + return (float)Math.Ceiling(s); + } + + public static float clamp(float val, float min, float max) + { + if (val < min) + { + return min; + } + else if (val > max) + { + return max; + } + + return val; + } + + public static float cos(float s) + { + return (float)Math.Cos(s); + } + + public static float cosh(float s) + { + return (float)Math.Cosh(s); + } + + public static int decimals(float step) + { + return decimals(step); + } + + public static int decimals(decimal step) + { + return BitConverter.GetBytes(decimal.GetBits(step)[3])[2]; + } + + public static float deg2rad(float deg) + { + return deg * Deg2RadConst; + } + + public static float ease(float s, float curve) + { + if (s < 0f) + { + s = 0f; + } + else if (s > 1.0f) + { + s = 1.0f; + } + + if (curve > 0f) + { + if (curve < 1.0f) + { + return 1.0f - pow(1.0f - s, 1.0f / curve); + } + + return pow(s, curve); + } + else if (curve < 0f) + { + if (s < 0.5f) + { + return pow(s * 2.0f, -curve) * 0.5f; + } + + return (1.0f - pow(1.0f - (s - 0.5f) * 2.0f, -curve)) * 0.5f + 0.5f; + } + + return 0f; + } + + public static float exp(float s) + { + return (float)Math.Exp(s); + } + + public static float floor(float s) + { + return (float)Math.Floor(s); + } + + public static float fposmod(float x, float y) + { + if (x >= 0f) + { + return x % y; + } + else + { + return y - (-x % y); + } + } + + public static float lerp(float from, float to, float weight) + { + return from + (to - from) * clamp(weight, 0f, 1f); + } + + public static float log(float s) + { + return (float)Math.Log(s); + } + + public static int max(int a, int b) + { + return (a > b) ? a : b; + } + + public static float max(float a, float b) + { + return (a > b) ? a : b; + } + + public static int min(int a, int b) + { + return (a < b) ? a : b; + } + + public static float min(float a, float b) + { + return (a < b) ? a : b; + } + + public static int nearest_po2(int val) + { + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val++; + return val; + } + + public static float pow(float x, float y) + { + return (float)Math.Pow(x, y); + } + + public static float rad2deg(float rad) + { + return rad * Rad2DegConst; + } + + public static float round(float s) + { + return (float)Math.Round(s); + } + + public static float sign(float s) + { + return (s < 0f) ? -1f : 1f; + } + + public static float sin(float s) + { + return (float)Math.Sin(s); + } + + public static float sinh(float s) + { + return (float)Math.Sinh(s); + } + + public static float sqrt(float s) + { + return (float)Math.Sqrt(s); + } + + public static float stepify(float s, float step) + { + if (step != 0f) + { + s = floor(s / step + 0.5f) * step; + } + + return s; + } + + public static float tan(float s) + { + return (float)Math.Tan(s); + } + + public static float tanh(float s) + { + return (float)Math.Tanh(s); + } + } +} diff --git a/modules/mono/glue/cs_files/Plane.cs b/modules/mono/glue/cs_files/Plane.cs new file mode 100644 index 0000000000..ada6e465ac --- /dev/null +++ b/modules/mono/glue/cs_files/Plane.cs @@ -0,0 +1,209 @@ +using System;
+
+namespace Godot
+{
+ public struct Plane : IEquatable<Plane>
+ {
+ Vector3 normal;
+
+ public float x
+ {
+ get
+ {
+ return normal.x;
+ }
+ set
+ {
+ normal.x = value;
+ }
+ }
+
+ public float y
+ {
+ get
+ {
+ return normal.y;
+ }
+ set
+ {
+ normal.y = value;
+ }
+ }
+
+ public float z
+ {
+ get
+ {
+ return normal.z;
+ }
+ set
+ {
+ normal.z = value;
+ }
+ }
+
+ float d;
+
+ public Vector3 Center
+ {
+ get
+ {
+ return normal * d;
+ }
+ }
+
+ public float distance_to(Vector3 point)
+ {
+ return normal.dot(point) - d;
+ }
+
+ public Vector3 get_any_point()
+ {
+ return normal * d;
+ }
+
+ public bool has_point(Vector3 point, float epsilon = Mathf.Epsilon)
+ {
+ float dist = normal.dot(point) - d;
+ return Mathf.abs(dist) <= epsilon;
+ }
+
+ public Vector3 intersect_3(Plane b, Plane c)
+ {
+ float denom = normal.cross(b.normal).dot(c.normal);
+
+ if (Mathf.abs(denom) <= Mathf.Epsilon)
+ return new Vector3();
+
+ Vector3 result = (b.normal.cross(c.normal) * this.d) +
+ (c.normal.cross(normal) * b.d) +
+ (normal.cross(b.normal) * c.d);
+
+ return result / denom;
+ }
+
+ public Vector3 intersect_ray(Vector3 from, Vector3 dir)
+ {
+ float den = normal.dot(dir);
+
+ if (Mathf.abs(den) <= Mathf.Epsilon)
+ return new Vector3();
+
+ float dist = (normal.dot(from) - d) / den;
+
+ // This is a ray, before the emiting pos (from) does not exist
+ if (dist > Mathf.Epsilon)
+ return new Vector3();
+
+ return from + dir * -dist;
+ }
+
+ public Vector3 intersect_segment(Vector3 begin, Vector3 end)
+ {
+ Vector3 segment = begin - end;
+ float den = normal.dot(segment);
+
+ if (Mathf.abs(den) <= Mathf.Epsilon)
+ return new Vector3();
+
+ float dist = (normal.dot(begin) - d) / den;
+
+ if (dist < -Mathf.Epsilon || dist > (1.0f + Mathf.Epsilon))
+ return new Vector3();
+
+ return begin + segment * -dist;
+ }
+
+ public bool is_point_over(Vector3 point)
+ {
+ return normal.dot(point) > d;
+ }
+
+ public Plane normalized()
+ {
+ float len = normal.length();
+
+ if (len == 0)
+ return new Plane(0, 0, 0, 0);
+
+ return new Plane(normal / len, d / len);
+ }
+
+ public Vector3 project(Vector3 point)
+ {
+ return point - normal * distance_to(point);
+ }
+
+ public Plane(float a, float b, float c, float d)
+ {
+ normal = new Vector3(a, b, c);
+ this.d = d;
+ }
+
+ public Plane(Vector3 normal, float d)
+ {
+ this.normal = normal;
+ this.d = d;
+ }
+
+ public Plane(Vector3 v1, Vector3 v2, Vector3 v3)
+ {
+ normal = (v1 - v3).cross(v1 - v2);
+ normal.normalize();
+ d = normal.dot(v1);
+ }
+
+ public static Plane operator -(Plane plane)
+ {
+ return new Plane(-plane.normal, -plane.d);
+ }
+
+ public static bool operator ==(Plane left, Plane right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Plane left, Plane right)
+ {
+ return !left.Equals(right);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is Plane)
+ {
+ return Equals((Plane)obj);
+ }
+
+ return false;
+ }
+
+ public bool Equals(Plane other)
+ {
+ return normal == other.normal && d == other.d;
+ }
+
+ public override int GetHashCode()
+ {
+ return normal.GetHashCode() ^ d.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return String.Format("({0}, {1})", new object[]
+ {
+ this.normal.ToString(),
+ this.d.ToString()
+ });
+ }
+
+ public string ToString(string format)
+ {
+ return String.Format("({0}, {1})", new object[]
+ {
+ this.normal.ToString(format),
+ this.d.ToString(format)
+ });
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/Quat.cs b/modules/mono/glue/cs_files/Quat.cs new file mode 100644 index 0000000000..9b4b7fb297 --- /dev/null +++ b/modules/mono/glue/cs_files/Quat.cs @@ -0,0 +1,328 @@ +using System; +using System.Runtime.InteropServices; + +namespace Godot +{ + [StructLayout(LayoutKind.Sequential)] + public struct Quat : IEquatable<Quat> + { + private static readonly Quat identity = new Quat(0f, 0f, 0f, 1f); + + public float x; + public float y; + public float z; + public float w; + + public static Quat Identity + { + get { return identity; } + } + + public float this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + break; + case 1: + y = value; + break; + case 2: + z = value; + break; + case 3: + w = value; + break; + default: + throw new IndexOutOfRangeException(); + } + } + } + + public Quat cubic_slerp(Quat b, Quat preA, Quat postB, float t) + { + float t2 = (1.0f - t) * t * 2f; + Quat sp = slerp(b, t); + Quat sq = preA.slerpni(postB, t); + return sp.slerpni(sq, t2); + } + + public float dot(Quat b) + { + return x * b.x + y * b.y + z * b.z + w * b.w; + } + + public Quat inverse() + { + return new Quat(-x, -y, -z, w); + } + + public float length() + { + return Mathf.sqrt(length_squared()); + } + + public float length_squared() + { + return dot(this); + } + + public Quat normalized() + { + return this / length(); + } + + public void set(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public Quat slerp(Quat b, float t) + { + // Calculate cosine + float cosom = x * b.x + y * b.y + z * b.z + w * b.w; + + float[] to1 = new float[4]; + + // Adjust signs if necessary + if (cosom < 0.0) + { + cosom = -cosom; to1[0] = -b.x; + to1[1] = -b.y; + to1[2] = -b.z; + to1[3] = -b.w; + } + else + { + to1[0] = b.x; + to1[1] = b.y; + to1[2] = b.z; + to1[3] = b.w; + } + + float sinom, scale0, scale1; + + // Calculate coefficients + if ((1.0 - cosom) > Mathf.Epsilon) + { + // Standard case (Slerp) + float omega = Mathf.acos(cosom); + sinom = Mathf.sin(omega); + scale0 = Mathf.sin((1.0f - t) * omega) / sinom; + scale1 = Mathf.sin(t * omega) / sinom; + } + else + { + // Quaternions are very close so we can do a linear interpolation + scale0 = 1.0f - t; + scale1 = t; + } + + // Calculate final values + return new Quat + ( + scale0 * x + scale1 * to1[0], + scale0 * y + scale1 * to1[1], + scale0 * z + scale1 * to1[2], + scale0 * w + scale1 * to1[3] + ); + } + + public Quat slerpni(Quat b, float t) + { + float dot = this.dot(b); + + if (Mathf.abs(dot) > 0.9999f) + { + return this; + } + + float theta = Mathf.acos(dot); + float sinT = 1.0f / Mathf.sin(theta); + float newFactor = Mathf.sin(t * theta) * sinT; + float invFactor = Mathf.sin((1.0f - t) * theta) * sinT; + + return new Quat + ( + invFactor * this.x + newFactor * b.x, + invFactor * this.y + newFactor * b.y, + invFactor * this.z + newFactor * b.z, + invFactor * this.w + newFactor * b.w + ); + } + + public Vector3 xform(Vector3 v) + { + Quat q = this * v; + q *= this.inverse(); + return new Vector3(q.x, q.y, q.z); + } + + public Quat(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public Quat(Vector3 axis, float angle) + { + float d = axis.length(); + + if (d == 0f) + { + x = 0f; + y = 0f; + z = 0f; + w = 0f; + } + else + { + float s = Mathf.sin(angle * 0.5f) / d; + + x = axis.x * s; + y = axis.y * s; + z = axis.z * s; + w = Mathf.cos(angle * 0.5f); + } + } + + public static Quat operator *(Quat left, Quat right) + { + return new Quat + ( + left.w * right.x + left.x * right.w + left.y * right.z - left.z * right.y, + left.w * right.y + left.y * right.w + left.z * right.x - left.x * right.z, + left.w * right.z + left.z * right.w + left.x * right.y - left.y * right.x, + left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z + ); + } + + public static Quat operator +(Quat left, Quat right) + { + return new Quat(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w); + } + + public static Quat operator -(Quat left, Quat right) + { + return new Quat(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w); + } + + public static Quat operator -(Quat left) + { + return new Quat(-left.x, -left.y, -left.z, -left.w); + } + + public static Quat operator *(Quat left, Vector3 right) + { + return new Quat + ( + left.w * right.x + left.y * right.z - left.z * right.y, + left.w * right.y + left.z * right.x - left.x * right.z, + left.w * right.z + left.x * right.y - left.y * right.x, + -left.x * right.x - left.y * right.y - left.z * right.z + ); + } + + public static Quat operator *(Vector3 left, Quat right) + { + return new Quat + ( + right.w * left.x + right.y * left.z - right.z * left.y, + right.w * left.y + right.z * left.x - right.x * left.z, + right.w * left.z + right.x * left.y - right.y * left.x, + -right.x * left.x - right.y * left.y - right.z * left.z + ); + } + + public static Quat operator *(Quat left, float right) + { + return new Quat(left.x * right, left.y * right, left.z * right, left.w * right); + } + + public static Quat operator *(float left, Quat right) + { + return new Quat(right.x * left, right.y * left, right.z * left, right.w * left); + } + + public static Quat operator /(Quat left, float right) + { + return left * (1.0f / right); + } + + public static bool operator ==(Quat left, Quat right) + { + return left.Equals(right); + } + + public static bool operator !=(Quat left, Quat right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Vector2) + { + return Equals((Vector2)obj); + } + + return false; + } + + public bool Equals(Quat other) + { + return x == other.x && y == other.y && z == other.z && w == other.w; + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1}, {2}, {3})", new object[] + { + this.x.ToString(), + this.y.ToString(), + this.z.ToString(), + this.w.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1}, {2}, {3})", new object[] + { + this.x.ToString(format), + this.y.ToString(format), + this.z.ToString(format), + this.w.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/cs_files/RPCAttributes.cs b/modules/mono/glue/cs_files/RPCAttributes.cs new file mode 100644 index 0000000000..08841ffd76 --- /dev/null +++ b/modules/mono/glue/cs_files/RPCAttributes.cs @@ -0,0 +1,16 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class RemoteAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class SyncAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class MasterAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class SlaveAttribute : Attribute {} +} diff --git a/modules/mono/glue/cs_files/Rect2.cs b/modules/mono/glue/cs_files/Rect2.cs new file mode 100644 index 0000000000..019342134a --- /dev/null +++ b/modules/mono/glue/cs_files/Rect2.cs @@ -0,0 +1,233 @@ +using System; +using System.Runtime.InteropServices; + +namespace Godot +{ + [StructLayout(LayoutKind.Sequential)] + public struct Rect2 : IEquatable<Rect2> + { + private Vector2 position; + private Vector2 size; + + public Vector2 Position + { + get { return position; } + set { position = value; } + } + + public Vector2 Size + { + get { return size; } + set { size = value; } + } + + public Vector2 End + { + get { return position + size; } + } + + public float Area + { + get { return get_area(); } + } + + public Rect2 clip(Rect2 b) + { + Rect2 newRect = b; + + if (!intersects(newRect)) + return new Rect2(); + + newRect.position.x = Mathf.max(b.position.x, position.x); + newRect.position.y = Mathf.max(b.position.y, position.y); + + Vector2 bEnd = b.position + b.size; + Vector2 end = position + size; + + newRect.size.x = Mathf.min(bEnd.x, end.x) - newRect.position.x; + newRect.size.y = Mathf.min(bEnd.y, end.y) - newRect.position.y; + + return newRect; + } + + public bool encloses(Rect2 b) + { + return (b.position.x >= position.x) && (b.position.y >= position.y) && + ((b.position.x + b.size.x) < (position.x + size.x)) && + ((b.position.y + b.size.y) < (position.y + size.y)); + } + + public Rect2 expand(Vector2 to) + { + Rect2 expanded = this; + + Vector2 begin = expanded.position; + Vector2 end = expanded.position + expanded.size; + + if (to.x < begin.x) + begin.x = to.x; + if (to.y < begin.y) + begin.y = to.y; + + if (to.x > end.x) + end.x = to.x; + if (to.y > end.y) + end.y = to.y; + + expanded.position = begin; + expanded.size = end - begin; + + return expanded; + } + + public float get_area() + { + return size.x * size.y; + } + + public Rect2 grow(float by) + { + Rect2 g = this; + + g.position.x -= by; + g.position.y -= by; + g.size.x += by * 2; + g.size.y += by * 2; + + return g; + } + + public Rect2 grow_individual(float left, float top, float right, float bottom) + { + Rect2 g = this; + + g.position.x -= left; + g.position.y -= top; + g.size.x += left + right; + g.size.y += top + bottom; + + return g; + } + + public Rect2 grow_margin(int margin, float by) + { + Rect2 g = this; + + g.grow_individual((GD.MARGIN_LEFT == margin) ? by : 0, + (GD.MARGIN_TOP == margin) ? by : 0, + (GD.MARGIN_RIGHT == margin) ? by : 0, + (GD.MARGIN_BOTTOM == margin) ? by : 0); + + return g; + } + + public bool has_no_area() + { + return size.x <= 0 || size.y <= 0; + } + + public bool has_point(Vector2 point) + { + if (point.x < position.x) + return false; + if (point.y < position.y) + return false; + + if (point.x >= (position.x + size.x)) + return false; + if (point.y >= (position.y + size.y)) + return false; + + return true; + } + + public bool intersects(Rect2 b) + { + if (position.x > (b.position.x + b.size.x)) + return false; + if ((position.x + size.x) < b.position.x) + return false; + if (position.y > (b.position.y + b.size.y)) + return false; + if ((position.y + size.y) < b.position.y) + return false; + + return true; + } + + public Rect2 merge(Rect2 b) + { + Rect2 newRect; + + newRect.position.x = Mathf.min(b.position.x, position.x); + newRect.position.y = Mathf.min(b.position.y, position.y); + + newRect.size.x = Mathf.max(b.position.x + b.size.x, position.x + size.x); + newRect.size.y = Mathf.max(b.position.y + b.size.y, position.y + size.y); + + newRect.size = newRect.size - newRect.position; // Make relative again + + return newRect; + } + + public Rect2(Vector2 position, Vector2 size) + { + this.position = position; + this.size = size; + } + + public Rect2(float x, float y, float width, float height) + { + this.position = new Vector2(x, y); + this.size = new Vector2(width, height); + } + + public static bool operator ==(Rect2 left, Rect2 right) + { + return left.Equals(right); + } + + public static bool operator !=(Rect2 left, Rect2 right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Rect2) + { + return Equals((Rect2)obj); + } + + return false; + } + + public bool Equals(Rect2 other) + { + return position.Equals(other.position) && size.Equals(other.size); + } + + public override int GetHashCode() + { + return position.GetHashCode() ^ size.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1})", new object[] + { + this.position.ToString(), + this.size.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1})", new object[] + { + this.position.ToString(format), + this.size.ToString(format) + }); + } + } +}
\ No newline at end of file diff --git a/modules/mono/glue/cs_files/Rect3.cs b/modules/mono/glue/cs_files/Rect3.cs new file mode 100644 index 0000000000..0d25de1ec6 --- /dev/null +++ b/modules/mono/glue/cs_files/Rect3.cs @@ -0,0 +1,477 @@ +using System;
+
+// file: core/math/rect3.h
+// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
+// file: core/math/rect3.cpp
+// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0
+// file: core/variant_call.cpp
+// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685
+
+namespace Godot
+{
+ public struct Rect3 : IEquatable<Rect3>
+ {
+ private Vector3 position;
+ private Vector3 size;
+
+ public Vector3 Position
+ {
+ get
+ {
+ return position;
+ }
+ }
+
+ public Vector3 Size
+ {
+ get
+ {
+ return size;
+ }
+ }
+
+ public Vector3 End
+ {
+ get
+ {
+ return position + size;
+ }
+ }
+
+ public bool encloses(Rect3 with)
+ {
+ Vector3 src_min = position;
+ Vector3 src_max = position + size;
+ Vector3 dst_min = with.position;
+ Vector3 dst_max = with.position + with.size;
+
+ return ((src_min.x <= dst_min.x) &&
+ (src_max.x > dst_max.x) &&
+ (src_min.y <= dst_min.y) &&
+ (src_max.y > dst_max.y) &&
+ (src_min.z <= dst_min.z) &&
+ (src_max.z > dst_max.z));
+ }
+
+ public Rect3 expand(Vector3 to_point)
+ {
+ Vector3 begin = position;
+ Vector3 end = position + size;
+
+ if (to_point.x < begin.x)
+ begin.x = to_point.x;
+ if (to_point.y < begin.y)
+ begin.y = to_point.y;
+ if (to_point.z < begin.z)
+ begin.z = to_point.z;
+
+ if (to_point.x > end.x)
+ end.x = to_point.x;
+ if (to_point.y > end.y)
+ end.y = to_point.y;
+ if (to_point.z > end.z)
+ end.z = to_point.z;
+
+ return new Rect3(begin, end - begin);
+ }
+
+ public float get_area()
+ {
+ return size.x * size.y * size.z;
+ }
+
+ public Vector3 get_endpoint(int idx)
+ {
+ switch (idx)
+ {
+ case 0:
+ return new Vector3(position.x, position.y, position.z);
+ case 1:
+ return new Vector3(position.x, position.y, position.z + size.z);
+ case 2:
+ return new Vector3(position.x, position.y + size.y, position.z);
+ case 3:
+ return new Vector3(position.x, position.y + size.y, position.z + size.z);
+ case 4:
+ return new Vector3(position.x + size.x, position.y, position.z);
+ case 5:
+ return new Vector3(position.x + size.x, position.y, position.z + size.z);
+ case 6:
+ return new Vector3(position.x + size.x, position.y + size.y, position.z);
+ case 7:
+ return new Vector3(position.x + size.x, position.y + size.y, position.z + size.z);
+ default:
+ throw new ArgumentOutOfRangeException(nameof(idx), String.Format("Index is {0}, but a value from 0 to 7 is expected.", idx));
+ }
+ }
+
+ public Vector3 get_longest_axis()
+ {
+ Vector3 axis = new Vector3(1f, 0f, 0f);
+ float max_size = size.x;
+
+ if (size.y > max_size)
+ {
+ axis = new Vector3(0f, 1f, 0f);
+ max_size = size.y;
+ }
+
+ if (size.z > max_size)
+ {
+ axis = new Vector3(0f, 0f, 1f);
+ max_size = size.z;
+ }
+
+ return axis;
+ }
+
+ public Vector3.Axis get_longest_axis_index()
+ {
+ Vector3.Axis axis = Vector3.Axis.X;
+ float max_size = size.x;
+
+ if (size.y > max_size)
+ {
+ axis = Vector3.Axis.Y;
+ max_size = size.y;
+ }
+
+ if (size.z > max_size)
+ {
+ axis = Vector3.Axis.Z;
+ max_size = size.z;
+ }
+
+ return axis;
+ }
+
+ public float get_longest_axis_size()
+ {
+ float max_size = size.x;
+
+ if (size.y > max_size)
+ max_size = size.y;
+
+ if (size.z > max_size)
+ max_size = size.z;
+
+ return max_size;
+ }
+
+ public Vector3 get_shortest_axis()
+ {
+ Vector3 axis = new Vector3(1f, 0f, 0f);
+ float max_size = size.x;
+
+ if (size.y < max_size)
+ {
+ axis = new Vector3(0f, 1f, 0f);
+ max_size = size.y;
+ }
+
+ if (size.z < max_size)
+ {
+ axis = new Vector3(0f, 0f, 1f);
+ max_size = size.z;
+ }
+
+ return axis;
+ }
+
+ public Vector3.Axis get_shortest_axis_index()
+ {
+ Vector3.Axis axis = Vector3.Axis.X;
+ float max_size = size.x;
+
+ if (size.y < max_size)
+ {
+ axis = Vector3.Axis.Y;
+ max_size = size.y;
+ }
+
+ if (size.z < max_size)
+ {
+ axis = Vector3.Axis.Z;
+ max_size = size.z;
+ }
+
+ return axis;
+ }
+
+ public float get_shortest_axis_size()
+ {
+ float max_size = size.x;
+
+ if (size.y < max_size)
+ max_size = size.y;
+
+ if (size.z < max_size)
+ max_size = size.z;
+
+ return max_size;
+ }
+
+ public Vector3 get_support(Vector3 dir)
+ {
+ Vector3 half_extents = size * 0.5f;
+ Vector3 ofs = position + half_extents;
+
+ return ofs + new Vector3(
+ (dir.x > 0f) ? -half_extents.x : half_extents.x,
+ (dir.y > 0f) ? -half_extents.y : half_extents.y,
+ (dir.z > 0f) ? -half_extents.z : half_extents.z);
+ }
+
+ public Rect3 grow(float by)
+ {
+ Rect3 res = this;
+
+ res.position.x -= by;
+ res.position.y -= by;
+ res.position.z -= by;
+ res.size.x += 2.0f * by;
+ res.size.y += 2.0f * by;
+ res.size.z += 2.0f * by;
+
+ return res;
+ }
+
+ public bool has_no_area()
+ {
+ return size.x <= 0f || size.y <= 0f || size.z <= 0f;
+ }
+
+ public bool has_no_surface()
+ {
+ return size.x <= 0f && size.y <= 0f && size.z <= 0f;
+ }
+
+ public bool has_point(Vector3 point)
+ {
+ if (point.x < position.x)
+ return false;
+ if (point.y < position.y)
+ return false;
+ if (point.z < position.z)
+ return false;
+ if (point.x > position.x + size.x)
+ return false;
+ if (point.y > position.y + size.y)
+ return false;
+ if (point.z > position.z + size.z)
+ return false;
+
+ return true;
+ }
+
+ public Rect3 intersection(Rect3 with)
+ {
+ Vector3 src_min = position;
+ Vector3 src_max = position + size;
+ Vector3 dst_min = with.position;
+ Vector3 dst_max = with.position + with.size;
+
+ Vector3 min, max;
+
+ if (src_min.x > dst_max.x || src_max.x < dst_min.x)
+ {
+ return new Rect3();
+ }
+ else
+ {
+ min.x = (src_min.x > dst_min.x) ? src_min.x : dst_min.x;
+ max.x = (src_max.x < dst_max.x) ? src_max.x : dst_max.x;
+ }
+
+ if (src_min.y > dst_max.y || src_max.y < dst_min.y)
+ {
+ return new Rect3();
+ }
+ else
+ {
+ min.y = (src_min.y > dst_min.y) ? src_min.y : dst_min.y;
+ max.y = (src_max.y < dst_max.y) ? src_max.y : dst_max.y;
+ }
+
+ if (src_min.z > dst_max.z || src_max.z < dst_min.z)
+ {
+ return new Rect3();
+ }
+ else
+ {
+ min.z = (src_min.z > dst_min.z) ? src_min.z : dst_min.z;
+ max.z = (src_max.z < dst_max.z) ? src_max.z : dst_max.z;
+ }
+
+ return new Rect3(min, max - min);
+ }
+
+ public bool intersects(Rect3 with)
+ {
+ if (position.x >= (with.position.x + with.size.x))
+ return false;
+ if ((position.x + size.x) <= with.position.x)
+ return false;
+ if (position.y >= (with.position.y + with.size.y))
+ return false;
+ if ((position.y + size.y) <= with.position.y)
+ return false;
+ if (position.z >= (with.position.z + with.size.z))
+ return false;
+ if ((position.z + size.z) <= with.position.z)
+ return false;
+
+ return true;
+ }
+
+ public bool intersects_plane(Plane plane)
+ {
+ Vector3[] points =
+ {
+ new Vector3(position.x, position.y, position.z),
+ new Vector3(position.x, position.y, position.z + size.z),
+ new Vector3(position.x, position.y + size.y, position.z),
+ new Vector3(position.x, position.y + size.y, position.z + size.z),
+ new Vector3(position.x + size.x, position.y, position.z),
+ new Vector3(position.x + size.x, position.y, position.z + size.z),
+ new Vector3(position.x + size.x, position.y + size.y, position.z),
+ new Vector3(position.x + size.x, position.y + size.y, position.z + size.z),
+ };
+
+ bool over = false;
+ bool under = false;
+
+ for (int i = 0; i < 8; i++)
+ {
+ if (plane.distance_to(points[i]) > 0)
+ over = true;
+ else
+ under = true;
+ }
+
+ return under && over;
+ }
+
+ public bool intersects_segment(Vector3 from, Vector3 to)
+ {
+ float min = 0f;
+ float max = 1f;
+
+ for (int i = 0; i < 3; i++)
+ {
+ float seg_from = from[i];
+ float seg_to = to[i];
+ float box_begin = position[i];
+ float box_end = box_begin + size[i];
+ float cmin, cmax;
+
+ if (seg_from < seg_to)
+ {
+ if (seg_from > box_end || seg_to < box_begin)
+ return false;
+
+ float length = seg_to - seg_from;
+ cmin = seg_from < box_begin ? (box_begin - seg_from) / length : 0f;
+ cmax = seg_to > box_end ? (box_end - seg_from) / length : 1f;
+ }
+ else
+ {
+ if (seg_to > box_end || seg_from < box_begin)
+ return false;
+
+ float length = seg_to - seg_from;
+ cmin = seg_from > box_end ? (box_end - seg_from) / length : 0f;
+ cmax = seg_to < box_begin ? (box_begin - seg_from) / length : 1f;
+ }
+
+ if (cmin > min)
+ {
+ min = cmin;
+ }
+
+ if (cmax < max)
+ max = cmax;
+ if (max < min)
+ return false;
+ }
+
+ return true;
+ }
+
+ public Rect3 merge(Rect3 with)
+ {
+ Vector3 beg_1 = position;
+ Vector3 beg_2 = with.position;
+ Vector3 end_1 = new Vector3(size.x, size.y, size.z) + beg_1;
+ Vector3 end_2 = new Vector3(with.size.x, with.size.y, with.size.z) + beg_2;
+
+ Vector3 min = new Vector3(
+ (beg_1.x < beg_2.x) ? beg_1.x : beg_2.x,
+ (beg_1.y < beg_2.y) ? beg_1.y : beg_2.y,
+ (beg_1.z < beg_2.z) ? beg_1.z : beg_2.z
+ );
+
+ Vector3 max = new Vector3(
+ (end_1.x > end_2.x) ? end_1.x : end_2.x,
+ (end_1.y > end_2.y) ? end_1.y : end_2.y,
+ (end_1.z > end_2.z) ? end_1.z : end_2.z
+ );
+
+ return new Rect3(min, max - min);
+ }
+
+ public Rect3(Vector3 position, Vector3 size)
+ {
+ this.position = position;
+ this.size = size;
+ }
+
+ public static bool operator ==(Rect3 left, Rect3 right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Rect3 left, Rect3 right)
+ {
+ return !left.Equals(right);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is Rect3)
+ {
+ return Equals((Rect3)obj);
+ }
+
+ return false;
+ }
+
+ public bool Equals(Rect3 other)
+ {
+ return position == other.position && size == other.size;
+ }
+
+ public override int GetHashCode()
+ {
+ return position.GetHashCode() ^ size.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return String.Format("{0} - {1}", new object[]
+ {
+ this.position.ToString(),
+ this.size.ToString()
+ });
+ }
+
+ public string ToString(string format)
+ {
+ return String.Format("{0} - {1}", new object[]
+ {
+ this.position.ToString(format),
+ this.size.ToString(format)
+ });
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/SignalAwaiter.cs b/modules/mono/glue/cs_files/SignalAwaiter.cs new file mode 100644 index 0000000000..19ccc26e79 --- /dev/null +++ b/modules/mono/glue/cs_files/SignalAwaiter.cs @@ -0,0 +1,59 @@ +using System; + +namespace Godot +{ + public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]> + { + private bool completed = false; + private object[] result = null; + private Action action = null; + + public SignalAwaiter(Godot.Object source, string signal, Godot.Object target) + { + NativeCalls.godot_icall_Object_connect_signal_awaiter( + Godot.Object.GetPtr(source), + signal, Godot.Object.GetPtr(target), this + ); + } + + public bool IsCompleted + { + get + { + return completed; + } + } + + public void OnCompleted(Action action) + { + this.action = action; + } + + public object[] GetResult() + { + return result; + } + + public IAwaiter<object[]> GetAwaiter() + { + return this; + } + + internal void SignalCallback(object[] args) + { + completed = true; + result = args; + + if (action != null) + { + action(); + } + } + + internal void FailureCallback() + { + action = null; + completed = true; + } + } +} diff --git a/modules/mono/glue/cs_files/StringExtensions.cs b/modules/mono/glue/cs_files/StringExtensions.cs new file mode 100644 index 0000000000..96041827aa --- /dev/null +++ b/modules/mono/glue/cs_files/StringExtensions.cs @@ -0,0 +1,962 @@ +//using System; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Security; +using System.Text; +using System.Text.RegularExpressions; + +namespace Godot +{ + public static class StringExtensions + { + private static int get_slice_count(this string instance, string splitter) + { + if (instance.empty() || splitter.empty()) + return 0; + + int pos = 0; + int slices = 1; + + while ((pos = instance.find(splitter, pos)) >= 0) + { + slices++; + pos += splitter.Length; + } + + return slices; + } + + private static string get_slicec(this string instance, char splitter, int slice) + { + if (!instance.empty() && slice >= 0) + { + int i = 0; + int prev = 0; + int count = 0; + + while (true) + { + if (instance[i] == 0 || instance[i] == splitter) + { + if (slice == count) + { + return instance.Substring(prev, i - prev); + } + else + { + count++; + prev = i + 1; + } + } + + i++; + } + } + + return string.Empty; + } + + // <summary> + // If the string is a path to a file, return the path to the file without the extension. + // </summary> + public static string basename(this string instance) + { + int index = instance.LastIndexOf('.'); + + if (index > 0) + return instance.Substring(0, index); + + return instance; + } + + // <summary> + // Return true if the strings begins with the given string. + // </summary> + public static bool begins_with(this string instance, string text) + { + return instance.StartsWith(text); + } + + // <summary> + // Return the bigrams (pairs of consecutive letters) of this string. + // </summary> + public static string[] bigrams(this string instance) + { + string[] b = new string[instance.Length - 1]; + + for (int i = 0; i < b.Length; i++) + { + b[i] = instance.Substring(i, 2); + } + + return b; + } + + // <summary> + // Return a copy of the string with special characters escaped using the C language standard. + // </summary> + public static string c_escape(this string instance) + { + StringBuilder sb = new StringBuilder(string.Copy(instance)); + + sb.Replace("\\", "\\\\"); + sb.Replace("\a", "\\a"); + sb.Replace("\b", "\\b"); + sb.Replace("\f", "\\f"); + sb.Replace("\n", "\\n"); + sb.Replace("\r", "\\r"); + sb.Replace("\t", "\\t"); + sb.Replace("\v", "\\v"); + sb.Replace("\'", "\\'"); + sb.Replace("\"", "\\\""); + sb.Replace("?", "\\?"); + + return sb.ToString(); + } + + // <summary> + // Return a copy of the string with escaped characters replaced by their meanings according to the C language standard. + // </summary> + public static string c_unescape(this string instance) + { + StringBuilder sb = new StringBuilder(string.Copy(instance)); + + sb.Replace("\\a", "\a"); + sb.Replace("\\b", "\b"); + sb.Replace("\\f", "\f"); + sb.Replace("\\n", "\n"); + sb.Replace("\\r", "\r"); + sb.Replace("\\t", "\t"); + sb.Replace("\\v", "\v"); + sb.Replace("\\'", "\'"); + sb.Replace("\\\"", "\""); + sb.Replace("\\?", "?"); + sb.Replace("\\\\", "\\"); + + return sb.ToString(); + } + + // <summary> + // Change the case of some letters. Replace underscores with spaces, convert all letters to lowercase then capitalize first and every letter following the space character. For [code]capitalize camelCase mixed_with_underscores[/code] it will return [code]Capitalize Camelcase Mixed With Underscores[/code]. + // </summary> + public static string capitalize(this string instance) + { + string aux = instance.Replace("_", " ").ToLower(); + string cap = string.Empty; + + for (int i = 0; i < aux.get_slice_count(" "); i++) + { + string slice = aux.get_slicec(' ', i); + if (slice.Length > 0) + { + slice = char.ToUpper(slice[0]) + slice.Substring(1); + if (i > 0) + cap += " "; + cap += slice; + } + } + + return cap; + } + + // <summary> + // Perform a case-sensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater. + // </summary> + public static int casecmp_to(this string instance, string to) + { + if (instance.empty()) + return to.empty() ? 0 : -1; + + if (to.empty()) + return 1; + + int instance_idx = 0; + int to_idx = 0; + + while (true) + { + if (to[to_idx] == 0 && instance[instance_idx] == 0) + return 0; // We're equal + else if (instance[instance_idx] == 0) + return -1; // If this is empty, and the other one is not, then we're less... I think? + else if (to[to_idx] == 0) + return 1; // Otherwise the other one is smaller... + else if (instance[instance_idx] < to[to_idx]) // More than + return -1; + else if (instance[instance_idx] > to[to_idx]) // Less than + return 1; + + instance_idx++; + to_idx++; + } + } + + // <summary> + // Return true if the string is empty. + // </summary> + public static bool empty(this string instance) + { + return string.IsNullOrEmpty(instance); + } + + // <summary> + // Return true if the strings ends with the given string. + // </summary> + public static bool ends_with(this string instance, string text) + { + return instance.EndsWith(text); + } + + // <summary> + // Erase [code]chars[/code] characters from the string starting from [code]pos[/code]. + // </summary> + public static void erase(this StringBuilder instance, int pos, int chars) + { + instance.Remove(pos, chars); + } + + // <summary> + // If the string is a path to a file, return the extension. + // </summary> + public static string extension(this string instance) + { + int pos = instance.find_last("."); + + if (pos < 0) + return instance; + + return instance.Substring(pos + 1, instance.Length); + } + + // <summary> + // Find the first occurrence of a substring, return the starting position of the substring or -1 if not found. Optionally, the initial search index can be passed. + // </summary> + public static int find(this string instance, string what, int from = 0) + { + return instance.IndexOf(what, StringComparison.OrdinalIgnoreCase); + } + + // <summary> + // Find the last occurrence of a substring, return the starting position of the substring or -1 if not found. Optionally, the initial search index can be passed. + // </summary> + public static int find_last(this string instance, string what) + { + return instance.LastIndexOf(what, StringComparison.OrdinalIgnoreCase); + } + + // <summary> + // Find the first occurrence of a substring but search as case-insensitive, return the starting position of the substring or -1 if not found. Optionally, the initial search index can be passed. + // </summary> + public static int findn(this string instance, string what, int from = 0) + { + return instance.IndexOf(what, StringComparison.Ordinal); + } + + // <summary> + // If the string is a path to a file, return the base directory. + // </summary> + public static string get_base_dir(this string instance) + { + int basepos = instance.find("://"); + + string rs = string.Empty; + string @base = string.Empty; + + if (basepos != -1) + { + int end = basepos + 3; + rs = instance.Substring(end, instance.Length); + @base = instance.Substring(0, end); + } + else + { + if (instance.begins_with("/")) + { + rs = instance.Substring(1, instance.Length); + @base = "/"; + } + else + { + rs = instance; + } + } + + int sep = Mathf.max(rs.find_last("/"), rs.find_last("\\")); + + if (sep == -1) + return @base; + + return @base + rs.substr(0, sep); + } + + // <summary> + // If the string is a path to a file, return the file and ignore the base directory. + // </summary> + public static string get_file(this string instance) + { + int sep = Mathf.max(instance.find_last("/"), instance.find_last("\\")); + + if (sep == -1) + return instance; + + return instance.Substring(sep + 1, instance.Length); + } + + // <summary> + // Hash the string and return a 32 bits integer. + // </summary> + public static int hash(this string instance) + { + int index = 0; + int hashv = 5381; + int c; + + while ((c = (int)instance[index++]) != 0) + hashv = ((hashv << 5) + hashv) + c; // hash * 33 + c + + return hashv; + } + + // <summary> + // Convert a string containing an hexadecimal number into an int. + // </summary> + public static int hex_to_int(this string instance) + { + int sign = 1; + + if (instance[0] == '-') + { + sign = -1; + instance = instance.Substring(1); + } + + if (!instance.StartsWith("0x")) + return 0; + + return sign * int.Parse(instance.Substring(2), NumberStyles.HexNumber); + } + + // <summary> + // Insert a substring at a given position. + // </summary> + public static string insert(this string instance, int pos, string what) + { + return instance.Insert(pos, what); + } + + // <summary> + // If the string is a path to a file or directory, return true if the path is absolute. + // </summary> + public static bool is_abs_path(this string instance) + { + return System.IO.Path.IsPathRooted(instance); + } + + // <summary> + // If the string is a path to a file or directory, return true if the path is relative. + // </summary> + public static bool is_rel_path(this string instance) + { + return !System.IO.Path.IsPathRooted(instance); + } + + // <summary> + // Check whether this string is a subsequence of the given string. + // </summary> + public static bool is_subsequence_of(this string instance, string text, bool case_insensitive) + { + int len = instance.Length; + + if (len == 0) + return true; // Technically an empty string is subsequence of any string + + if (len > text.Length) + return false; + + int src = 0; + int tgt = 0; + + while (instance[src] != 0 && text[tgt] != 0) + { + bool match = false; + + if (case_insensitive) + { + char srcc = char.ToLower(instance[src]); + char tgtc = char.ToLower(text[tgt]); + match = srcc == tgtc; + } + else + { + match = instance[src] == text[tgt]; + } + if (match) + { + src++; + if (instance[src] == 0) + return true; + } + + tgt++; + } + + return false; + } + + // <summary> + // Check whether this string is a subsequence of the given string, considering case. + // </summary> + public static bool is_subsequence_of(this string instance, string text) + { + return instance.is_subsequence_of(text, false); + } + + // <summary> + // Check whether this string is a subsequence of the given string, without considering case. + // </summary> + public static bool is_subsequence_ofi(this string instance, string text) + { + return instance.is_subsequence_of(text, true); + } + + // <summary> + // Check whether the string contains a valid float. + // </summary> + public static bool is_valid_float(this string instance) + { + float f; + return float.TryParse(instance, out f); + } + + // <summary> + // Check whether the string contains a valid color in HTML notation. + // </summary> + public static bool is_valid_html_color(this string instance) + { + return Color.html_is_valid(instance); + } + + // <summary> + // Check whether the string is a valid identifier. As is common in programming languages, a valid identifier may contain only letters, digits and underscores (_) and the first character may not be a digit. + // </summary> + public static bool is_valid_identifier(this string instance) + { + int len = instance.Length; + + if (len == 0) + return false; + + for (int i = 0; i < len; i++) + { + if (i == 0) + { + if (instance[0] >= '0' && instance[0] <= '9') + return false; // Don't start with number plz + } + + bool valid_char = (instance[i] >= '0' && instance[i] <= '9') || (instance[i] >= 'a' && instance[i] <= 'z') || (instance[i] >= 'A' && instance[i] <= 'Z') || instance[i] == '_'; + + if (!valid_char) + return false; + } + + return true; + } + + // <summary> + // Check whether the string contains a valid integer. + // </summary> + public static bool is_valid_integer(this string instance) + { + int f; + return int.TryParse(instance, out f); + } + + // <summary> + // Check whether the string contains a valid IP address. + // </summary> + public static bool is_valid_ip_address(this string instance) + { + string[] ip = instance.split("."); + + if (ip.Length != 4) + return false; + + for (int i = 0; i < ip.Length; i++) + { + string n = ip[i]; + if (!n.is_valid_integer()) + return false; + + int val = n.to_int(); + if (val < 0 || val > 255) + return false; + } + + return true; + } + + // <summary> + // Return a copy of the string with special characters escaped using the JSON standard. + // </summary> + public static string json_escape(this string instance) + { + StringBuilder sb = new StringBuilder(string.Copy(instance)); + + sb.Replace("\\", "\\\\"); + sb.Replace("\b", "\\b"); + sb.Replace("\f", "\\f"); + sb.Replace("\n", "\\n"); + sb.Replace("\r", "\\r"); + sb.Replace("\t", "\\t"); + sb.Replace("\v", "\\v"); + sb.Replace("\"", "\\\""); + + return sb.ToString(); + } + + // <summary> + // Return an amount of characters from the left of the string. + // </summary> + public static string left(this string instance, int pos) + { + if (pos <= 0) + return string.Empty; + + if (pos >= instance.Length) + return instance; + + return instance.Substring(0, pos); + } + + /// <summary> + /// Return the length of the string in characters. + /// </summary> + public static int length(this string instance) + { + return instance.Length; + } + + // <summary> + // Do a simple expression match, where '*' matches zero or more arbitrary characters and '?' matches any single character except '.'. + // </summary> + public static bool expr_match(this string instance, string expr, bool case_sensitive) + { + if (expr.Length == 0 || instance.Length == 0) + return false; + + switch (expr[0]) + { + case '\0': + return instance[0] == 0; + case '*': + return expr_match(expr + 1, instance, case_sensitive) || (instance[0] != 0 && expr_match(expr, instance + 1, case_sensitive)); + case '?': + return instance[0] != 0 && instance[0] != '.' && expr_match(expr + 1, instance + 1, case_sensitive); + default: + return (case_sensitive ? instance[0] == expr[0] : char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && + expr_match(expr + 1, instance + 1, case_sensitive); + } + } + + // <summary> + // Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]). + // </summary> + public static bool match(this string instance, string expr) + { + return instance.expr_match(expr, true); + } + + // <summary> + // Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]). + // </summary> + public static bool matchn(this string instance, string expr) + { + return instance.expr_match(expr, false); + } + + // <summary> + // Return the MD5 hash of the string as an array of bytes. + // </summary> + public static byte[] md5_buffer(this string instance) + { + return NativeCalls.godot_icall_String_md5_buffer(instance); + } + + // <summary> + // Return the MD5 hash of the string as a string. + // </summary> + public static string md5_text(this string instance) + { + return NativeCalls.godot_icall_String_md5_text(instance); + } + + // <summary> + // Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater. + // </summary> + public static int nocasecmp_to(this string instance, string to) + { + if (instance.empty()) + return to.empty() ? 0 : -1; + + if (to.empty()) + return 1; + + int instance_idx = 0; + int to_idx = 0; + + while (true) + { + if (to[to_idx] == 0 && instance[instance_idx] == 0) + return 0; // We're equal + else if (instance[instance_idx] == 0) + return -1; // If this is empty, and the other one is not, then we're less... I think? + else if (to[to_idx] == 0) + return 1; // Otherwise the other one is smaller.. + else if (char.ToUpper(instance[instance_idx]) < char.ToUpper(to[to_idx])) // More than + return -1; + else if (char.ToUpper(instance[instance_idx]) > char.ToUpper(to[to_idx])) // Less than + return 1; + + instance_idx++; + to_idx++; + } + } + + // <summary> + // Return the character code at position [code]at[/code]. + // </summary> + public static int ord_at(this string instance, int at) + { + return instance[at]; + } + + // <summary> + // Format a number to have an exact number of [code]digits[/code] after the decimal point. + // </summary> + public static string pad_decimals(this string instance, int digits) + { + int c = instance.find("."); + + if (c == -1) + { + if (digits <= 0) + return instance; + + instance += "."; + c = instance.Length - 1; + } + else + { + if (digits <= 0) + return instance.Substring(0, c); + } + + if (instance.Length - (c + 1) > digits) + { + instance = instance.Substring(0, c + digits + 1); + } + else + { + while (instance.Length - (c + 1) < digits) + { + instance += "0"; + } + } + + return instance; + } + + // <summary> + // Format a number to have an exact number of [code]digits[/code] before the decimal point. + // </summary> + public static string pad_zeros(this string instance, int digits) + { + string s = instance; + int end = s.find("."); + + if (end == -1) + end = s.Length; + + if (end == 0) + return s; + + int begin = 0; + + while (begin < end && (s[begin] < '0' || s[begin] > '9')) + { + begin++; + } + + if (begin >= end) + return s; + + while (end - begin < digits) + { + s = s.Insert(begin, "0"); + end++; + } + + return s; + } + + // <summary> + // Decode a percent-encoded string. See [method percent_encode]. + // </summary> + public static string percent_decode(this string instance) + { + return Uri.UnescapeDataString(instance); + } + + // <summary> + // Percent-encode a string. This is meant to encode parameters in a URL when sending a HTTP GET request and bodies of form-urlencoded POST request. + // </summary> + public static string percent_encode(this string instance) + { + return Uri.EscapeDataString(instance); + } + + // <summary> + // If the string is a path, this concatenates [code]file[/code] at the end of the string as a subpath. E.g. [code]"this/is".plus_file("path") == "this/is/path"[/code]. + // </summary> + public static string plus_file(this string instance, string file) + { + if (instance.Length > 0 && instance[instance.Length - 1] == '/') + return instance + file; + else + return instance + "/" + file; + } + + // <summary> + // Replace occurrences of a substring for different ones inside the string. + // </summary> + public static string replace(this string instance, string what, string forwhat) + { + return instance.Replace(what, forwhat); + } + + // <summary> + // Replace occurrences of a substring for different ones inside the string, but search case-insensitive. + // </summary> + public static string replacen(this string instance, string what, string forwhat) + { + return Regex.Replace(instance, what, forwhat, RegexOptions.IgnoreCase); + } + + // <summary> + // Perform a search for a substring, but start from the end of the string instead of the beginning. + // </summary> + public static int rfind(this string instance, string what, int from = -1) + { + return NativeCalls.godot_icall_String_rfind(instance, what, from); + } + + // <summary> + // Perform a search for a substring, but start from the end of the string instead of the beginning. Also search case-insensitive. + // </summary> + public static int rfindn(this string instance, string what, int from = -1) + { + return NativeCalls.godot_icall_String_rfindn(instance, what, from); + } + + // <summary> + // Return the right side of the string from a given position. + // </summary> + public static string right(this string instance, int pos) + { + if (pos >= instance.Length) + return instance; + + if (pos < 0) + return string.Empty; + + return instance.Substring(pos, (instance.Length - pos)); + } + + public static byte[] sha256_buffer(this string instance) + { + return NativeCalls.godot_icall_String_sha256_buffer(instance); + } + + // <summary> + // Return the SHA-256 hash of the string as a string. + // </summary> + public static string sha256_text(this string instance) + { + return NativeCalls.godot_icall_String_sha256_text(instance); + } + + // <summary> + // Return the similarity index of the text compared to this string. 1 means totally similar and 0 means totally dissimilar. + // </summary> + public static float similarity(this string instance, string text) + { + if (instance == text) + { + // Equal strings are totally similar + return 1.0f; + } + if (instance.Length < 2 || text.Length < 2) + { + // No way to calculate similarity without a single bigram + return 0.0f; + } + + string[] src_bigrams = instance.bigrams(); + string[] tgt_bigrams = text.bigrams(); + + int src_size = src_bigrams.Length; + int tgt_size = tgt_bigrams.Length; + + float sum = src_size + tgt_size; + float inter = 0; + + for (int i = 0; i < src_size; i++) + { + for (int j = 0; j < tgt_size; j++) + { + if (src_bigrams[i] == tgt_bigrams[j]) + { + inter++; + break; + } + } + } + + return (2.0f * inter) / sum; + } + + // <summary> + // Split the string by a divisor string, return an array of the substrings. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",". + // </summary> + public static string[] split(this string instance, string divisor, bool allow_empty = true) + { + return instance.Split(new string[] { divisor }, StringSplitOptions.RemoveEmptyEntries); + } + + // <summary> + // Split the string in floats by using a divisor string, return an array of the substrings. Example "1,2.5,3" will return [1,2.5,3] if split by ",". + // </summary> + public static float[] split_floats(this string instance, string divisor, bool allow_empty = true) + { + List<float> ret = new List<float>(); + int from = 0; + int len = instance.Length; + + while (true) + { + int end = instance.find(divisor, from); + if (end < 0) + end = len; + if (allow_empty || (end > from)) + ret.Add(float.Parse(instance.Substring(from))); + if (end == len) + break; + + from = end + divisor.Length; + } + + return ret.ToArray(); + } + + private static readonly char[] non_printable = { + (char)00, (char)01, (char)02, (char)03, (char)04, (char)05, + (char)06, (char)07, (char)08, (char)09, (char)10, (char)11, + (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, + (char)18, (char)19, (char)20, (char)21, (char)22, (char)23, + (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, + (char)30, (char)31, (char)32 + }; + + // <summary> + // Return a copy of the string stripped of any non-printable character at the beginning and the end. The optional arguments are used to toggle stripping on the left and right edges respectively. + // </summary> + public static string strip_edges(this string instance, bool left = true, bool right = true) + { + if (left) + { + if (right) + return instance.Trim(non_printable); + else + return instance.TrimStart(non_printable); + } + else + { + return instance.TrimEnd(non_printable); + } + } + + // <summary> + // Return part of the string from the position [code]from[/code], with length [code]len[/code]. + // </summary> + public static string substr(this string instance, int from, int len) + { + return instance.Substring(from, len); + } + + // <summary> + // Convert the String (which is a character array) to PoolByteArray (which is an array of bytes). The conversion is speeded up in comparison to to_utf8() with the assumption that all the characters the String contains are only ASCII characters. + // </summary> + public static byte[] to_ascii(this string instance) + { + return Encoding.ASCII.GetBytes(instance); + } + + // <summary> + // Convert a string, containing a decimal number, into a [code]float[/code]. + // </summary> + public static float to_float(this string instance) + { + return float.Parse(instance); + } + + // <summary> + // Convert a string, containing an integer number, into an [code]int[/code]. + // </summary> + public static int to_int(this string instance) + { + return int.Parse(instance); + } + + // <summary> + // Return the string converted to lowercase. + // </summary> + public static string to_lower(this string instance) + { + return instance.ToLower(); + } + + // <summary> + // Return the string converted to uppercase. + // </summary> + public static string to_upper(this string instance) + { + return instance.ToUpper(); + } + + // <summary> + // Convert the String (which is an array of characters) to PoolByteArray (which is an array of bytes). The conversion is a bit slower than to_ascii(), but supports all UTF-8 characters. Therefore, you should prefer this function over to_ascii(). + // </summary> + public static byte[] to_utf8(this string instance) + { + return Encoding.UTF8.GetBytes(instance); + } + + // <summary> + // Return a copy of the string with special characters escaped using the XML standard. + // </summary> + public static string xml_escape(this string instance) + { + return SecurityElement.Escape(instance); + } + + // <summary> + // Return a copy of the string with escaped characters replaced by their meanings according to the XML standard. + // </summary> + public static string xml_unescape(this string instance) + { + return SecurityElement.FromString(instance).Text; + } + } +} diff --git a/modules/mono/glue/cs_files/ToolAttribute.cs b/modules/mono/glue/cs_files/ToolAttribute.cs new file mode 100644 index 0000000000..0275982c7f --- /dev/null +++ b/modules/mono/glue/cs_files/ToolAttribute.cs @@ -0,0 +1,7 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Class)] + public class ToolAttribute : Attribute {} +} diff --git a/modules/mono/glue/cs_files/Transform.cs b/modules/mono/glue/cs_files/Transform.cs new file mode 100644 index 0000000000..74271e758b --- /dev/null +++ b/modules/mono/glue/cs_files/Transform.cs @@ -0,0 +1,174 @@ +using System; +using System.Runtime.InteropServices; + +namespace Godot +{ + [StructLayout(LayoutKind.Sequential)] + public struct Transform : IEquatable<Transform> + { + public Basis basis; + public Vector3 origin; + + public Transform affine_inverse() + { + Basis basisInv = basis.inverse(); + return new Transform(basisInv, basisInv.xform(-origin)); + } + + public Transform inverse() + { + Basis basisTr = basis.transposed(); + return new Transform(basisTr, basisTr.xform(-origin)); + } + + public Transform looking_at(Vector3 target, Vector3 up) + { + Transform t = this; + t.set_look_at(origin, target, up); + return t; + } + + public Transform orthonormalized() + { + return new Transform(basis.orthonormalized(), origin); + } + + public Transform rotated(Vector3 axis, float phi) + { + return new Transform(new Basis(axis, phi), new Vector3()) * this; + } + + public Transform scaled(Vector3 scale) + { + return new Transform(basis.scaled(scale), origin * scale); + } + + public void set_look_at(Vector3 eye, Vector3 target, Vector3 up) + { + // Make rotation matrix + // Z vector + Vector3 zAxis = eye - target; + + zAxis.normalize(); + + Vector3 yAxis = up; + + Vector3 xAxis = yAxis.cross(zAxis); + + // Recompute Y = Z cross X + yAxis = zAxis.cross(xAxis); + + xAxis.normalize(); + yAxis.normalize(); + + basis = Basis.create_from_axes(xAxis, yAxis, zAxis); + + origin = eye; + } + + public Transform translated(Vector3 ofs) + { + return new Transform(basis, new Vector3 + ( + origin[0] += basis[0].dot(ofs), + origin[1] += basis[1].dot(ofs), + origin[2] += basis[2].dot(ofs) + )); + } + + public Vector3 xform(Vector3 v) + { + return new Vector3 + ( + basis[0].dot(v) + origin.x, + basis[1].dot(v) + origin.y, + basis[2].dot(v) + origin.z + ); + } + + public Vector3 xform_inv(Vector3 v) + { + Vector3 vInv = v - origin; + + return new Vector3 + ( + (basis[0, 0] * vInv.x) + (basis[1, 0] * vInv.y) + (basis[2, 0] * vInv.z), + (basis[0, 1] * vInv.x) + (basis[1, 1] * vInv.y) + (basis[2, 1] * vInv.z), + (basis[0, 2] * vInv.x) + (basis[1, 2] * vInv.y) + (basis[2, 2] * vInv.z) + ); + } + + public Transform(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis, Vector3 origin) + { + this.basis = Basis.create_from_axes(xAxis, yAxis, zAxis); + this.origin = origin; + } + + public Transform(Quat quat, Vector3 origin) + { + this.basis = new Basis(quat); + this.origin = origin; + } + + public Transform(Basis basis, Vector3 origin) + { + this.basis = basis; + this.origin = origin; + } + + public static Transform operator *(Transform left, Transform right) + { + left.origin = left.xform(right.origin); + left.basis *= right.basis; + return left; + } + + public static bool operator ==(Transform left, Transform right) + { + return left.Equals(right); + } + + public static bool operator !=(Transform left, Transform right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Transform) + { + return Equals((Transform)obj); + } + + return false; + } + + public bool Equals(Transform other) + { + return basis.Equals(other.basis) && origin.Equals(other.origin); + } + + public override int GetHashCode() + { + return basis.GetHashCode() ^ origin.GetHashCode(); + } + + public override string ToString() + { + return String.Format("{0} - {1}", new object[] + { + this.basis.ToString(), + this.origin.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("{0} - {1}", new object[] + { + this.basis.ToString(format), + this.origin.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/cs_files/Transform2D.cs b/modules/mono/glue/cs_files/Transform2D.cs new file mode 100644 index 0000000000..526dc767c6 --- /dev/null +++ b/modules/mono/glue/cs_files/Transform2D.cs @@ -0,0 +1,356 @@ +using System; +using System.Runtime.InteropServices; + +namespace Godot +{ + [StructLayout(LayoutKind.Sequential)] + public struct Transform2D : IEquatable<Transform2D> + { + private static readonly Transform2D identity = new Transform2D + ( + new Vector2(1f, 0f), + new Vector2(0f, 1f), + new Vector2(0f, 0f) + ); + + public Vector2 x; + public Vector2 y; + public Vector2 o; + + public static Transform2D Identity + { + get { return identity; } + } + + public Vector2 Origin + { + get { return o; } + } + + public float Rotation + { + get { return Mathf.atan2(y.x, o.y); } + } + + public Vector2 Scale + { + get { return new Vector2(x.length(), y.length()); } + } + + public Vector2 this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return o; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + case 2: + o = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + + public float this[int index, int axis] + { + get + { + switch (index) + { + case 0: + return x[axis]; + case 1: + return y[axis]; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x[axis] = value; + return; + case 1: + y[axis] = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + public Transform2D affine_inverse() + { + Transform2D inv = this; + + float det = this[0, 0] * this[1, 1] - this[1, 0] * this[0, 1]; + + if (det == 0) + { + return new Transform2D + ( + float.NaN, float.NaN, + float.NaN, float.NaN, + float.NaN, float.NaN + ); + } + + float idet = 1.0f / det; + + float temp = this[0, 0]; + this[0, 0] = this[1, 1]; + this[1, 1] = temp; + + this[0] *= new Vector2(idet, -idet); + this[1] *= new Vector2(-idet, idet); + + this[2] = basis_xform(-this[2]); + + return inv; + } + + public Vector2 basis_xform(Vector2 v) + { + return new Vector2(tdotx(v), tdoty(v)); + } + + public Vector2 basis_xform_inv(Vector2 v) + { + return new Vector2(x.dot(v), y.dot(v)); + } + + public Transform2D interpolate_with(Transform2D m, float c) + { + float r1 = Rotation; + float r2 = m.Rotation; + + Vector2 s1 = Scale; + Vector2 s2 = m.Scale; + + // Slerp rotation + Vector2 v1 = new Vector2(Mathf.cos(r1), Mathf.sin(r1)); + Vector2 v2 = new Vector2(Mathf.cos(r2), Mathf.sin(r2)); + + float dot = v1.dot(v2); + + // Clamp dot to [-1, 1] + dot = (dot < -1.0f) ? -1.0f : ((dot > 1.0f) ? 1.0f : dot); + + Vector2 v = new Vector2(); + + if (dot > 0.9995f) + { + // Linearly interpolate to avoid numerical precision issues + v = v1.linear_interpolate(v2, c).normalized(); + } + else + { + float angle = c * Mathf.acos(dot); + Vector2 v3 = (v2 - v1 * dot).normalized(); + v = v1 * Mathf.cos(angle) + v3 * Mathf.sin(angle); + } + + // Extract parameters + Vector2 p1 = Origin; + Vector2 p2 = m.Origin; + + // Construct matrix + Transform2D res = new Transform2D(Mathf.atan2(v.y, v.x), p1.linear_interpolate(p2, c)); + Vector2 scale = s1.linear_interpolate(s2, c); + res.x *= scale; + res.y *= scale; + + return res; + } + + public Transform2D inverse() + { + Transform2D inv = this; + + // Swap + float temp = inv.x.y; + inv.x.y = inv.y.x; + inv.y.x = temp; + + inv.o = inv.basis_xform(-inv.o); + + return inv; + } + + public Transform2D orthonormalized() + { + Transform2D on = this; + + Vector2 onX = on.x; + Vector2 onY = on.y; + + onX.normalize(); + onY = onY - onX * (onX.dot(onY)); + onY.normalize(); + + on.x = onX; + on.y = onY; + + return on; + } + + public Transform2D rotated(float phi) + { + return this * new Transform2D(phi, new Vector2()); + } + + public Transform2D scaled(Vector2 scale) + { + Transform2D copy = this; + copy.x *= scale; + copy.y *= scale; + copy.o *= scale; + return copy; + } + + private float tdotx(Vector2 with) + { + return this[0, 0] * with[0] + this[1, 0] * with[1]; + } + + private float tdoty(Vector2 with) + { + return this[0, 1] * with[0] + this[1, 1] * with[1]; + } + + public Transform2D translated(Vector2 offset) + { + Transform2D copy = this; + copy.o += copy.basis_xform(offset); + return copy; + } + + public Vector2 xform(Vector2 v) + { + return new Vector2(tdotx(v), tdoty(v)) + o; + } + + public Vector2 xform_inv(Vector2 v) + { + Vector2 vInv = v - o; + return new Vector2(x.dot(vInv), y.dot(vInv)); + } + + public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 origin) + { + this.x = xAxis; + this.y = yAxis; + this.o = origin; + } + public Transform2D(float xx, float xy, float yx, float yy, float ox, float oy) + { + this.x = new Vector2(xx, xy); + this.y = new Vector2(yx, yy); + this.o = new Vector2(ox, oy); + } + + public Transform2D(float rot, Vector2 pos) + { + float cr = Mathf.cos(rot); + float sr = Mathf.sin(rot); + x.x = cr; + y.y = cr; + x.y = -sr; + y.x = sr; + o = pos; + } + + public static Transform2D operator *(Transform2D left, Transform2D right) + { + left.o = left.xform(right.o); + + float x0, x1, y0, y1; + + x0 = left.tdotx(right.x); + x1 = left.tdoty(right.x); + y0 = left.tdotx(right.y); + y1 = left.tdoty(right.y); + + left.x.x = x0; + left.x.y = x1; + left.y.x = y0; + left.y.y = y1; + + return left; + } + + public static bool operator ==(Transform2D left, Transform2D right) + { + return left.Equals(right); + } + + public static bool operator !=(Transform2D left, Transform2D right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Transform2D) + { + return Equals((Transform2D)obj); + } + + return false; + } + + public bool Equals(Transform2D other) + { + return x.Equals(other.x) && y.Equals(other.y) && o.Equals(other.o); + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ y.GetHashCode() ^ o.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1}, {2})", new object[] + { + this.x.ToString(), + this.y.ToString(), + this.o.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1}, {2})", new object[] + { + this.x.ToString(format), + this.y.ToString(format), + this.o.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/cs_files/Vector2.cs b/modules/mono/glue/cs_files/Vector2.cs new file mode 100644 index 0000000000..28fedc365b --- /dev/null +++ b/modules/mono/glue/cs_files/Vector2.cs @@ -0,0 +1,362 @@ +using System; +using System.Runtime.InteropServices; + +// file: core/math/math_2d.h +// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 +// file: core/math/math_2d.cpp +// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 +// file: core/variant_call.cpp +// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 + +namespace Godot +{ + [StructLayout(LayoutKind.Sequential)] + public struct Vector2 : IEquatable<Vector2> + { + public float x; + public float y; + + public float this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + internal void normalize() + { + float length = x * x + y * y; + + if (length != 0f) + { + length = Mathf.sqrt(length); + x /= length; + y /= length; + } + } + + private float cross(Vector2 b) + { + return x * b.y - y * b.x; + } + + public Vector2 abs() + { + return new Vector2(Mathf.abs(x), Mathf.abs(y)); + } + + public float angle() + { + return Mathf.atan2(y, x); + } + + public float angle_to(Vector2 to) + { + return Mathf.atan2(cross(to), dot(to)); + } + + public float angle_to_point(Vector2 to) + { + return Mathf.atan2(x - to.x, y - to.y); + } + + public float aspect() + { + return x / y; + } + + public Vector2 bounce(Vector2 n) + { + return -reflect(n); + } + + public Vector2 clamped(float length) + { + Vector2 v = this; + float l = this.length(); + + if (l > 0 && length < l) + { + v /= l; + v *= length; + } + + return v; + } + + public Vector2 cubic_interpolate(Vector2 b, Vector2 preA, Vector2 postB, float t) + { + Vector2 p0 = preA; + Vector2 p1 = this; + Vector2 p2 = b; + Vector2 p3 = postB; + + float t2 = t * t; + float t3 = t2 * t; + + return 0.5f * ((p1 * 2.0f) + + (-p0 + p2) * t + + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 + + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); + } + + public float distance_squared_to(Vector2 to) + { + return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y); + } + + public float distance_to(Vector2 to) + { + return Mathf.sqrt((x - to.x) * (x - to.x) + (y - to.y) * (y - to.y)); + } + + public float dot(Vector2 with) + { + return x * with.x + y * with.y; + } + + public Vector2 floor() + { + return new Vector2(Mathf.floor(x), Mathf.floor(y)); + } + + public bool is_normalized() + { + return Mathf.abs(length_squared() - 1.0f) < Mathf.Epsilon; + } + + public float length() + { + return Mathf.sqrt(x * x + y * y); + } + + public float length_squared() + { + return x * x + y * y; + } + + public Vector2 linear_interpolate(Vector2 b, float t) + { + Vector2 res = this; + + res.x += (t * (b.x - x)); + res.y += (t * (b.y - y)); + + return res; + } + + public Vector2 normalized() + { + Vector2 result = this; + result.normalize(); + return result; + } + + public Vector2 reflect(Vector2 n) + { + return 2.0f * n * dot(n) - this; + } + + public Vector2 rotated(float phi) + { + float rads = angle() + phi; + return new Vector2(Mathf.cos(rads), Mathf.sin(rads)) * length(); + } + + public Vector2 slide(Vector2 n) + { + return this - n * dot(n); + } + + public Vector2 snapped(Vector2 by) + { + return new Vector2(Mathf.stepify(x, by.x), Mathf.stepify(y, by.y)); + } + + public Vector2 tangent() + { + return new Vector2(y, -x); + } + + public Vector2(float x, float y) + { + this.x = x; + this.y = y; + } + + public static Vector2 operator +(Vector2 left, Vector2 right) + { + left.x += right.x; + left.y += right.y; + return left; + } + + public static Vector2 operator -(Vector2 left, Vector2 right) + { + left.x -= right.x; + left.y -= right.y; + return left; + } + + public static Vector2 operator -(Vector2 vec) + { + vec.x = -vec.x; + vec.y = -vec.y; + return vec; + } + + public static Vector2 operator *(Vector2 vec, float scale) + { + vec.x *= scale; + vec.y *= scale; + return vec; + } + + public static Vector2 operator *(float scale, Vector2 vec) + { + vec.x *= scale; + vec.y *= scale; + return vec; + } + + public static Vector2 operator *(Vector2 left, Vector2 right) + { + left.x *= right.x; + left.y *= right.y; + return left; + } + + public static Vector2 operator /(Vector2 vec, float scale) + { + vec.x /= scale; + vec.y /= scale; + return vec; + } + + public static Vector2 operator /(Vector2 left, Vector2 right) + { + left.x /= right.x; + left.y /= right.y; + return left; + } + + public static bool operator ==(Vector2 left, Vector2 right) + { + return left.Equals(right); + } + + public static bool operator !=(Vector2 left, Vector2 right) + { + return !left.Equals(right); + } + + public static bool operator <(Vector2 left, Vector2 right) + { + if (left.x.Equals(right.x)) + { + return left.y < right.y; + } + else + { + return left.x < right.x; + } + } + + public static bool operator >(Vector2 left, Vector2 right) + { + if (left.x.Equals(right.x)) + { + return left.y > right.y; + } + else + { + return left.x > right.x; + } + } + + public static bool operator <=(Vector2 left, Vector2 right) + { + if (left.x.Equals(right.x)) + { + return left.y <= right.y; + } + else + { + return left.x <= right.x; + } + } + + public static bool operator >=(Vector2 left, Vector2 right) + { + if (left.x.Equals(right.x)) + { + return left.y >= right.y; + } + else + { + return left.x >= right.x; + } + } + + public override bool Equals(object obj) + { + if (obj is Vector2) + { + return Equals((Vector2)obj); + } + + return false; + } + + public bool Equals(Vector2 other) + { + return x == other.x && y == other.y; + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ x.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1})", new object[] + { + this.x.ToString(), + this.y.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1})", new object[] + { + this.x.ToString(format), + this.y.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/cs_files/Vector3.cs b/modules/mono/glue/cs_files/Vector3.cs new file mode 100644 index 0000000000..c023cd83cf --- /dev/null +++ b/modules/mono/glue/cs_files/Vector3.cs @@ -0,0 +1,420 @@ +using System; +using System.Runtime.InteropServices; + +// file: core/math/vector3.h +// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0 +// file: core/math/vector3.cpp +// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 +// file: core/variant_call.cpp +// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 + +namespace Godot +{ + [StructLayout(LayoutKind.Sequential)] + public struct Vector3 : IEquatable<Vector3> + { + public enum Axis + { + X = 0, + Y, + Z + } + + public float x; + public float y; + public float z; + + public float this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + case 2: + z = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + internal void normalize() + { + float length = this.length(); + + if (length == 0f) + { + x = y = z = 0f; + } + else + { + x /= length; + y /= length; + z /= length; + } + } + + public Vector3 abs() + { + return new Vector3(Mathf.abs(x), Mathf.abs(y), Mathf.abs(z)); + } + + public float angle_to(Vector3 to) + { + return Mathf.atan2(cross(to).length(), dot(to)); + } + + public Vector3 bounce(Vector3 n) + { + return -reflect(n); + } + + public Vector3 ceil() + { + return new Vector3(Mathf.ceil(x), Mathf.ceil(y), Mathf.ceil(z)); + } + + public Vector3 cross(Vector3 b) + { + return new Vector3 + ( + (y * b.z) - (z * b.y), + (z * b.x) - (x * b.z), + (x * b.y) - (y * b.x) + ); + } + + public Vector3 cubic_interpolate(Vector3 b, Vector3 preA, Vector3 postB, float t) + { + Vector3 p0 = preA; + Vector3 p1 = this; + Vector3 p2 = b; + Vector3 p3 = postB; + + float t2 = t * t; + float t3 = t2 * t; + + return 0.5f * ( + (p1 * 2.0f) + (-p0 + p2) * t + + (2.0f * p0 - 5.0f * p1 + 4f * p2 - p3) * t2 + + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3 + ); + } + + public float distance_squared_to(Vector3 b) + { + return (b - this).length_squared(); + } + + public float distance_to(Vector3 b) + { + return (b - this).length(); + } + + public float dot(Vector3 b) + { + return x * b.x + y * b.y + z * b.z; + } + + public Vector3 floor() + { + return new Vector3(Mathf.floor(x), Mathf.floor(y), Mathf.floor(z)); + } + + public Vector3 inverse() + { + return new Vector3(1.0f / x, 1.0f / y, 1.0f / z); + } + + public bool is_normalized() + { + return Mathf.abs(length_squared() - 1.0f) < Mathf.Epsilon; + } + + public float length() + { + float x2 = x * x; + float y2 = y * y; + float z2 = z * z; + + return Mathf.sqrt(x2 + y2 + z2); + } + + public float length_squared() + { + float x2 = x * x; + float y2 = y * y; + float z2 = z * z; + + return x2 + y2 + z2; + } + + public Vector3 linear_interpolate(Vector3 b, float t) + { + return new Vector3 + ( + x + (t * (b.x - x)), + y + (t * (b.y - y)), + z + (t * (b.z - z)) + ); + } + + public Axis max_axis() + { + return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X); + } + + public Axis min_axis() + { + return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z); + } + + public Vector3 normalized() + { + Vector3 v = this; + v.normalize(); + return v; + } + + public Basis outer(Vector3 b) + { + return new Basis( + new Vector3(x * b.x, x * b.y, x * b.z), + new Vector3(y * b.x, y * b.y, y * b.z), + new Vector3(z * b.x, z * b.y, z * b.z) + ); + } + + public Vector3 reflect(Vector3 n) + { +#if DEBUG + if (!n.is_normalized()) + throw new ArgumentException(String.Format("{0} is not normalized", n), nameof(n)); +#endif + return 2.0f * n * dot(n) - this; + } + + public Vector3 rotated(Vector3 axis, float phi) + { + return new Basis(axis, phi).xform(this); + } + + public Vector3 slide(Vector3 n) + { + return this - n * dot(n); + } + + public Vector3 snapped(Vector3 by) + { + return new Vector3 + ( + Mathf.stepify(x, by.x), + Mathf.stepify(y, by.y), + Mathf.stepify(z, by.z) + ); + } + + public Basis to_diagonal_matrix() + { + return new Basis( + x, 0f, 0f, + 0f, y, 0f, + 0f, 0f, z + ); + } + + public Vector3(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + public static Vector3 operator +(Vector3 left, Vector3 right) + { + left.x += right.x; + left.y += right.y; + left.z += right.z; + return left; + } + + public static Vector3 operator -(Vector3 left, Vector3 right) + { + left.x -= right.x; + left.y -= right.y; + left.z -= right.z; + return left; + } + + public static Vector3 operator -(Vector3 vec) + { + vec.x = -vec.x; + vec.y = -vec.y; + vec.z = -vec.z; + return vec; + } + + public static Vector3 operator *(Vector3 vec, float scale) + { + vec.x *= scale; + vec.y *= scale; + vec.z *= scale; + return vec; + } + + public static Vector3 operator *(float scale, Vector3 vec) + { + vec.x *= scale; + vec.y *= scale; + vec.z *= scale; + return vec; + } + + public static Vector3 operator *(Vector3 left, Vector3 right) + { + left.x *= right.x; + left.y *= right.y; + left.z *= right.z; + return left; + } + + public static Vector3 operator /(Vector3 vec, float scale) + { + vec.x /= scale; + vec.y /= scale; + vec.z /= scale; + return vec; + } + + public static Vector3 operator /(Vector3 left, Vector3 right) + { + left.x /= right.x; + left.y /= right.y; + left.z /= right.z; + return left; + } + + public static bool operator ==(Vector3 left, Vector3 right) + { + return left.Equals(right); + } + + public static bool operator !=(Vector3 left, Vector3 right) + { + return !left.Equals(right); + } + + public static bool operator <(Vector3 left, Vector3 right) + { + if (left.x == right.x) + { + if (left.y == right.y) + return left.z < right.z; + else + return left.y < right.y; + } + + return left.x < right.x; + } + + public static bool operator >(Vector3 left, Vector3 right) + { + if (left.x == right.x) + { + if (left.y == right.y) + return left.z > right.z; + else + return left.y > right.y; + } + + return left.x > right.x; + } + + public static bool operator <=(Vector3 left, Vector3 right) + { + if (left.x == right.x) + { + if (left.y == right.y) + return left.z <= right.z; + else + return left.y < right.y; + } + + return left.x < right.x; + } + + public static bool operator >=(Vector3 left, Vector3 right) + { + if (left.x == right.x) + { + if (left.y == right.y) + return left.z >= right.z; + else + return left.y > right.y; + } + + return left.x > right.x; + } + + public override bool Equals(object obj) + { + if (obj is Vector3) + { + return Equals((Vector3)obj); + } + + return false; + } + + public bool Equals(Vector3 other) + { + return x == other.x && y == other.y && z == other.z; + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1}, {2})", new object[] + { + this.x.ToString(), + this.y.ToString(), + this.z.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1}, {2})", new object[] + { + this.x.ToString(format), + this.y.ToString(format), + this.z.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h new file mode 100644 index 0000000000..0751a0160f --- /dev/null +++ b/modules/mono/glue/glue_header.h @@ -0,0 +1,302 @@ +/*************************************************************************/ +/* glue_header.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "../csharp_script.h" +#include "../mono_gd/gd_mono_class.h" +#include "../mono_gd/gd_mono_internals.h" +#include "../mono_gd/gd_mono_marshal.h" +#include "../signal_awaiter_utils.h" + +#include "bind/core_bind.h" +#include "class_db.h" +#include "io/marshalls.h" +#include "object.h" +#include "os/os.h" +#include "project_settings.h" +#include "reference.h" +#include "variant_parser.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_node.h" +#endif + +#define GODOTSHARP_INSTANCE_OBJECT(m_instance, m_type) \ + static ClassDB::ClassInfo *ci = NULL; \ + if (!ci) { \ + ci = ClassDB::classes.getptr(m_type); \ + } \ + Object *m_instance = ci->creation_func(); + +void godot_icall_Object_Dtor(Object *ptr) { + ERR_FAIL_NULL(ptr); + _GodotSharp::get_singleton()->queue_dispose(ptr); +} + +// -- ClassDB -- + +MethodBind *godot_icall_ClassDB_get_method(MonoString *p_type, MonoString *p_method) { + StringName type(GDMonoMarshal::mono_string_to_godot(p_type)); + StringName method(GDMonoMarshal::mono_string_to_godot(p_method)); + return ClassDB::get_method(type, method); +} + +// -- SignalAwaiter -- + +Error godot_icall_Object_connect_signal_awaiter(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter) { + String signal = GDMonoMarshal::mono_string_to_godot(p_signal); + return SignalAwaiterUtils::connect_signal_awaiter(p_source, signal, p_target, p_awaiter); +} + +// -- NodePath -- + +NodePath *godot_icall_NodePath_Ctor(MonoString *p_path) { + return memnew(NodePath(GDMonoMarshal::mono_string_to_godot(p_path))); +} + +void godot_icall_NodePath_Dtor(NodePath *p_ptr) { + ERR_FAIL_NULL(p_ptr); + _GodotSharp::get_singleton()->queue_dispose(p_ptr); +} + +MonoString *godot_icall_NodePath_operator_String(NodePath *p_np) { + return GDMonoMarshal::mono_string_from_godot(p_np->operator String()); +} + +MonoArray *godot_icall_String_md5_buffer(MonoString *p_str) { + Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_buffer(); + // TODO Check possible Array/Vector<uint8_t> problem? + return GDMonoMarshal::Array_to_mono_array(Variant(ret)); +} + +// -- RID -- + +RID *godot_icall_RID_Ctor(Object *p_from) { + Resource *res_from = Object::cast_to<Resource>(p_from); + + if (res_from) + return memnew(RID(res_from->get_rid())); + + return memnew(RID); +} + +void godot_icall_RID_Dtor(RID *p_ptr) { + ERR_FAIL_NULL(p_ptr); + _GodotSharp::get_singleton()->queue_dispose(p_ptr); +} + +// -- String -- + +MonoString *godot_icall_String_md5_text(MonoString *p_str) { + String ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_text(); + return GDMonoMarshal::mono_string_from_godot(ret); +} + +int godot_icall_String_rfind(MonoString *p_str, MonoString *p_what, int p_from) { + String what = GDMonoMarshal::mono_string_to_godot(p_what); + return GDMonoMarshal::mono_string_to_godot(p_str).rfind(what, p_from); +} + +int godot_icall_String_rfindn(MonoString *p_str, MonoString *p_what, int p_from) { + String what = GDMonoMarshal::mono_string_to_godot(p_what); + return GDMonoMarshal::mono_string_to_godot(p_str).rfindn(what, p_from); +} + +MonoArray *godot_icall_String_sha256_buffer(MonoString *p_str) { + Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_buffer(); + return GDMonoMarshal::Array_to_mono_array(Variant(ret)); +} + +MonoString *godot_icall_String_sha256_text(MonoString *p_str) { + String ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_text(); + return GDMonoMarshal::mono_string_from_godot(ret); +} + +// -- Global Scope -- + +MonoObject *godot_icall_Godot_bytes2var(MonoArray *p_bytes) { + Variant ret; + PoolByteArray varr = GDMonoMarshal::mono_array_to_PoolByteArray(p_bytes); + PoolByteArray::Read r = varr.read(); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + if (err != OK) { + ret = RTR("Not enough bytes for decoding bytes, or invalid format."); + } + return GDMonoMarshal::variant_to_mono_object(ret); +} + +MonoObject *godot_icall_Godot_convert(MonoObject *p_what, int p_type) { + Variant what = GDMonoMarshal::mono_object_to_variant(p_what); + const Variant *args[1] = { &what }; + Variant::CallError ce; + Variant ret = Variant::construct(Variant::Type(p_type), args, 1, ce); + ERR_FAIL_COND_V(ce.error != Variant::CallError::CALL_OK, NULL); + return GDMonoMarshal::variant_to_mono_object(ret); +} + +int godot_icall_Godot_hash(MonoObject *p_var) { + return GDMonoMarshal::mono_object_to_variant(p_var).hash(); +} + +MonoObject *godot_icall_Godot_instance_from_id(int p_instance_id) { + return GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(p_instance_id)); +} + +void godot_icall_Godot_print(MonoArray *p_what) { + Array what = GDMonoMarshal::mono_array_to_Array(p_what); + String str; + for (int i = 0; i < what.size(); i++) + str += what[i].operator String(); + print_line(str); +} + +void godot_icall_Godot_printerr(MonoArray *p_what) { + Array what = GDMonoMarshal::mono_array_to_Array(p_what); + String str; + for (int i = 0; i < what.size(); i++) + str += what[i].operator String(); + OS::get_singleton()->printerr("%s\n", str.utf8().get_data()); +} + +void godot_icall_Godot_printraw(MonoArray *p_what) { + Array what = GDMonoMarshal::mono_array_to_Array(p_what); + String str; + for (int i = 0; i < what.size(); i++) + str += what[i].operator String(); + OS::get_singleton()->print("%s", str.utf8().get_data()); +} + +void godot_icall_Godot_prints(MonoArray *p_what) { + Array what = GDMonoMarshal::mono_array_to_Array(p_what); + String str; + for (int i = 0; i < what.size(); i++) { + if (i) + str += " "; + str += what[i].operator String(); + } + print_line(str); +} + +void godot_icall_Godot_printt(MonoArray *p_what) { + Array what = GDMonoMarshal::mono_array_to_Array(p_what); + String str; + for (int i = 0; i < what.size(); i++) { + if (i) + str += "\t"; + str += what[i].operator String(); + } + print_line(str); +} + +void godot_icall_Godot_seed(int p_seed) { + Math::seed(p_seed); +} + +MonoString *godot_icall_Godot_str(MonoArray *p_what) { + String str; + Array what = GDMonoMarshal::mono_array_to_Array(p_what); + + for (int i = 0; i < what.size(); i++) { + String os = what[i].operator String(); + + if (i == 0) + str = os; + else + str += os; + } + + return GDMonoMarshal::mono_string_from_godot(str); +} + +MonoObject *godot_icall_Godot_str2var(MonoString *p_str) { + Variant ret; + + VariantParser::StreamString ss; + ss.s = GDMonoMarshal::mono_string_to_godot(p_str); + + String errs; + int line; + Error err = VariantParser::parse(&ss, ret, errs, line); + if (err != OK) { + String err_str = "Parse error at line " + itos(line) + ": " + errs; + ERR_PRINTS(err_str); + ret = err_str; + } + + return GDMonoMarshal::variant_to_mono_object(ret); +} + +bool godot_icall_Godot_type_exists(MonoString *p_type) { + return ClassDB::class_exists(GDMonoMarshal::mono_string_to_godot(p_type)); +} + +MonoArray *godot_icall_Godot_var2bytes(MonoObject *p_var) { + Variant var = GDMonoMarshal::mono_object_to_variant(p_var); + + PoolByteArray barr; + int len; + Error err = encode_variant(var, NULL, len); + ERR_EXPLAIN("Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."); + ERR_FAIL_COND_V(err != OK, NULL); + + barr.resize(len); + { + PoolByteArray::Write w = barr.write(); + encode_variant(var, w.ptr(), len); + } + + return GDMonoMarshal::PoolByteArray_to_mono_array(barr); +} + +MonoString *godot_icall_Godot_var2str(MonoObject *p_var) { + String vars; + VariantWriter::write_to_string(GDMonoMarshal::mono_object_to_variant(p_var), vars); + return GDMonoMarshal::mono_string_from_godot(vars); +} + +MonoObject *godot_icall_Godot_weakref(Object *p_obj) { + if (!p_obj) + return NULL; + + Ref<WeakRef> wref; + Reference *ref = Object::cast_to<Reference>(p_obj); + + if (ref) { + REF r = ref; + if (!r.is_valid()) + return NULL; + + wref.instance(); + wref->set_ref(r); + } else { + wref.instance(); + wref->set_obj(p_obj); + } + + return GDMonoUtils::create_managed_for_godot_object(CACHED_CLASS(WeakRef), Reference::get_class_static(), Object::cast_to<Object>(wref.ptr())); +} diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h new file mode 100644 index 0000000000..f941a4d6c5 --- /dev/null +++ b/modules/mono/godotsharp_defs.h @@ -0,0 +1,41 @@ +/*************************************************************************/ +/* godotsharp_defs.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GODOTSHARP_DEFS_H +#define GODOTSHARP_DEFS_H + +#define BINDINGS_NAMESPACE "Godot" +#define BINDINGS_GLOBAL_SCOPE_CLASS "GD" +#define BINDINGS_PTR_FIELD "ptr" +#define BINDINGS_NATIVE_NAME_FIELD "nativeName" +#define API_ASSEMBLY_NAME "GodotSharp" +#define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor" +#define EDITOR_TOOLS_ASSEMBLY_NAME "GodotSharpTools" + +#endif // GODOTSHARP_DEFS_H diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp new file mode 100644 index 0000000000..7cc2168b70 --- /dev/null +++ b/modules/mono/godotsharp_dirs.cpp @@ -0,0 +1,201 @@ +/*************************************************************************/ +/* godotsharp_dirs.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "godotsharp_dirs.h" + +#include "os/os.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#include "os/dir_access.h" +#include "project_settings.h" +#include "version.h" +#endif + +namespace GodotSharpDirs { + +String _get_expected_build_config() { +#ifdef TOOLS_ENABLED + return "Tools"; +#else + +#ifdef DEBUG_ENABLED + return "Debug"; +#else + return "Release"; +#endif + +#endif +} + +String _get_mono_user_dir() { +#ifdef TOOLS_ENABLED + if (EditorSettings::get_singleton()) { + return EditorSettings::get_singleton()->get_settings_path().plus_file("mono"); + } else { + String settings_path; + + String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir(); + DirAccessRef d = DirAccess::create_for_path(exe_dir); + + if (d->file_exists("._sc_") || d->file_exists("_sc_")) { + // contain yourself + settings_path = exe_dir.plus_file("editor_data"); + } else { + if (OS::get_singleton()->has_environment("APPDATA")) { + String app_data = OS::get_singleton()->get_environment("APPDATA").replace("\\", "/"); + settings_path = app_data.plus_file(String(_MKSTR(VERSION_SHORT_NAME)).capitalize()); + } else if (OS::get_singleton()->has_environment("HOME")) { + String home = OS::get_singleton()->get_environment("HOME"); + settings_path = home.plus_file("." + String(_MKSTR(VERSION_SHORT_NAME)).to_lower()); + } + } + + return settings_path.plus_file("mono"); + } +#else + return OS::get_singleton()->get_data_dir().plus_file("mono"); +#endif +} + +class _GodotSharpDirs { + +public: + String res_data_dir; + String res_metadata_dir; + String res_assemblies_dir; + String res_config_dir; + String res_temp_dir; + String res_temp_assemblies_base_dir; + String res_temp_assemblies_dir; + String mono_user_dir; + String mono_logs_dir; + +#ifdef TOOLS_ENABLED + String mono_solutions_dir; + String build_logs_dir; + String sln_filepath; + String csproj_filepath; +#endif + +private: + _GodotSharpDirs() { + res_data_dir = "res://.mono"; + res_metadata_dir = res_data_dir.plus_file("metadata"); + res_assemblies_dir = res_data_dir.plus_file("assemblies"); + res_config_dir = res_data_dir.plus_file("etc").plus_file("mono"); + + // TODO use paths from csproj + res_temp_dir = res_data_dir.plus_file("temp"); + res_temp_assemblies_base_dir = res_temp_dir.plus_file("bin"); + res_temp_assemblies_dir = res_temp_assemblies_base_dir.plus_file(_get_expected_build_config()); + + mono_user_dir = _get_mono_user_dir(); + mono_logs_dir = mono_user_dir.plus_file("mono_logs"); + +#ifdef TOOLS_ENABLED + mono_solutions_dir = mono_user_dir.plus_file("solutions"); + build_logs_dir = mono_user_dir.plus_file("build_logs"); + + String name = ProjectSettings::get_singleton()->get("application/config/name"); + if (name.empty()) { + name = "UnnamedProject"; + } + + String base_path = String("res://") + name; + + sln_filepath = ProjectSettings::get_singleton()->globalize_path(base_path + ".sln"); + csproj_filepath = ProjectSettings::get_singleton()->globalize_path(base_path + ".csproj"); +#endif + } + + _GodotSharpDirs(const _GodotSharpDirs &); + _GodotSharpDirs &operator=(const _GodotSharpDirs &); + +public: + static _GodotSharpDirs &get_singleton() { + static _GodotSharpDirs singleton; + return singleton; + } +}; + +String get_res_data_dir() { + return _GodotSharpDirs::get_singleton().res_data_dir; +} + +String get_res_metadata_dir() { + return _GodotSharpDirs::get_singleton().res_metadata_dir; +} + +String get_res_assemblies_dir() { + return _GodotSharpDirs::get_singleton().res_assemblies_dir; +} + +String get_res_config_dir() { + return _GodotSharpDirs::get_singleton().res_config_dir; +} + +String get_res_temp_dir() { + return _GodotSharpDirs::get_singleton().res_temp_dir; +} + +String get_res_temp_assemblies_base_dir() { + return _GodotSharpDirs::get_singleton().res_temp_assemblies_base_dir; +} + +String get_res_temp_assemblies_dir() { + return _GodotSharpDirs::get_singleton().res_temp_assemblies_dir; +} + +String get_mono_user_dir() { + return _GodotSharpDirs::get_singleton().mono_user_dir; +} + +String get_mono_logs_dir() { + return _GodotSharpDirs::get_singleton().mono_logs_dir; +} + +#ifdef TOOLS_ENABLED +String get_mono_solutions_dir() { + return _GodotSharpDirs::get_singleton().mono_solutions_dir; +} + +String get_build_logs_dir() { + return _GodotSharpDirs::get_singleton().build_logs_dir; +} + +String get_project_sln_path() { + return _GodotSharpDirs::get_singleton().sln_filepath; +} + +String get_project_csproj_path() { + return _GodotSharpDirs::get_singleton().csproj_filepath; +} +#endif +} diff --git a/modules/mono/godotsharp_dirs.h b/modules/mono/godotsharp_dirs.h new file mode 100644 index 0000000000..ba2c065210 --- /dev/null +++ b/modules/mono/godotsharp_dirs.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* godotsharp_dirs.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GODOTSHARP_DIRS_H +#define GODOTSHARP_DIRS_H + +#include "ustring.h" + +namespace GodotSharpDirs { + +String get_res_data_dir(); +String get_res_metadata_dir(); +String get_res_assemblies_dir(); +String get_res_config_dir(); +String get_res_temp_dir(); +String get_res_temp_assemblies_base_dir(); +String get_res_temp_assemblies_dir(); + +String get_mono_user_dir(); +String get_mono_logs_dir(); + +#ifdef TOOLS_ENABLED +String get_mono_solutions_dir(); +String get_build_logs_dir(); +String get_custom_project_settings_dir(); +#endif + +String get_project_sln_path(); +String get_project_csproj_path(); +} + +#endif // GODOTSHARP_DIRS_H diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp new file mode 100644 index 0000000000..d3ad968135 --- /dev/null +++ b/modules/mono/mono_gc_handle.cpp @@ -0,0 +1,77 @@ +/*************************************************************************/ +/* mono_gc_handle.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "mono_gc_handle.h" + +#include "mono_gd/gd_mono.h" + +uint32_t MonoGCHandle::make_strong_handle(MonoObject *p_object) { + + return mono_gchandle_new( + p_object, + false /* do not pin the object */ + ); +} + +uint32_t MonoGCHandle::make_weak_handle(MonoObject *p_object) { + + return mono_gchandle_new_weakref( + p_object, + true /* track_resurrection: allows us to invoke _notification(NOTIFICATION_PREDELETE) while disposing */ + ); +} + +Ref<MonoGCHandle> MonoGCHandle::create_strong(MonoObject *p_object) { + + return memnew(MonoGCHandle(make_strong_handle(p_object))); +} + +Ref<MonoGCHandle> MonoGCHandle::create_weak(MonoObject *p_object) { + + return memnew(MonoGCHandle(make_weak_handle(p_object))); +} + +void MonoGCHandle::release() { + + if (!released && GDMono::get_singleton()->is_runtime_initialized()) { + mono_gchandle_free(handle); + released = true; + } +} + +MonoGCHandle::MonoGCHandle(uint32_t p_handle) { + + released = false; + handle = p_handle; +} + +MonoGCHandle::~MonoGCHandle() { + + release(); +} diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h new file mode 100644 index 0000000000..cf5b6cec21 --- /dev/null +++ b/modules/mono/mono_gc_handle.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* mono_gc_handle.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 CSHARP_GC_HANDLE_H +#define CSHARP_GC_HANDLE_H + +#include <mono/jit/jit.h> + +#include "reference.h" + +class MonoGCHandle : public Reference { + + GDCLASS(MonoGCHandle, Reference) + + bool released; + uint32_t handle; + +public: + static uint32_t make_strong_handle(MonoObject *p_object); + static uint32_t make_weak_handle(MonoObject *p_object); + + static Ref<MonoGCHandle> create_strong(MonoObject *p_object); + static Ref<MonoGCHandle> create_weak(MonoObject *p_object); + + _FORCE_INLINE_ MonoObject *get_target() const { return released ? NULL : mono_gchandle_get_target(handle); } + + _FORCE_INLINE_ void set_handle(uint32_t p_handle) { + handle = p_handle; + released = false; + } + void release(); + + MonoGCHandle(uint32_t p_handle); + ~MonoGCHandle(); +}; + +#endif // CSHARP_GC_HANDLE_H diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp new file mode 100644 index 0000000000..904a8ae2c7 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -0,0 +1,784 @@ +/*************************************************************************/ +/* gd_mono.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "gd_mono.h" + +#include <mono/metadata/exception.h> +#include <mono/metadata/mono-config.h> +#include <mono/metadata/mono-debug.h> +#include <mono/metadata/mono-gc.h> + +#include "os/dir_access.h" +#include "os/file_access.h" +#include "os/os.h" +#include "os/thread.h" +#include "project_settings.h" + +#include "../csharp_script.h" +#include "../utils/path_utils.h" +#include "gd_mono_utils.h" + +#ifdef TOOLS_ENABLED +#include "../editor/godotsharp_editor.h" +#endif + +void gdmono_unhandled_exception_hook(MonoObject *exc, void *user_data) { + + (void)user_data; // UNUSED + + ERR_PRINT(GDMonoUtils::get_exception_name_and_message(exc).utf8()); + mono_print_unhandled_exception(exc); + abort(); +} + +#ifdef MONO_PRINT_HANDLER_ENABLED +void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) { + + if (is_stdout) { + OS::get_singleton()->print(string); + } else { + OS::get_singleton()->printerr(string); + } +} +#endif + +GDMono *GDMono::singleton = NULL; + +#ifdef DEBUG_ENABLED +static bool _wait_for_debugger_msecs(uint32_t p_msecs) { + + do { + if (mono_is_debugger_attached()) + return true; + + int last_tick = OS::get_singleton()->get_ticks_msec(); + + OS::get_singleton()->delay_usec((p_msecs < 25 ? p_msecs : 25) * 1000); + + int tdiff = OS::get_singleton()->get_ticks_msec() - last_tick; + + if (tdiff > p_msecs) { + p_msecs = 0; + } else { + p_msecs -= tdiff; + } + } while (p_msecs > 0); + + return mono_is_debugger_attached(); +} +#endif + +#ifdef TOOLS_ENABLED +// temporary workaround. should be provided from Main::setup/setup2 instead +bool _is_project_manager_requested() { + + List<String> cmdline_args = OS::get_singleton()->get_cmdline_args(); + for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) { + const String &arg = E->get(); + if (arg == "-p" || arg == "--project-manager") + return true; + } + + return false; +} +#endif + +#ifdef DEBUG_ENABLED +void gdmono_debug_init() { + + mono_debug_init(MONO_DEBUG_FORMAT_MONO); + + int da_port = GLOBAL_DEF("mono/debugger_agent/port", 23685); + bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false); + int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000); + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint() || + ProjectSettings::get_singleton()->get_resource_path().empty() || + _is_project_manager_requested()) { + return; + } +#endif + + CharString da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) + + ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n")) + .utf8(); + // --debugger-agent=help + const char *options[] = { + "--soft-breakpoints", + da_args.get_data() + }; + mono_jit_parse_options(2, (char **)options); +} +#endif + +void GDMono::initialize() { + + ERR_FAIL_NULL(Engine::get_singleton()); + + OS::get_singleton()->print("Mono: Initializing module...\n"); + +#ifdef DEBUG_METHODS_ENABLED + _initialize_and_check_api_hashes(); +#endif + + GDMonoLog::get_singleton()->initialize(); + +#ifdef MONO_PRINT_HANDLER_ENABLED + mono_trace_set_print_handler(gdmono_MonoPrintCallback); + mono_trace_set_printerr_handler(gdmono_MonoPrintCallback); +#endif + +#ifdef WINDOWS_ENABLED + mono_reg_info = MonoRegUtils::find_mono(); + + CharString assembly_dir; + CharString config_dir; + + if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) { + assembly_dir = mono_reg_info.assembly_dir.utf8(); + } + + if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) { + config_dir = mono_reg_info.config_dir.utf8(); + } + + mono_set_dirs(assembly_dir.length() ? assembly_dir.get_data() : NULL, + config_dir.length() ? config_dir.get_data() : NULL); +#else + mono_set_dirs(NULL, NULL); +#endif + + GDMonoAssembly::initialize(); + +#ifdef DEBUG_ENABLED + gdmono_debug_init(); +#endif + + mono_config_parse(NULL); + + root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319"); + + ERR_EXPLAIN("Mono: Failed to initialize runtime"); + ERR_FAIL_NULL(root_domain); + + GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread()); + + runtime_initialized = true; + + OS::get_singleton()->print("Mono: Runtime initialized\n"); + + // mscorlib assembly MUST be present at initialization + ERR_EXPLAIN("Mono: Failed to load mscorlib assembly"); + ERR_FAIL_COND(!_load_corlib_assembly()); + +#ifdef TOOLS_ENABLED + // The tools domain must be loaded here, before the scripts domain. + // Otherwise domain unload on the scripts domain will hang indefinitely. + + ERR_EXPLAIN("Mono: Failed to load tools domain"); + ERR_FAIL_COND(_load_tools_domain() != OK); + + // TODO move to editor init callback, and do it lazily when required before editor init (e.g.: bindings generation) + ERR_EXPLAIN("Mono: Failed to load Editor Tools assembly"); + ERR_FAIL_COND(!_load_editor_tools_assembly()); +#endif + + ERR_EXPLAIN("Mono: Failed to load scripts domain"); + ERR_FAIL_COND(_load_scripts_domain() != OK); + +#ifdef DEBUG_ENABLED + bool debugger_attached = _wait_for_debugger_msecs(500); + if (!debugger_attached && OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->printerr("Mono: Debugger wait timeout\n"); +#endif + + _register_internal_calls(); + + // The following assemblies are not required at initialization + _load_all_script_assemblies(); + + mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL); + + OS::get_singleton()->print("Mono: ALL IS GOOD\n"); +} + +#ifndef MONO_GLUE_DISABLED +namespace GodotSharpBindings { + +uint64_t get_core_api_hash(); +uint64_t get_editor_api_hash(); + +void register_generated_icalls(); +} // namespace GodotSharpBindings +#endif + +void GDMono::_register_internal_calls() { +#ifndef MONO_GLUE_DISABLED + GodotSharpBindings::register_generated_icalls(); +#endif + +#ifdef TOOLS_ENABLED + GodotSharpBuilds::_register_internal_calls(); +#endif +} + +#ifdef DEBUG_METHODS_ENABLED +void GDMono::_initialize_and_check_api_hashes() { + + api_core_hash = ClassDB::get_api_hash(ClassDB::API_CORE); + +#ifndef MONO_GLUE_DISABLED + if (api_core_hash != GodotSharpBindings::get_core_api_hash()) { + ERR_PRINT("Mono: Core API hash mismatch!"); + } +#endif + +#ifdef TOOLS_ENABLED + api_editor_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR); + +#ifndef MONO_GLUE_DISABLED + if (api_editor_hash != GodotSharpBindings::get_editor_api_hash()) { + ERR_PRINT("Mono: Editor API hash mismatch!"); + } +#endif + +#endif // TOOLS_ENABLED +} +#endif // DEBUG_METHODS_ENABLED + +void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) { + + assemblies[p_domain_id][p_assembly->get_name()] = p_assembly; +} + +GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) { + + MonoDomain *domain = mono_domain_get(); + uint32_t domain_id = domain ? mono_domain_get_id(domain) : 0; + return assemblies[domain_id].getptr(p_name); +} + +bool GDMono::_load_assembly(const String &p_name, GDMonoAssembly **r_assembly) { + + CRASH_COND(!r_assembly); + + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + "...\n").utf8()); + + MonoImageOpenStatus status = MONO_IMAGE_OK; + MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8()); + MonoAssembly *assembly = mono_assembly_load_full(aname, NULL, &status, false); + mono_assembly_name_free(aname); + + if (!assembly) + return false; + + uint32_t domain_id = mono_domain_get_id(mono_domain_get()); + + GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name); + + ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false); + ERR_FAIL_COND_V(stored_assembly == NULL, false); + + ERR_FAIL_COND_V((*stored_assembly)->get_assembly() != assembly, false); + *r_assembly = *stored_assembly; + + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->print(String("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path() + "\n").utf8()); + + return true; +} + +bool GDMono::_load_corlib_assembly() { + + if (corlib_assembly) + return true; + + bool success = _load_assembly("mscorlib", &corlib_assembly); + + if (success) + GDMonoUtils::update_corlib_cache(); + + return success; +} + +bool GDMono::_load_core_api_assembly() { + + if (api_assembly) + return true; + + bool success = _load_assembly(API_ASSEMBLY_NAME, &api_assembly); + + if (success) + GDMonoUtils::update_godot_api_cache(); + + return success; +} + +#ifdef TOOLS_ENABLED +bool GDMono::_load_editor_api_assembly() { + + if (editor_api_assembly) + return true; + + return _load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly); +} +#endif + +#ifdef TOOLS_ENABLED +bool GDMono::_load_editor_tools_assembly() { + + if (editor_tools_assembly) + return true; + + _GDMONO_SCOPE_DOMAIN_(tools_domain) + + return _load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly); +} +#endif + +bool GDMono::_load_project_assembly() { + + if (project_assembly) + return true; + + String name = ProjectSettings::get_singleton()->get("application/config/name"); + if (name.empty()) { + name = "UnnamedProject"; + } + + bool success = _load_assembly(name, &project_assembly); + + if (success) + mono_assembly_set_main(project_assembly->get_assembly()); + + return success; +} + +bool GDMono::_load_all_script_assemblies() { + +#ifndef MONO_GLUE_DISABLED + if (!_load_core_api_assembly()) { + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->printerr("Mono: Failed to load Core API assembly\n"); + return false; + } else { +#ifdef TOOLS_ENABLED + if (!_load_editor_api_assembly()) { + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->printerr("Mono: Failed to load Editor API assembly\n"); + return false; + } +#endif + } + + if (!_load_project_assembly()) { + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->printerr("Mono: Failed to load project assembly\n"); + return false; + } + + return true; +#else + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->print("Mono: Glue disbled, ignoring script assemblies\n"); + + return true; +#endif +} + +Error GDMono::_load_scripts_domain() { + + ERR_FAIL_COND_V(scripts_domain != NULL, ERR_BUG); + + if (OS::get_singleton()->is_stdout_verbose()) { + OS::get_singleton()->print("Mono: Loading scripts domain...\n"); + } + + scripts_domain = GDMonoUtils::create_domain("GodotEngine.ScriptsDomain"); + + ERR_EXPLAIN("Mono: Could not create scripts app domain"); + ERR_FAIL_NULL_V(scripts_domain, ERR_CANT_CREATE); + + mono_domain_set(scripts_domain, true); + + return OK; +} + +Error GDMono::_unload_scripts_domain() { + + ERR_FAIL_NULL_V(scripts_domain, ERR_BUG); + + if (OS::get_singleton()->is_stdout_verbose()) { + OS::get_singleton()->print("Mono: Unloading scripts domain...\n"); + } + + _GodotSharp::get_singleton()->_dispose_callback(); + + if (mono_domain_get() != root_domain) + mono_domain_set(root_domain, true); + + mono_gc_collect(mono_gc_max_generation()); + + finalizing_scripts_domain = true; + mono_domain_finalize(scripts_domain, 2000); + finalizing_scripts_domain = false; + + mono_gc_collect(mono_gc_max_generation()); + + _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain)); + + api_assembly = NULL; + project_assembly = NULL; +#ifdef TOOLS_ENABLED + editor_api_assembly = NULL; +#endif + + MonoDomain *domain = scripts_domain; + scripts_domain = NULL; + + _GodotSharp::get_singleton()->_dispose_callback(); + + MonoObject *ex = NULL; + mono_domain_try_unload(domain, &ex); + + if (ex) { + ERR_PRINT("Exception thrown when unloading scripts domain:"); + mono_print_unhandled_exception(ex); + return FAILED; + } + + return OK; +} + +#ifdef TOOLS_ENABLED +Error GDMono::_load_tools_domain() { + + ERR_FAIL_COND_V(tools_domain != NULL, ERR_BUG); + + if (OS::get_singleton()->is_stdout_verbose()) { + OS::get_singleton()->print("Mono: Loading tools domain...\n"); + } + + tools_domain = GDMonoUtils::create_domain("GodotEngine.ToolsDomain"); + + ERR_EXPLAIN("Mono: Could not create tools app domain"); + ERR_FAIL_NULL_V(tools_domain, ERR_CANT_CREATE); + + return OK; +} +#endif + +#ifdef TOOLS_ENABLED +Error GDMono::reload_scripts_domain() { + + ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG); + + if (scripts_domain) { + Error err = _unload_scripts_domain(); + if (err != OK) { + ERR_PRINT("Mono: Failed to unload scripts domain"); + return err; + } + } + + Error err = _load_scripts_domain(); + if (err != OK) { + ERR_PRINT("Mono: Failed to load scripts domain"); + return err; + } + + if (!_load_all_script_assemblies()) { + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->printerr("Mono: Failed to load script assemblies\n"); + return ERR_CANT_OPEN; + } + + return OK; +} +#endif + +GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) { + + MonoImage *image = mono_class_get_image(p_raw_class); + + if (image == corlib_assembly->get_image()) + return corlib_assembly->get_class(p_raw_class); + + uint32_t domain_id = mono_domain_get_id(mono_domain_get()); + HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id]; + + const String *k = NULL; + while ((k = domain_assemblies.next(k))) { + GDMonoAssembly *assembly = domain_assemblies.get(*k); + if (assembly->get_image() == image) { + GDMonoClass *klass = assembly->get_class(p_raw_class); + + if (klass) + return klass; + } + } + + return NULL; +} + +void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) { + + HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id]; + + const String *k = NULL; + while ((k = domain_assemblies.next(k))) { + memdelete(domain_assemblies.get(*k)); + } + + assemblies.erase(p_domain_id); +} + +GDMono::GDMono() { + + singleton = this; + + gdmono_log = memnew(GDMonoLog); + + runtime_initialized = false; + finalizing_scripts_domain = false; + + root_domain = NULL; + scripts_domain = NULL; +#ifdef TOOLS_ENABLED + tools_domain = NULL; +#endif + + corlib_assembly = NULL; + api_assembly = NULL; + project_assembly = NULL; +#ifdef TOOLS_ENABLED + editor_api_assembly = NULL; + editor_tools_assembly = NULL; +#endif + +#ifdef DEBUG_METHODS_ENABLED + api_core_hash = 0; +#ifdef TOOLS_ENABLED + api_editor_hash = 0; +#endif +#endif +} + +GDMono::~GDMono() { + + if (runtime_initialized) { + + if (scripts_domain) { + + Error err = _unload_scripts_domain(); + if (err != OK) { + WARN_PRINT("Mono: Failed to unload scripts domain"); + } + } + + const uint32_t *k = NULL; + while ((k = assemblies.next(k))) { + HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies.get(*k); + + const String *kk = NULL; + while ((kk = domain_assemblies.next(kk))) { + memdelete(domain_assemblies.get(*kk)); + } + } + assemblies.clear(); + + GDMonoUtils::clear_cache(); + + OS::get_singleton()->print("Mono: Runtime cleanup...\n"); + + runtime_initialized = false; + mono_jit_cleanup(root_domain); + } + + if (gdmono_log) + memdelete(gdmono_log); + + singleton = NULL; +} + +_GodotSharp *_GodotSharp::singleton = NULL; + +void _GodotSharp::_dispose_object(Object *p_object) { + + if (p_object->get_script_instance()) { + CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_object->get_script_instance()); + if (cs_instance) { + cs_instance->mono_object_disposed(); + return; + } + } + + // Unsafe refcount decrement. The managed instance also counts as a reference. + // See: CSharpLanguage::alloc_instance_binding_data(Object *p_object) + if (Object::cast_to<Reference>(p_object)->unreference()) { + memdelete(p_object); + } +} + +void _GodotSharp::_dispose_callback() { + +#ifndef NO_THREADS + queue_mutex->lock(); +#endif + + for (List<Object *>::Element *E = obj_delete_queue.front(); E; E = E->next()) { + _dispose_object(E->get()); + } + + for (List<NodePath *>::Element *E = np_delete_queue.front(); E; E = E->next()) { + memdelete(E->get()); + } + + for (List<RID *>::Element *E = rid_delete_queue.front(); E; E = E->next()) { + memdelete(E->get()); + } + + obj_delete_queue.clear(); + np_delete_queue.clear(); + rid_delete_queue.clear(); + queue_empty = true; + +#ifndef NO_THREADS + queue_mutex->unlock(); +#endif +} + +void _GodotSharp::attach_thread() { + + GDMonoUtils::attach_current_thread(); +} + +void _GodotSharp::detach_thread() { + + GDMonoUtils::detach_current_thread(); +} + +bool _GodotSharp::is_finalizing_domain() { + + return GDMono::get_singleton()->is_finalizing_scripts_domain(); +} + +bool _GodotSharp::is_domain_loaded() { + + return GDMono::get_singleton()->get_scripts_domain() != NULL; +} + +#define ENQUEUE_FOR_DISPOSAL(m_queue, m_inst) \ + m_queue.push_back(m_inst); \ + if (queue_empty) { \ + queue_empty = false; \ + call_deferred("_dispose_callback"); \ + } + +void _GodotSharp::queue_dispose(Object *p_object) { + + if (Thread::get_main_id() == Thread::get_caller_id() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) { + _dispose_object(p_object); + } else { +#ifndef NO_THREADS + queue_mutex->lock(); +#endif + + ENQUEUE_FOR_DISPOSAL(obj_delete_queue, p_object); + +#ifndef NO_THREADS + queue_mutex->unlock(); +#endif + } +} + +void _GodotSharp::queue_dispose(NodePath *p_node_path) { + + if (Thread::get_main_id() == Thread::get_caller_id() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) { + memdelete(p_node_path); + } else { +#ifndef NO_THREADS + queue_mutex->lock(); +#endif + + ENQUEUE_FOR_DISPOSAL(np_delete_queue, p_node_path); + +#ifndef NO_THREADS + queue_mutex->unlock(); +#endif + } +} + +void _GodotSharp::queue_dispose(RID *p_rid) { + + if (Thread::get_main_id() == Thread::get_caller_id() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) { + memdelete(p_rid); + } else { +#ifndef NO_THREADS + queue_mutex->lock(); +#endif + + ENQUEUE_FOR_DISPOSAL(rid_delete_queue, p_rid); + +#ifndef NO_THREADS + queue_mutex->unlock(); +#endif + } +} + +void _GodotSharp::_bind_methods() { + + ClassDB::bind_method(D_METHOD("attach_thread"), &_GodotSharp::attach_thread); + ClassDB::bind_method(D_METHOD("detach_thread"), &_GodotSharp::detach_thread); + + ClassDB::bind_method(D_METHOD("is_finalizing_domain"), &_GodotSharp::is_finalizing_domain); + ClassDB::bind_method(D_METHOD("is_domain_loaded"), &_GodotSharp::is_domain_loaded); + + ClassDB::bind_method(D_METHOD("_dispose_callback"), &_GodotSharp::_dispose_callback); +} + +_GodotSharp::_GodotSharp() { + + singleton = this; + queue_empty = true; +#ifndef NO_THREADS + queue_mutex = Mutex::create(); +#endif +} + +_GodotSharp::~_GodotSharp() { + + singleton = NULL; + + if (queue_mutex) { + memdelete(queue_mutex); + } +} diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h new file mode 100644 index 0000000000..b188c0730a --- /dev/null +++ b/modules/mono/mono_gd/gd_mono.h @@ -0,0 +1,226 @@ +/*************************************************************************/ +/* gd_mono.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GD_MONO_H +#define GD_MONO_H + +#include "../godotsharp_defs.h" +#include "gd_mono_assembly.h" +#include "gd_mono_log.h" + +#ifdef WINDOWS_ENABLED +#include "../utils/mono_reg_utils.h" +#endif + +#define SCRIPTS_DOMAIN GDMono::get_singleton()->get_scripts_domain() +#ifdef TOOLS_ENABLED +#define TOOLS_DOMAIN GDMono::get_singleton()->get_tools_domain() +#endif + +class GDMono { + + bool runtime_initialized; + bool finalizing_scripts_domain; + + MonoDomain *root_domain; + MonoDomain *scripts_domain; +#ifdef TOOLS_ENABLED + MonoDomain *tools_domain; +#endif + + GDMonoAssembly *corlib_assembly; + GDMonoAssembly *api_assembly; + GDMonoAssembly *project_assembly; +#ifdef TOOLS_ENABLED + GDMonoAssembly *editor_api_assembly; + GDMonoAssembly *editor_tools_assembly; +#endif + + HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies; + + void _domain_assemblies_cleanup(uint32_t p_domain_id); + + bool _load_corlib_assembly(); + bool _load_core_api_assembly(); +#ifdef TOOLS_ENABLED + bool _load_editor_api_assembly(); + bool _load_editor_tools_assembly(); +#endif + bool _load_project_assembly(); + + bool _load_all_script_assemblies(); + + void _register_internal_calls(); + + Error _load_scripts_domain(); + Error _unload_scripts_domain(); + +#ifdef TOOLS_ENABLED + Error _load_tools_domain(); +#endif + +#ifdef DEBUG_METHODS_ENABLED + uint64_t api_core_hash; +#ifdef TOOLS_ENABLED + uint64_t api_editor_hash; +#endif + void _initialize_and_check_api_hashes(); +#endif + + bool _load_assembly(const String &p_name, GDMonoAssembly **r_assembly); + + GDMonoLog *gdmono_log; + +#ifdef WINDOWS_ENABLED + MonoRegInfo mono_reg_info; +#endif + +protected: + static GDMono *singleton; + +public: +#ifdef DEBUG_METHODS_ENABLED + uint64_t get_api_core_hash() { return api_core_hash; } +#ifdef TOOLS_ENABLED + uint64_t get_api_editor_hash() { return api_editor_hash; } +#endif +#endif + + enum MemberVisibility { + PRIVATE, + PROTECTED_AND_INTERNAL, // FAM_AND_ASSEM + INTERNAL, // ASSEMBLY + PROTECTED, // FAMILY + PUBLIC + }; + + static GDMono *get_singleton() { return singleton; } + + // Do not use these, unless you know what you're doing + void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly); + GDMonoAssembly **get_loaded_assembly(const String &p_name); + + _FORCE_INLINE_ bool is_runtime_initialized() const { return runtime_initialized; } + _FORCE_INLINE_ bool is_finalizing_scripts_domain() const { return finalizing_scripts_domain; } + + _FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; } +#ifdef TOOLS_ENABLED + _FORCE_INLINE_ MonoDomain *get_tools_domain() { return tools_domain; } +#endif + + _FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; } + _FORCE_INLINE_ GDMonoAssembly *get_api_assembly() const { return api_assembly; } + _FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; } +#ifdef TOOLS_ENABLED + _FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly; } + _FORCE_INLINE_ GDMonoAssembly *get_editor_tools_assembly() const { return editor_tools_assembly; } +#endif + +#ifdef WINDOWS_ENABLED + const MonoRegInfo &get_mono_reg_info() { return mono_reg_info; } +#endif + + GDMonoClass *get_class(MonoClass *p_raw_class); + +#ifdef TOOLS_ENABLED + Error reload_scripts_domain(); +#endif + + void initialize(); + + GDMono(); + ~GDMono(); +}; + +class GDMonoScopeDomain { + + MonoDomain *prev_domain; + +public: + GDMonoScopeDomain(MonoDomain *p_domain) { + MonoDomain *prev_domain = mono_domain_get(); + if (prev_domain != p_domain) { + this->prev_domain = prev_domain; + mono_domain_set(p_domain, false); + } else { + this->prev_domain = NULL; + } + } + + ~GDMonoScopeDomain() { + if (prev_domain) + mono_domain_set(prev_domain, false); + } +}; + +#define _GDMONO_SCOPE_DOMAIN_(m_mono_domain) \ + GDMonoScopeDomain __gdmono__scope__domain__(m_mono_domain); \ + (void)__gdmono__scope__domain__; + +class _GodotSharp : public Object { + GDCLASS(_GodotSharp, Object) + + friend class GDMono; + + void _dispose_object(Object *p_object); + + void _dispose_callback(); + + List<Object *> obj_delete_queue; + List<NodePath *> np_delete_queue; + List<RID *> rid_delete_queue; + + bool queue_empty; + +#ifndef NO_THREADS + Mutex *queue_mutex; +#endif + +protected: + static _GodotSharp *singleton; + static void _bind_methods(); + +public: + static _GodotSharp *get_singleton() { return singleton; } + + void attach_thread(); + void detach_thread(); + + bool is_finalizing_domain(); + bool is_domain_loaded(); + + void queue_dispose(Object *p_object); + void queue_dispose(NodePath *p_node_path); + void queue_dispose(RID *p_rid); + + _GodotSharp(); + ~_GodotSharp(); +}; + +#endif // GD_MONO_H diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp new file mode 100644 index 0000000000..7dc7043eec --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -0,0 +1,359 @@ +/*************************************************************************/ +/* gd_mono_assembly.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "gd_mono_assembly.h" + +#include <mono/metadata/mono-debug.h> +#include <mono/metadata/tokentype.h> + +#include "list.h" +#include "os/file_access.h" +#include "os/os.h" + +#include "../godotsharp_dirs.h" +#include "gd_mono_class.h" + +bool GDMonoAssembly::no_search = false; +Vector<String> GDMonoAssembly::search_dirs; + +MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data) { + + (void)user_data; // UNUSED + + String name = mono_assembly_name_get_name(aname); + bool has_extension = name.ends_with(".dll") || name.ends_with(".exe"); + + if (no_search) + return NULL; + + GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name); + if (loaded_asm) + return (*loaded_asm)->get_assembly(); + + no_search = true; // Avoid the recursion madness + + String path; + MonoAssembly *res = NULL; + + for (int i = 0; i < search_dirs.size(); i++) { + const String &search_dir = search_dirs[i]; + + if (has_extension) { + path = search_dir.plus_file(name); + if (FileAccess::exists(path)) { + res = _load_assembly_from(name.get_basename(), path); + break; + } + } else { + path = search_dir.plus_file(name + ".dll"); + if (FileAccess::exists(path)) { + res = _load_assembly_from(name, path); + break; + } + + path = search_dir.plus_file(name + ".exe"); + if (FileAccess::exists(path)) { + res = _load_assembly_from(name, path); + break; + } + } + } + + no_search = false; + + return res; +} + +MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) { + + (void)user_data; // UNUSED + + if (search_dirs.empty()) { +#ifdef TOOLS_DOMAIN + search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir()); +#endif + search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir()); + search_dirs.push_back(OS::get_singleton()->get_resource_dir()); + search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir()); + + const char *rootdir = mono_assembly_getrootdir(); + if (rootdir) { + search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5")); + } + + if (assemblies_path) { + while (*assemblies_path) { + search_dirs.push_back(*assemblies_path); + ++assemblies_path; + } + } + } + + return NULL; +} + +MonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path) { + + GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path)); + + MonoDomain *domain = mono_domain_get(); + + Error err = assembly->load(domain); + + if (err != OK) { + memdelete(assembly); + ERR_FAIL_V(NULL); + } + + GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, assembly); + + return assembly->get_assembly(); +} + +void GDMonoAssembly::initialize() { + + // TODO refonly as well? + mono_install_assembly_preload_hook(&GDMonoAssembly::_preload_hook, NULL); + mono_install_assembly_search_hook(&GDMonoAssembly::_search_hook, NULL); +} + +Error GDMonoAssembly::load(MonoDomain *p_domain) { + + ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE); + + uint64_t last_modified_time = FileAccess::get_modified_time(path); + + Vector<uint8_t> data = FileAccess::get_file_as_array(path); + ERR_FAIL_COND_V(data.empty(), ERR_FILE_CANT_READ); + + String image_filename(path); + + MonoImageOpenStatus status; + + image = mono_image_open_from_data_with_name( + (char *)&data[0], data.size(), + true, &status, false, + image_filename.utf8().get_data()); + + ERR_FAIL_COND_V(status != MONO_IMAGE_OK || image == NULL, ERR_FILE_CANT_OPEN); + +#ifdef DEBUG_ENABLED + String pdb_path(path + ".pdb"); + + if (!FileAccess::exists(pdb_path)) { + pdb_path = path.get_basename() + ".pdb"; // without .dll + + if (!FileAccess::exists(pdb_path)) + goto no_pdb; + } + + pdb_data.clear(); + pdb_data = FileAccess::get_file_as_array(pdb_path); + mono_debug_open_image_from_memory(image, &pdb_data[0], pdb_data.size()); + +no_pdb: + +#endif + + assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, false); + + ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN); + + if (p_domain && mono_image_get_entry_point(image)) { + // TODO should this be removed? do we want to call main? what other effects does this have? + mono_jit_exec(p_domain, assembly, 0, NULL); + } + + loaded = true; + modified_time = last_modified_time; + + return OK; +} + +Error GDMonoAssembly::wrapper_for_image(MonoImage *p_image) { + + ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE); + + assembly = mono_image_get_assembly(p_image); + ERR_FAIL_NULL_V(assembly, FAILED); + + image = p_image; + + mono_image_addref(image); + + loaded = true; + + return OK; +} + +void GDMonoAssembly::unload() { + + ERR_FAIL_COND(!loaded); + +#ifdef DEBUG_ENABLED + if (pdb_data.size()) { + mono_debug_close_image(image); + pdb_data.clear(); + } +#endif + + for (Map<MonoClass *, GDMonoClass *>::Element *E = cached_raw.front(); E; E = E->next()) { + memdelete(E->value()); + } + + cached_classes.clear(); + cached_raw.clear(); + + mono_image_close(image); + + assembly = NULL; + image = NULL; + loaded = false; +} + +GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const StringName &p_name) { + + ERR_FAIL_COND_V(!loaded, NULL); + + ClassKey key(p_namespace, p_name); + + GDMonoClass **match = cached_classes.getptr(key); + + if (match) + return *match; + + MonoClass *mono_class = mono_class_from_name(image, String(p_namespace).utf8(), String(p_name).utf8()); + + if (!mono_class) + return NULL; + + GDMonoClass *wrapped_class = memnew(GDMonoClass(p_namespace, p_name, mono_class, this)); + + cached_classes[key] = wrapped_class; + cached_raw[mono_class] = wrapped_class; + + return wrapped_class; +} + +GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) { + + ERR_FAIL_COND_V(!loaded, NULL); + + Map<MonoClass *, GDMonoClass *>::Element *match = cached_raw.find(p_mono_class); + + if (match) + return match->value(); + + StringName namespace_name = mono_class_get_namespace(p_mono_class); + StringName class_name = mono_class_get_name(p_mono_class); + + GDMonoClass *wrapped_class = memnew(GDMonoClass(namespace_name, class_name, p_mono_class, this)); + + cached_classes[ClassKey(namespace_name, class_name)] = wrapped_class; + cached_raw[p_mono_class] = wrapped_class; + + return wrapped_class; +} + +GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) { + + GDMonoClass *match = NULL; + + if (gdobject_class_cache_updated) { + Map<StringName, GDMonoClass *>::Element *result = gdobject_class_cache.find(p_class); + + if (result) + match = result->get(); + } else { + List<GDMonoClass *> nested_classes; + + int rows = mono_image_get_table_rows(image, MONO_TABLE_TYPEDEF); + + for (int i = 1; i < rows; i++) { + MonoClass *mono_class = mono_class_get(image, (i + 1) | MONO_TOKEN_TYPE_DEF); + + if (!mono_class_is_assignable_from(CACHED_CLASS_RAW(GodotObject), mono_class)) + continue; + + GDMonoClass *current = get_class(mono_class); + + if (!current) + continue; + + nested_classes.push_back(current); + + if (!match && current->get_name() == p_class) + match = current; + + while (!nested_classes.empty()) { + GDMonoClass *current_nested = nested_classes.front()->get(); + nested_classes.pop_back(); + + void *iter = NULL; + + while (true) { + MonoClass *raw_nested = mono_class_get_nested_types(current_nested->get_raw(), &iter); + + if (!raw_nested) + break; + + GDMonoClass *nested_class = get_class(raw_nested); + + if (nested_class) { + gdobject_class_cache.insert(nested_class->get_name(), nested_class); + nested_classes.push_back(nested_class); + } + } + } + + gdobject_class_cache.insert(current->get_name(), current); + } + + gdobject_class_cache_updated = true; + } + + return match; +} + +GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) { + + loaded = false; + gdobject_class_cache_updated = false; + name = p_name; + path = p_path; + modified_time = 0; + assembly = NULL; + image = NULL; +} + +GDMonoAssembly::~GDMonoAssembly() { + + if (loaded) + unload(); +} diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h new file mode 100644 index 0000000000..710b674622 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -0,0 +1,121 @@ +/*************************************************************************/ +/* gd_mono_assembly.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GD_MONO_ASSEMBLY_H +#define GD_MONO_ASSEMBLY_H + +#include <mono/jit/jit.h> +#include <mono/metadata/assembly.h> + +#include "gd_mono_utils.h" +#include "hash_map.h" +#include "map.h" +#include "ustring.h" + +class GDMonoAssembly { + + struct ClassKey { + struct Hasher { + static _FORCE_INLINE_ uint32_t hash(const ClassKey &p_key) { + uint32_t hash = 0; + + GDMonoUtils::hash_combine(hash, p_key.namespace_name.hash()); + GDMonoUtils::hash_combine(hash, p_key.class_name.hash()); + + return hash; + } + }; + + _FORCE_INLINE_ bool operator==(const ClassKey &p_a) const { + return p_a.class_name == class_name && p_a.namespace_name == namespace_name; + } + + ClassKey() {} + + ClassKey(const StringName &p_namespace_name, const StringName &p_class_name) { + namespace_name = p_namespace_name; + class_name = p_class_name; + } + + StringName namespace_name; + StringName class_name; + }; + + MonoAssembly *assembly; + MonoImage *image; + + bool loaded; + + String name; + String path; + uint64_t modified_time; + + HashMap<ClassKey, GDMonoClass *, ClassKey::Hasher> cached_classes; + Map<MonoClass *, GDMonoClass *> cached_raw; + + bool gdobject_class_cache_updated; + Map<StringName, GDMonoClass *> gdobject_class_cache; + +#ifdef DEBUG_ENABLED + Vector<uint8_t> pdb_data; +#endif + + static bool no_search; + static Vector<String> search_dirs; + + static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data); + static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data); + + static MonoAssembly *_load_assembly_from(const String &p_name, const String &p_path); + + friend class GDMono; + static void initialize(); + +public: + Error load(MonoDomain *p_domain); + Error wrapper_for_image(MonoImage *p_image); + void unload(); + + _FORCE_INLINE_ bool is_loaded() const { return loaded; } + _FORCE_INLINE_ MonoImage *get_image() const { return image; } + _FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; } + _FORCE_INLINE_ String get_name() const { return name; } + _FORCE_INLINE_ String get_path() const { return path; } + _FORCE_INLINE_ uint64_t get_modified_time() const { return modified_time; } + + GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_class); + GDMonoClass *get_class(MonoClass *p_mono_class); + + GDMonoClass *get_object_derived_class(const StringName &p_class); + + GDMonoAssembly(const String &p_name, const String &p_path = String()); + ~GDMonoAssembly(); +}; + +#endif // GD_MONO_ASSEMBLY_H diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp new file mode 100644 index 0000000000..77ba0ee90e --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -0,0 +1,413 @@ +/*************************************************************************/ +/* gd_mono_class.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "gd_mono_class.h" + +#include <mono/metadata/attrdefs.h> + +#include "gd_mono_assembly.h" + +MonoType *GDMonoClass::get_raw_type(GDMonoClass *p_class) { + + return mono_class_get_type(p_class->get_raw()); +} + +bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const { + + return mono_class_is_assignable_from(mono_class, p_from->mono_class); +} + +String GDMonoClass::get_full_name() const { + + String res = namespace_name; + if (res.length()) + res += "."; + return res + class_name; +} + +GDMonoClass *GDMonoClass::get_parent_class() { + + if (assembly) { + MonoClass *parent_mono_class = mono_class_get_parent(mono_class); + + if (parent_mono_class) { + return GDMono::get_singleton()->get_class(parent_mono_class); + } + } + + return NULL; +} + +#ifdef TOOLS_ENABLED +Vector<MonoClassField *> GDMonoClass::get_enum_fields() { + + bool class_is_enum = mono_class_is_enum(mono_class); + ERR_FAIL_COND_V(!class_is_enum, Vector<MonoClassField *>()); + + Vector<MonoClassField *> enum_fields; + + void *iter = NULL; + MonoClassField *raw_field = NULL; + while ((raw_field = mono_class_get_fields(get_raw(), &iter)) != NULL) { + uint32_t field_flags = mono_field_get_flags(raw_field); + + // Enums have an instance field named value__ which holds the value of the enum. + // Enum constants are static, so we will use this to ignore the value__ field. + if (field_flags & MONO_FIELD_ATTR_PUBLIC && field_flags & MONO_FIELD_ATTR_STATIC) { + enum_fields.push_back(raw_field); + } + } + + return enum_fields; +} +#endif + +bool GDMonoClass::has_method(const StringName &p_name) { + + return get_method(p_name) != NULL; +} + +bool GDMonoClass::has_attribute(GDMonoClass *p_attr_class) { + +#ifdef DEBUG_ENABLED + ERR_FAIL_NULL_V(p_attr_class, false); +#endif + + if (!attrs_fetched) + fetch_attributes(); + + if (!attributes) + return false; + + return mono_custom_attrs_has_attr(attributes, p_attr_class->get_raw()); +} + +MonoObject *GDMonoClass::get_attribute(GDMonoClass *p_attr_class) { + +#ifdef DEBUG_ENABLED + ERR_FAIL_NULL_V(p_attr_class, NULL); +#endif + + if (!attrs_fetched) + fetch_attributes(); + + if (!attributes) + return NULL; + + return mono_custom_attrs_get_attr(attributes, p_attr_class->get_raw()); +} + +void GDMonoClass::fetch_attributes() { + + ERR_FAIL_COND(attributes != NULL); + + attributes = mono_custom_attrs_from_class(get_raw()); + attrs_fetched = true; +} + +void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base) { + + CRASH_COND(!CACHED_CLASS(GodotObject)->is_assignable_from(this)); + + if (methods_fetched) + return; + + void *iter = NULL; + MonoMethod *raw_method = NULL; + while ((raw_method = mono_class_get_methods(get_raw(), &iter)) != NULL) { + StringName name = mono_method_get_name(raw_method); + + GDMonoMethod *method = get_method(raw_method, name); + ERR_CONTINUE(!method); + + if (method->get_name() != name) { + +#ifdef DEBUG_ENABLED + String fullname = method->get_ret_type_full_name() + " " + name + "(" + method->get_signature_desc(true) + ")"; + WARN_PRINTS("Method `" + fullname + "` is hidden by Godot API method. Should be `" + + method->get_full_name_no_class() + "`. In class `" + namespace_name + "." + class_name + "`."); +#endif + continue; + } + +#ifdef DEBUG_ENABLED + // For debug builds, we also fetched from native base classes as well before if this is not a native base class. + // This allows us to warn the user here if he is using snake_case by mistake. + + if (p_native_base != this) { + + GDMonoClass *native_top = p_native_base; + while (native_top) { + GDMonoMethod *m = native_top->get_method(name, method->get_parameters_count()); + + if (m && m->get_name() != name) { + // found + String fullname = m->get_ret_type_full_name() + " " + name + "(" + m->get_signature_desc(true) + ")"; + WARN_PRINTS("Method `" + fullname + "` should be `" + m->get_full_name_no_class() + + "`. In class `" + namespace_name + "." + class_name + "`."); + break; + } + + if (native_top == CACHED_CLASS(GodotObject)) + break; + + native_top = native_top->get_parent_class(); + } + } +#endif + + uint32_t flags = mono_method_get_flags(method->mono_method, NULL); + + if (!(flags & MONO_METHOD_ATTR_VIRTUAL)) + continue; + + // Virtual method of Godot Object derived type, let's try to find GodotMethod attribute + + GDMonoClass *top = p_native_base; + + while (top) { + GDMonoMethod *base_method = top->get_method(name, method->get_parameters_count()); + + if (base_method && base_method->has_attribute(CACHED_CLASS(GodotMethodAttribute))) { + // Found base method with GodotMethod attribute. + // We get the original API method name from this attribute. + // This name must point to the virtual method. + + MonoObject *attr = base_method->get_attribute(CACHED_CLASS(GodotMethodAttribute)); + + StringName godot_method_name = CACHED_FIELD(GodotMethodAttribute, methodName)->get_string_value(attr); +#ifdef DEBUG_ENABLED + CRASH_COND(godot_method_name == StringName()); +#endif + MethodKey key = MethodKey(godot_method_name, method->get_parameters_count()); + GDMonoMethod **existing_method = methods.getptr(key); + if (existing_method) + memdelete(*existing_method); // Must delete old one + methods.set(key, method); + + break; + } + + if (top == CACHED_CLASS(GodotObject)) + break; + + top = top->get_parent_class(); + } + } + + methods_fetched = true; +} + +GDMonoMethod *GDMonoClass::get_method(const StringName &p_name) { + + ERR_FAIL_COND_V(!methods_fetched, NULL); + + const MethodKey *k = NULL; + + while ((k = methods.next(k))) { + if (k->name == p_name) + return methods.get(*k); + } + + return NULL; +} + +GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) { + + MethodKey key = MethodKey(p_name, p_params_count); + + GDMonoMethod **match = methods.getptr(key); + + if (match) + return *match; + + if (methods_fetched) + return NULL; + + MonoMethod *raw_method = mono_class_get_method_from_name(mono_class, String(p_name).utf8().get_data(), p_params_count); + + if (raw_method) { + GDMonoMethod *method = memnew(GDMonoMethod(p_name, raw_method)); + methods.set(key, method); + + return method; + } + + return NULL; +} + +GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method) { + + MonoMethodSignature *sig = mono_method_signature(p_raw_method); + + int params_count = mono_signature_get_param_count(sig); + StringName method_name = mono_method_get_name(p_raw_method); + + return get_method(p_raw_method, method_name, params_count); +} + +GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name) { + + MonoMethodSignature *sig = mono_method_signature(p_raw_method); + int params_count = mono_signature_get_param_count(sig); + return get_method(p_raw_method, p_name, params_count); +} + +GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count) { + + ERR_FAIL_NULL_V(p_raw_method, NULL); + + MethodKey key = MethodKey(p_name, p_params_count); + + GDMonoMethod **match = methods.getptr(key); + + if (match) + return *match; + + GDMonoMethod *method = memnew(GDMonoMethod(p_name, p_raw_method)); + methods.set(key, method); + + return method; +} + +GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, bool p_include_namespace) { + + MonoMethodDesc *desc = mono_method_desc_new(p_description.utf8().get_data(), p_include_namespace); + MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class); + mono_method_desc_free(desc); + + return get_method(method); +} + +GDMonoField *GDMonoClass::get_field(const StringName &p_name) { + + Map<StringName, GDMonoField *>::Element *result = fields.find(p_name); + + if (result) + return result->value(); + + if (fields_fetched) + return NULL; + + MonoClassField *raw_field = mono_class_get_field_from_name(mono_class, String(p_name).utf8().get_data()); + + if (raw_field) { + GDMonoField *field = memnew(GDMonoField(raw_field, this)); + fields.insert(p_name, field); + + return field; + } + + return NULL; +} + +const Vector<GDMonoField *> &GDMonoClass::get_all_fields() { + + if (fields_fetched) + return fields_list; + + void *iter = NULL; + MonoClassField *raw_field = NULL; + while ((raw_field = mono_class_get_fields(get_raw(), &iter)) != NULL) { + StringName name = mono_field_get_name(raw_field); + + Map<StringName, GDMonoField *>::Element *match = fields.find(name); + + if (match) { + fields_list.push_back(match->get()); + } else { + GDMonoField *field = memnew(GDMonoField(raw_field, this)); + fields.insert(name, field); + fields_list.push_back(field); + } + } + + fields_fetched = true; + + return fields_list; +} + +GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly) { + + namespace_name = p_namespace; + class_name = p_name; + mono_class = p_class; + assembly = p_assembly; + + attrs_fetched = false; + attributes = NULL; + + methods_fetched = false; + fields_fetched = false; +} + +GDMonoClass::~GDMonoClass() { + + if (attributes) { + mono_custom_attrs_free(attributes); + } + + for (Map<StringName, GDMonoField *>::Element *E = fields.front(); E; E = E->next()) { + memdelete(E->value()); + } + + { + // Ugly workaround... + // We may have duplicated values, because we redirect snake_case methods to PascalCasel (only Godot API methods). + // This way, we end with both the snake_case name and the PascalCasel name paired with the same method. + // Therefore, we must avoid deleting the same pointer twice. + + int offset = 0; + Vector<GDMonoMethod *> deleted_methods; + deleted_methods.resize(methods.size()); + + const MethodKey *k = NULL; + while ((k = methods.next(k))) { + GDMonoMethod *method = methods.get(*k); + + if (method) { + for (int i = 0; i < offset; i++) { + if (deleted_methods[i] == method) { + // Already deleted + goto already_deleted; + } + } + + deleted_methods[offset] = method; + ++offset; + + memdelete(method); + } + + already_deleted:; + } + + methods.clear(); + } +} diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h new file mode 100644 index 0000000000..ef1ca425a7 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -0,0 +1,130 @@ +/*************************************************************************/ +/* gd_mono_class.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GD_MONO_CLASS_H +#define GD_MONO_CLASS_H + +#include <mono/metadata/debug-helpers.h> + +#include "map.h" +#include "ustring.h" + +#include "gd_mono_field.h" +#include "gd_mono_header.h" +#include "gd_mono_method.h" +#include "gd_mono_utils.h" + +class GDMonoClass { + struct MethodKey { + struct Hasher { + static _FORCE_INLINE_ uint32_t hash(const MethodKey &p_key) { + uint32_t hash = 0; + + GDMonoUtils::hash_combine(hash, p_key.name.hash()); + GDMonoUtils::hash_combine(hash, HashMapHasherDefault::hash(p_key.params_count)); + + return hash; + } + }; + + _FORCE_INLINE_ bool operator==(const MethodKey &p_a) const { + return p_a.params_count == params_count && p_a.name == name; + } + + MethodKey() {} + + MethodKey(const StringName &p_name, int p_params_count) { + name = p_name; + params_count = p_params_count; + } + + StringName name; + int params_count; + }; + + StringName namespace_name; + StringName class_name; + + MonoClass *mono_class; + GDMonoAssembly *assembly; + + bool attrs_fetched; + MonoCustomAttrInfo *attributes; + + bool methods_fetched; + HashMap<MethodKey, GDMonoMethod *, MethodKey::Hasher> methods; + + bool fields_fetched; + Map<StringName, GDMonoField *> fields; + Vector<GDMonoField *> fields_list; + + friend class GDMonoAssembly; + GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly); + +public: + static MonoType *get_raw_type(GDMonoClass *p_class); + + bool is_assignable_from(GDMonoClass *p_from) const; + + _FORCE_INLINE_ StringName get_namespace() const { return namespace_name; } + _FORCE_INLINE_ StringName get_name() const { return class_name; } + + _FORCE_INLINE_ MonoClass *get_raw() const { return mono_class; } + _FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; } + + String get_full_name() const; + + GDMonoClass *get_parent_class(); + +#ifdef TOOLS_ENABLED + Vector<MonoClassField *> get_enum_fields(); +#endif + + bool has_method(const StringName &p_name); + + bool has_attribute(GDMonoClass *p_attr_class); + MonoObject *get_attribute(GDMonoClass *p_attr_class); + + void fetch_attributes(); + void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base); + + GDMonoMethod *get_method(const StringName &p_name); + GDMonoMethod *get_method(const StringName &p_name, int p_params_count); + GDMonoMethod *get_method(MonoMethod *p_raw_method); + GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name); + GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count); + GDMonoMethod *get_method_with_desc(const String &p_description, bool p_includes_namespace); + + GDMonoField *get_field(const StringName &p_name); + const Vector<GDMonoField *> &get_all_fields(); + + ~GDMonoClass(); +}; + +#endif // GD_MONO_CLASS_H diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp new file mode 100644 index 0000000000..1643f8cfc5 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -0,0 +1,364 @@ +/*************************************************************************/ +/* gd_mono_field.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "gd_mono_field.h" + +#include <mono/metadata/attrdefs.h> + +#include "gd_mono_class.h" +#include "gd_mono_marshal.h" + +void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) { + mono_field_set_value(p_object, mono_field, &p_ptr); +} + +void GDMonoField::set_value(MonoObject *p_object, const Variant &p_value) { +#define SET_FROM_STRUCT_AND_BREAK(m_type) \ + { \ + const m_type &val = p_value.operator m_type(); \ + MARSHALLED_OUT(m_type, val, raw); \ + mono_field_set_value(p_object, mono_field, raw); \ + break; \ + } + +#define SET_FROM_PRIMITIVE(m_type) \ + { \ + m_type val = p_value.operator m_type(); \ + mono_field_set_value(p_object, mono_field, &val); \ + break; \ + } + +#define SET_FROM_ARRAY_AND_BREAK(m_type) \ + { \ + MonoArray *managed = GDMonoMarshal::m_type##_to_mono_array(p_value.operator m_type()); \ + mono_field_set_value(p_object, mono_field, &managed); \ + break; \ + } + + switch (type.type_encoding) { + case MONO_TYPE_BOOLEAN: { + SET_FROM_PRIMITIVE(bool); + } break; + + case MONO_TYPE_I1: { + SET_FROM_PRIMITIVE(signed char); + } break; + case MONO_TYPE_I2: { + SET_FROM_PRIMITIVE(signed short); + } break; + case MONO_TYPE_I4: { + SET_FROM_PRIMITIVE(signed int); + } break; + case MONO_TYPE_I8: { + SET_FROM_PRIMITIVE(int64_t); + } break; + + case MONO_TYPE_U1: { + SET_FROM_PRIMITIVE(unsigned char); + } break; + case MONO_TYPE_U2: { + SET_FROM_PRIMITIVE(unsigned short); + } break; + case MONO_TYPE_U4: { + SET_FROM_PRIMITIVE(unsigned int); + } break; + case MONO_TYPE_U8: { + SET_FROM_PRIMITIVE(uint64_t); + } break; + + case MONO_TYPE_R4: { + SET_FROM_PRIMITIVE(float); + } break; + + case MONO_TYPE_R8: { + SET_FROM_PRIMITIVE(double); + } break; + + case MONO_TYPE_STRING: { + MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value); + mono_field_set_value(p_object, mono_field, mono_string); + } break; + + case MONO_TYPE_VALUETYPE: { + GDMonoClass *tclass = type.type_class; + + if (tclass == CACHED_CLASS(Vector2)) + SET_FROM_STRUCT_AND_BREAK(Vector2); + + if (tclass == CACHED_CLASS(Rect2)) + SET_FROM_STRUCT_AND_BREAK(Rect2); + + if (tclass == CACHED_CLASS(Transform2D)) + SET_FROM_STRUCT_AND_BREAK(Transform2D); + + if (tclass == CACHED_CLASS(Vector3)) + SET_FROM_STRUCT_AND_BREAK(Vector3); + + if (tclass == CACHED_CLASS(Basis)) + SET_FROM_STRUCT_AND_BREAK(Basis); + + if (tclass == CACHED_CLASS(Quat)) + SET_FROM_STRUCT_AND_BREAK(Quat); + + if (tclass == CACHED_CLASS(Transform)) + SET_FROM_STRUCT_AND_BREAK(Transform); + + if (tclass == CACHED_CLASS(Rect3)) + SET_FROM_STRUCT_AND_BREAK(Rect3); + + if (tclass == CACHED_CLASS(Color)) + SET_FROM_STRUCT_AND_BREAK(Color); + + if (tclass == CACHED_CLASS(Plane)) + SET_FROM_STRUCT_AND_BREAK(Plane); + + if (mono_class_is_enum(tclass->get_raw())) + SET_FROM_PRIMITIVE(signed int); + + ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + tclass->get_name()); + ERR_FAIL(); + } break; + + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: { + MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class)); + + if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) + SET_FROM_ARRAY_AND_BREAK(Array); + + if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) + SET_FROM_ARRAY_AND_BREAK(PoolByteArray); + + if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) + SET_FROM_ARRAY_AND_BREAK(PoolIntArray); + + if (array_type->eklass == REAL_T_MONOCLASS) + SET_FROM_ARRAY_AND_BREAK(PoolRealArray); + + if (array_type->eklass == CACHED_CLASS_RAW(String)) + SET_FROM_ARRAY_AND_BREAK(PoolStringArray); + + if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) + SET_FROM_ARRAY_AND_BREAK(PoolVector2Array); + + if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) + SET_FROM_ARRAY_AND_BREAK(PoolVector3Array); + + if (array_type->eklass == CACHED_CLASS_RAW(Color)) + SET_FROM_ARRAY_AND_BREAK(PoolColorArray); + + ERR_EXPLAIN(String() + "Attempted to convert Variant to a managed array of unmarshallable element type."); + ERR_FAIL(); + } break; + + case MONO_TYPE_CLASS: { + GDMonoClass *type_class = type.type_class; + + // GodotObject + if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { + MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *()); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + if (CACHED_CLASS(NodePath) == type_class) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath()); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + if (CACHED_CLASS(RID) == type_class) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator RID()); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name()); + ERR_FAIL(); + } break; + + case MONO_TYPE_OBJECT: { + // Variant + switch (p_value.get_type()) { + case Variant::BOOL: { + SET_FROM_PRIMITIVE(bool); + } break; + case Variant::INT: { + SET_FROM_PRIMITIVE(int); + } break; + case Variant::REAL: { +#ifdef REAL_T_IS_DOUBLE + SET_FROM_PRIMITIVE(double); +#else + SET_FROM_PRIMITIVE(float); +#endif + } break; + case Variant::STRING: { + MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value); + mono_field_set_value(p_object, mono_field, mono_string); + } break; + case Variant::VECTOR2: SET_FROM_STRUCT_AND_BREAK(Vector2); + case Variant::RECT2: SET_FROM_STRUCT_AND_BREAK(Rect2); + case Variant::VECTOR3: SET_FROM_STRUCT_AND_BREAK(Vector3); + case Variant::TRANSFORM2D: SET_FROM_STRUCT_AND_BREAK(Transform2D); + case Variant::PLANE: SET_FROM_STRUCT_AND_BREAK(Plane); + case Variant::QUAT: SET_FROM_STRUCT_AND_BREAK(Quat); + case Variant::RECT3: SET_FROM_STRUCT_AND_BREAK(Rect3); + case Variant::BASIS: SET_FROM_STRUCT_AND_BREAK(Basis); + case Variant::TRANSFORM: SET_FROM_STRUCT_AND_BREAK(Transform); + case Variant::COLOR: SET_FROM_STRUCT_AND_BREAK(Color); + case Variant::NODE_PATH: { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath()); + mono_field_set_value(p_object, mono_field, managed); + } break; + case Variant::_RID: { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator RID()); + mono_field_set_value(p_object, mono_field, managed); + } break; + case Variant::OBJECT: { + MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *()); + mono_field_set_value(p_object, mono_field, managed); + break; + } + case Variant::DICTIONARY: { + MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary()); + mono_field_set_value(p_object, mono_field, managed); + } break; + case Variant::ARRAY: SET_FROM_ARRAY_AND_BREAK(Array); + case Variant::POOL_BYTE_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolByteArray); + case Variant::POOL_INT_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolIntArray); + case Variant::POOL_REAL_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolRealArray); + case Variant::POOL_STRING_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolStringArray); + case Variant::POOL_VECTOR2_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolVector2Array); + case Variant::POOL_VECTOR3_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolVector3Array); + case Variant::POOL_COLOR_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolColorArray); +#undef SET_FROM_ARRAY_AND_BREAK + default: break; + } + } break; + + case MONO_TYPE_GENERICINST: { + if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_raw()) { + MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary()); + mono_field_set_value(p_object, mono_field, managed); + break; + } + } break; + + default: { + ERR_PRINTS(String() + "Attempted to set the value of a field of unexpected type encoding: " + itos(type.type_encoding)); + } break; + } + +#undef SET_FROM_STRUCT_AND_BREAK +#undef SET_FROM_PRIMITIVE +} + +bool GDMonoField::get_bool_value(MonoObject *p_object) { + return (bool)GDMonoMarshal::unbox<MonoBoolean>(get_value(p_object)); +} + +int GDMonoField::get_int_value(MonoObject *p_object) { + return GDMonoMarshal::unbox<int32_t>(get_value(p_object)); +} + +String GDMonoField::get_string_value(MonoObject *p_object) { + MonoObject *val = get_value(p_object); + return val ? GDMonoMarshal::mono_string_to_godot((MonoString *)val) : String(); +} + +bool GDMonoField::has_attribute(GDMonoClass *p_attr_class) { + ERR_FAIL_NULL_V(p_attr_class, false); + + if (!attrs_fetched) + fetch_attributes(); + + if (!attributes) + return false; + + return mono_custom_attrs_has_attr(attributes, p_attr_class->get_raw()); +} + +MonoObject *GDMonoField::get_attribute(GDMonoClass *p_attr_class) { + ERR_FAIL_NULL_V(p_attr_class, NULL); + + if (!attrs_fetched) + fetch_attributes(); + + if (!attributes) + return NULL; + + return mono_custom_attrs_get_attr(attributes, p_attr_class->get_raw()); +} + +void GDMonoField::fetch_attributes() { + ERR_FAIL_COND(attributes != NULL); + attributes = mono_custom_attrs_from_field(owner->get_raw(), get_raw()); + attrs_fetched = true; +} + +bool GDMonoField::is_static() { + return mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_STATIC; +} + +GDMono::MemberVisibility GDMonoField::get_visibility() { + switch (mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_FIELD_ACCESS_MASK) { + case MONO_FIELD_ATTR_PRIVATE: + return GDMono::PRIVATE; + case MONO_FIELD_ATTR_FAM_AND_ASSEM: + return GDMono::PROTECTED_AND_INTERNAL; + case MONO_FIELD_ATTR_ASSEMBLY: + return GDMono::INTERNAL; + case MONO_FIELD_ATTR_FAMILY: + return GDMono::PROTECTED; + case MONO_FIELD_ATTR_PUBLIC: + return GDMono::PUBLIC; + default: + ERR_FAIL_V(GDMono::PRIVATE); + } +} + +GDMonoField::GDMonoField(MonoClassField *p_raw_field, GDMonoClass *p_owner) { + owner = p_owner; + mono_field = p_raw_field; + name = mono_field_get_name(mono_field); + MonoType *field_type = mono_field_get_type(mono_field); + type.type_encoding = mono_type_get_type(field_type); + MonoClass *field_type_class = mono_class_from_mono_type(field_type); + type.type_class = GDMono::get_singleton()->get_class(field_type_class); + + attrs_fetched = false; + attributes = NULL; +} + +GDMonoField::~GDMonoField() { + if (attributes) { + mono_custom_attrs_free(attributes); + } +} diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h new file mode 100644 index 0000000000..b7e1942d71 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_field.h @@ -0,0 +1,74 @@ +/*************************************************************************/ +/* gd_mono_field.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GDMONOFIELD_H +#define GDMONOFIELD_H + +#include "gd_mono.h" +#include "gd_mono_header.h" + +class GDMonoField { + GDMonoClass *owner; + MonoClassField *mono_field; + + String name; + ManagedType type; + + bool attrs_fetched; + MonoCustomAttrInfo *attributes; + +public: + _FORCE_INLINE_ String get_name() const { return name; } + _FORCE_INLINE_ ManagedType get_type() const { return type; } + + _FORCE_INLINE_ MonoClassField *get_raw() const { return mono_field; } + + void set_value_raw(MonoObject *p_object, void *p_ptr); + void set_value(MonoObject *p_object, const Variant &p_value); + + _FORCE_INLINE_ MonoObject *get_value(MonoObject *p_object) { + return mono_field_get_value_object(mono_domain_get(), mono_field, p_object); + } + + bool get_bool_value(MonoObject *p_object); + int get_int_value(MonoObject *p_object); + String get_string_value(MonoObject *p_object); + + bool has_attribute(GDMonoClass *p_attr_class); + MonoObject *get_attribute(GDMonoClass *p_attr_class); + void fetch_attributes(); + + bool is_static(); + GDMono::MemberVisibility get_visibility(); + + GDMonoField(MonoClassField *p_raw_field, GDMonoClass *p_owner); + ~GDMonoField(); +}; + +#endif // GDMONOFIELD_H diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h new file mode 100644 index 0000000000..803d394f96 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_header.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* gd_mono_header.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GD_MONO_HEADER_H +#define GD_MONO_HEADER_H + +#include "int_types.h" + +class GDMonoAssembly; +class GDMonoClass; +class GDMonoMethod; +class GDMonoField; + +struct ManagedType { + int type_encoding; + GDMonoClass *type_class; + + ManagedType() { + type_class = 0; + } +}; + +typedef union { + uint32_t _uint32; + float _float; +} mono_float; + +typedef union { + uint64_t _uint64; + float _double; +} mono_double; + +#endif // GD_MONO_HEADER_H diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp new file mode 100644 index 0000000000..cfe2148b80 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -0,0 +1,66 @@ +/*************************************************************************/ +/* godotsharp_internals.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "gd_mono_internals.h" + +#include "../csharp_script.h" +#include "../mono_gc_handle.h" +#include "gd_mono_utils.h" + +namespace GDMonoInternals { + +void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { + + // This method should not fail + + CRASH_COND(!unmanaged); + + // All mono objects created from the managed world (e.g.: `new Player()`) + // need to have a CSharpScript in order for their methods to be callable from the unmanaged side + + Reference *ref = Object::cast_to<Reference>(unmanaged); + + GDMonoClass *klass = GDMonoUtils::get_object_class(managed); + + CRASH_COND(!klass); + + Ref<MonoGCHandle> gchandle = ref ? MonoGCHandle::create_weak(managed) : + MonoGCHandle::create_strong(managed); + + Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass); + + CRASH_COND(script.is_null()); + + ScriptInstance *si = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle); + + unmanaged->set_script_and_instance(script.get_ref_ptr(), si); + + return; +} +} diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h new file mode 100644 index 0000000000..6bdf4a6c46 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_internals.h @@ -0,0 +1,42 @@ +/*************************************************************************/ +/* godotsharp_internals.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GD_MONO_INTERNALS_H +#define GD_MONO_INTERNALS_H + +#include <mono/jit/jit.h> + +#include "core/object.h" + +namespace GDMonoInternals { + +void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged); +} + +#endif // GD_MONO_INTERNALS_H diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp new file mode 100644 index 0000000000..e473348897 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_log.cpp @@ -0,0 +1,175 @@ +/*************************************************************************/ +/* gd_mono_log.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "gd_mono_log.h" + +#include <mono/utils/mono-logger.h> +#include <stdlib.h> // abort + +#include "os/dir_access.h" +#include "os/os.h" + +#include "../godotsharp_dirs.h" + +static int log_level_get_id(const char *p_log_level) { + + const char *valid_log_levels[] = { "error", "critical", "warning", "message", "info", "debug", NULL }; + + int i = 0; + while (valid_log_levels[i]) { + if (!strcmp(valid_log_levels[i], p_log_level)) + return i; + i++; + } + + return -1; +} + +void gdmono_MonoLogCallback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) { + + FileAccess *f = GDMonoLog::get_singleton()->get_log_file(); + + if (GDMonoLog::get_singleton()->get_log_level_id() >= log_level_get_id(log_level)) { + String text(message); + text += " (in domain "; + text += log_domain; + if (log_level) { + text += ", "; + text += log_level; + } + text += ")\n"; + + f->seek_end(); + f->store_string(text); + } + + if (fatal) { + ERR_PRINTS("Mono: FALTAL ERROR, ABORTING! Logfile: " + GDMonoLog::get_singleton()->get_log_file_path() + "\n"); + abort(); + } +} + +GDMonoLog *GDMonoLog::singleton = NULL; + +bool GDMonoLog::_try_create_logs_dir(const String &p_logs_dir) { + + if (!DirAccess::exists(p_logs_dir)) { + DirAccessRef diraccess = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + ERR_FAIL_COND_V(!diraccess, false); + Error logs_mkdir_err = diraccess->make_dir_recursive(p_logs_dir); + ERR_EXPLAIN("Failed to create mono logs directory"); + ERR_FAIL_COND_V(logs_mkdir_err != OK, false); + } + + return true; +} + +void GDMonoLog::_open_log_file(const String &p_file_path) { + + log_file = FileAccess::open(p_file_path, FileAccess::WRITE); + + ERR_EXPLAIN("Failed to create log file"); + ERR_FAIL_COND(!log_file); +} + +void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { + + static const uint64_t MAX_SECS = 5 * 86400; + + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + ERR_FAIL_COND(!da); + + Error err = da->change_dir(p_logs_dir); + ERR_FAIL_COND(err != OK); + + ERR_FAIL_COND(da->list_dir_begin() != OK); + + String current; + while ((current = da->get_next()).length()) { + if (da->current_is_dir()) + continue; + if (!current.ends_with(".txt")) + continue; + + String name = current.get_basename(); + uint64_t unixtime = (uint64_t)name.to_int64(); + + if (OS::get_singleton()->get_unix_time() - unixtime > MAX_SECS) { + da->remove(current); + } + } + + da->list_dir_end(); +} + +void GDMonoLog::initialize() { + +#ifdef DEBUG_ENABLED + const char *log_level = "debug"; +#else + const char *log_level = "warning"; +#endif + + String logs_dir = GodotSharpDirs::get_mono_logs_dir(); + + if (_try_create_logs_dir(logs_dir)) { + _delete_old_log_files(logs_dir); + + log_file_path = logs_dir.plus_file(String::num_int64(OS::get_singleton()->get_unix_time()) + ".txt"); + _open_log_file(log_file_path); + } + + mono_trace_set_level_string(log_level); + log_level_id = log_level_get_id(log_level); + + if (log_file) { + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->print(String("Mono: Logfile is " + log_file_path + "\n").utf8()); + mono_trace_set_log_handler(gdmono_MonoLogCallback, this); + } else { + OS::get_singleton()->printerr("Mono: No log file, using default log handler\n"); + } +} + +GDMonoLog::GDMonoLog() { + + singleton = this; + + log_level_id = -1; +} + +GDMonoLog::~GDMonoLog() { + + singleton = NULL; + + if (log_file) { + log_file->close(); + memdelete(log_file); + } +} diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h new file mode 100644 index 0000000000..497f1e5317 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_log.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* gd_mono_log.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GD_MONO_LOG_H +#define GD_MONO_LOG_H + +#include "os/file_access.h" + +class GDMonoLog { + + int log_level_id; + + FileAccess *log_file; + String log_file_path; + + bool _try_create_logs_dir(const String &p_logs_dir); + void _open_log_file(const String &p_file_path); + void _delete_old_log_files(const String &p_logs_dir); + + static GDMonoLog *singleton; + +public: + _FORCE_INLINE_ static GDMonoLog *get_singleton() { return singleton; } + + void initialize(); + + _FORCE_INLINE_ FileAccess *get_log_file() { return log_file; } + _FORCE_INLINE_ String get_log_file_path() { return log_file_path; } + _FORCE_INLINE_ int get_log_level_id() { return log_level_id; } + + GDMonoLog(); + ~GDMonoLog(); +}; + +#endif // GD_MONO_LOG_H diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp new file mode 100644 index 0000000000..77a1ef3cb0 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -0,0 +1,859 @@ +/*************************************************************************/ +/* gd_mono_marshal.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "gd_mono_marshal.h" + +#include "gd_mono.h" +#include "gd_mono_class.h" + +namespace GDMonoMarshal { + +#define RETURN_BOXED_STRUCT(m_t, m_var_in) \ + { \ + const m_t &m_in = m_var_in->operator m_t(); \ + MARSHALLED_OUT(m_t, m_in, raw); \ + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(m_t), raw); \ + } + +#define RETURN_UNBOXED_STRUCT(m_t, m_var_in) \ + { \ + float *raw = (float *)mono_object_unbox(m_var_in); \ + MARSHALLED_IN(m_t, raw, ret); \ + return ret; \ + } + +Variant::Type managed_to_variant_type(const ManagedType &p_type) { + switch (p_type.type_encoding) { + case MONO_TYPE_BOOLEAN: + return Variant::BOOL; + + case MONO_TYPE_I1: + return Variant::INT; + case MONO_TYPE_I2: + return Variant::INT; + case MONO_TYPE_I4: + return Variant::INT; + case MONO_TYPE_I8: + return Variant::INT; + + case MONO_TYPE_U1: + return Variant::INT; + case MONO_TYPE_U2: + return Variant::INT; + case MONO_TYPE_U4: + return Variant::INT; + case MONO_TYPE_U8: + return Variant::INT; + + case MONO_TYPE_R4: + return Variant::REAL; + case MONO_TYPE_R8: + return Variant::REAL; + + case MONO_TYPE_STRING: { + return Variant::STRING; + } break; + + case MONO_TYPE_VALUETYPE: { + GDMonoClass *tclass = p_type.type_class; + + if (tclass == CACHED_CLASS(Vector2)) + return Variant::VECTOR2; + + if (tclass == CACHED_CLASS(Rect2)) + return Variant::RECT2; + + if (tclass == CACHED_CLASS(Transform2D)) + return Variant::TRANSFORM2D; + + if (tclass == CACHED_CLASS(Vector3)) + return Variant::VECTOR3; + + if (tclass == CACHED_CLASS(Basis)) + return Variant::BASIS; + + if (tclass == CACHED_CLASS(Quat)) + return Variant::QUAT; + + if (tclass == CACHED_CLASS(Transform)) + return Variant::TRANSFORM; + + if (tclass == CACHED_CLASS(Rect3)) + return Variant::RECT3; + + if (tclass == CACHED_CLASS(Color)) + return Variant::COLOR; + + if (tclass == CACHED_CLASS(Plane)) + return Variant::PLANE; + + if (mono_class_is_enum(tclass->get_raw())) + return Variant::INT; + } break; + + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: { + MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class)); + + if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) + return Variant::ARRAY; + + if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) + return Variant::POOL_BYTE_ARRAY; + + if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) + return Variant::POOL_INT_ARRAY; + + if (array_type->eklass == REAL_T_MONOCLASS) + return Variant::POOL_REAL_ARRAY; + + if (array_type->eklass == CACHED_CLASS_RAW(String)) + return Variant::POOL_STRING_ARRAY; + + if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) + return Variant::POOL_VECTOR2_ARRAY; + + if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) + return Variant::POOL_VECTOR3_ARRAY; + + if (array_type->eklass == CACHED_CLASS_RAW(Color)) + return Variant::POOL_COLOR_ARRAY; + } break; + + case MONO_TYPE_CLASS: { + GDMonoClass *type_class = p_type.type_class; + + // GodotObject + if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { + return Variant::OBJECT; + } + + if (CACHED_CLASS(NodePath) == type_class) { + return Variant::NODE_PATH; + } + + if (CACHED_CLASS(RID) == type_class) { + return Variant::_RID; + } + } break; + + case MONO_TYPE_GENERICINST: { + if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_raw()) { + return Variant::DICTIONARY; + } + } break; + + default: { + } break; + } + + // Unknown + return Variant::NIL; +} + +String mono_to_utf8_string(MonoString *p_mono_string) { + MonoError error; + char *utf8 = mono_string_to_utf8_checked(p_mono_string, &error); + + ERR_EXPLAIN("Conversion of MonoString to UTF8 failed."); + ERR_FAIL_COND_V(!mono_error_ok(&error), String()); + + String ret = String::utf8(utf8); + + mono_free(utf8); + + return ret; +} + +String mono_to_utf16_string(MonoString *p_mono_string) { + int len = mono_string_length(p_mono_string); + String ret; + + if (len == 0) + return ret; + + ret.resize(len + 1); + ret.set(len, 0); + + CharType *src = (CharType *)mono_string_chars(p_mono_string); + CharType *dst = &(ret.operator[](0)); + + for (int i = 0; i < len; i++) { + dst[i] = src[i]; + } + + return ret; +} + +MonoObject *variant_to_mono_object(const Variant *p_var) { + ManagedType type; + + type.type_encoding = MONO_TYPE_OBJECT; + + return variant_to_mono_object(p_var, type); +} + +MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type) { + switch (p_type.type_encoding) { + case MONO_TYPE_BOOLEAN: { + MonoBoolean val = p_var->operator bool(); + return BOX_BOOLEAN(val); + } + + case MONO_TYPE_I1: { + char val = p_var->operator signed char(); + return BOX_INT8(val); + } + case MONO_TYPE_I2: { + short val = p_var->operator signed short(); + return BOX_INT16(val); + } + case MONO_TYPE_I4: { + int val = p_var->operator signed int(); + return BOX_INT32(val); + } + case MONO_TYPE_I8: { + int64_t val = p_var->operator int64_t(); + return BOX_INT64(val); + } + + case MONO_TYPE_U1: { + char val = p_var->operator unsigned char(); + return BOX_UINT8(val); + } + case MONO_TYPE_U2: { + short val = p_var->operator unsigned short(); + return BOX_UINT16(val); + } + case MONO_TYPE_U4: { + int val = p_var->operator unsigned int(); + return BOX_UINT32(val); + } + case MONO_TYPE_U8: { + uint64_t val = p_var->operator uint64_t(); + return BOX_UINT64(val); + } + + case MONO_TYPE_R4: { + float val = p_var->operator float(); + return BOX_FLOAT(val); + } + case MONO_TYPE_R8: { + double val = p_var->operator double(); + return BOX_DOUBLE(val); + } + + case MONO_TYPE_STRING: { + return (MonoObject *)mono_string_from_godot(p_var->operator String()); + } break; + + case MONO_TYPE_VALUETYPE: { + GDMonoClass *tclass = p_type.type_class; + + if (tclass == CACHED_CLASS(Vector2)) + RETURN_BOXED_STRUCT(Vector2, p_var); + + if (tclass == CACHED_CLASS(Rect2)) + RETURN_BOXED_STRUCT(Rect2, p_var); + + if (tclass == CACHED_CLASS(Transform2D)) + RETURN_BOXED_STRUCT(Transform2D, p_var); + + if (tclass == CACHED_CLASS(Vector3)) + RETURN_BOXED_STRUCT(Vector3, p_var); + + if (tclass == CACHED_CLASS(Basis)) + RETURN_BOXED_STRUCT(Basis, p_var); + + if (tclass == CACHED_CLASS(Quat)) + RETURN_BOXED_STRUCT(Quat, p_var); + + if (tclass == CACHED_CLASS(Transform)) + RETURN_BOXED_STRUCT(Transform, p_var); + + if (tclass == CACHED_CLASS(Rect3)) + RETURN_BOXED_STRUCT(Rect3, p_var); + + if (tclass == CACHED_CLASS(Color)) + RETURN_BOXED_STRUCT(Color, p_var); + + if (tclass == CACHED_CLASS(Plane)) + RETURN_BOXED_STRUCT(Plane, p_var); + + if (mono_class_is_enum(tclass->get_raw())) { + int val = p_var->operator signed int(); + return BOX_ENUM(tclass->get_raw(), val); + } + } break; + + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: { + MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class)); + + if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) + return (MonoObject *)Array_to_mono_array(p_var->operator Array()); + + if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) + return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray()); + + if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) + return (MonoObject *)PoolIntArray_to_mono_array(p_var->operator PoolIntArray()); + + if (array_type->eklass == REAL_T_MONOCLASS) + return (MonoObject *)PoolRealArray_to_mono_array(p_var->operator PoolRealArray()); + + if (array_type->eklass == CACHED_CLASS_RAW(String)) + return (MonoObject *)PoolStringArray_to_mono_array(p_var->operator PoolStringArray()); + + if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) + return (MonoObject *)PoolVector2Array_to_mono_array(p_var->operator PoolVector2Array()); + + if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) + return (MonoObject *)PoolVector3Array_to_mono_array(p_var->operator PoolVector3Array()); + + if (array_type->eklass == CACHED_CLASS_RAW(Color)) + return (MonoObject *)PoolColorArray_to_mono_array(p_var->operator PoolColorArray()); + + ERR_EXPLAIN(String() + "Attempted to convert Variant to a managed array of unmarshallable element type."); + ERR_FAIL_V(NULL); + } break; + + case MONO_TYPE_CLASS: { + GDMonoClass *type_class = p_type.type_class; + + // GodotObject + if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { + return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *()); + } + + if (CACHED_CLASS(NodePath) == type_class) { + return GDMonoUtils::create_managed_from(p_var->operator NodePath()); + } + + if (CACHED_CLASS(RID) == type_class) { + return GDMonoUtils::create_managed_from(p_var->operator RID()); + } + } break; + case MONO_TYPE_OBJECT: { + // Variant + switch (p_var->get_type()) { + case Variant::BOOL: { + MonoBoolean val = p_var->operator bool(); + return BOX_BOOLEAN(val); + } + case Variant::INT: { + int val = p_var->operator signed int(); + return BOX_INT32(val); + } + case Variant::REAL: { +#ifdef REAL_T_IS_DOUBLE + double val = p_var->operator double(); + return BOX_DOUBLE(val); +#else + float val = p_var->operator float(); + return BOX_FLOAT(val); +#endif + } + case Variant::STRING: + return (MonoObject *)mono_string_from_godot(p_var->operator String()); + case Variant::VECTOR2: + RETURN_BOXED_STRUCT(Vector2, p_var); + case Variant::RECT2: + RETURN_BOXED_STRUCT(Rect2, p_var); + case Variant::VECTOR3: + RETURN_BOXED_STRUCT(Vector3, p_var); + case Variant::TRANSFORM2D: + RETURN_BOXED_STRUCT(Transform2D, p_var); + case Variant::PLANE: + RETURN_BOXED_STRUCT(Plane, p_var); + case Variant::QUAT: + RETURN_BOXED_STRUCT(Quat, p_var); + case Variant::RECT3: + RETURN_BOXED_STRUCT(Rect3, p_var); + case Variant::BASIS: + RETURN_BOXED_STRUCT(Basis, p_var); + case Variant::TRANSFORM: + RETURN_BOXED_STRUCT(Transform, p_var); + case Variant::COLOR: + RETURN_BOXED_STRUCT(Color, p_var); + case Variant::NODE_PATH: + return GDMonoUtils::create_managed_from(p_var->operator NodePath()); + case Variant::_RID: + return GDMonoUtils::create_managed_from(p_var->operator RID()); + case Variant::OBJECT: { + return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *()); + } + case Variant::DICTIONARY: + return Dictionary_to_mono_object(p_var->operator Dictionary()); + case Variant::ARRAY: + return (MonoObject *)Array_to_mono_array(p_var->operator Array()); + case Variant::POOL_BYTE_ARRAY: + return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray()); + case Variant::POOL_INT_ARRAY: + return (MonoObject *)PoolIntArray_to_mono_array(p_var->operator PoolIntArray()); + case Variant::POOL_REAL_ARRAY: + return (MonoObject *)PoolRealArray_to_mono_array(p_var->operator PoolRealArray()); + case Variant::POOL_STRING_ARRAY: + return (MonoObject *)PoolStringArray_to_mono_array(p_var->operator PoolStringArray()); + case Variant::POOL_VECTOR2_ARRAY: + return (MonoObject *)PoolVector2Array_to_mono_array(p_var->operator PoolVector2Array()); + case Variant::POOL_VECTOR3_ARRAY: + return (MonoObject *)PoolVector3Array_to_mono_array(p_var->operator PoolVector3Array()); + case Variant::POOL_COLOR_ARRAY: + return (MonoObject *)PoolColorArray_to_mono_array(p_var->operator PoolColorArray()); + default: + return NULL; + } + break; + case MONO_TYPE_GENERICINST: { + if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_raw()) { + return Dictionary_to_mono_object(p_var->operator Dictionary()); + } + } break; + } break; + } + + ERR_EXPLAIN(String() + "Attempted to convert Variant to an unmarshallable managed type. Name: \'" + + p_type.type_class->get_name() + "\' Encoding: " + itos(p_type.type_encoding)); + ERR_FAIL_V(NULL); +} + +Variant mono_object_to_variant(MonoObject *p_obj) { + if (!p_obj) + return Variant(); + + GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_object_get_class(p_obj)); + ERR_FAIL_COND_V(!tclass, Variant()); + + MonoType *raw_type = tclass->get_raw_type(tclass); + + ManagedType type; + + type.type_encoding = mono_type_get_type(raw_type); + type.type_class = tclass; + + return mono_object_to_variant(p_obj, type); +} + +Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type) { + switch (p_type.type_encoding) { + case MONO_TYPE_BOOLEAN: + return (bool)unbox<MonoBoolean>(p_obj); + + case MONO_TYPE_I1: + return unbox<int8_t>(p_obj); + case MONO_TYPE_I2: + return unbox<int16_t>(p_obj); + case MONO_TYPE_I4: + return unbox<int32_t>(p_obj); + case MONO_TYPE_I8: + return unbox<int64_t>(p_obj); + + case MONO_TYPE_U1: + return unbox<uint8_t>(p_obj); + case MONO_TYPE_U2: + return unbox<uint16_t>(p_obj); + case MONO_TYPE_U4: + return unbox<uint32_t>(p_obj); + case MONO_TYPE_U8: + return unbox<uint64_t>(p_obj); + + case MONO_TYPE_R4: + return unbox<float>(p_obj); + case MONO_TYPE_R8: + return unbox<double>(p_obj); + + case MONO_TYPE_STRING: { + String str = mono_string_to_godot((MonoString *)p_obj); + return str; + } break; + + case MONO_TYPE_VALUETYPE: { + GDMonoClass *tclass = p_type.type_class; + + if (tclass == CACHED_CLASS(Vector2)) + RETURN_UNBOXED_STRUCT(Vector2, p_obj); + + if (tclass == CACHED_CLASS(Rect2)) + RETURN_UNBOXED_STRUCT(Rect2, p_obj); + + if (tclass == CACHED_CLASS(Transform2D)) + RETURN_UNBOXED_STRUCT(Transform2D, p_obj); + + if (tclass == CACHED_CLASS(Vector3)) + RETURN_UNBOXED_STRUCT(Vector3, p_obj); + + if (tclass == CACHED_CLASS(Basis)) + RETURN_UNBOXED_STRUCT(Basis, p_obj); + + if (tclass == CACHED_CLASS(Quat)) + RETURN_UNBOXED_STRUCT(Quat, p_obj); + + if (tclass == CACHED_CLASS(Transform)) + RETURN_UNBOXED_STRUCT(Transform, p_obj); + + if (tclass == CACHED_CLASS(Rect3)) + RETURN_UNBOXED_STRUCT(Rect3, p_obj); + + if (tclass == CACHED_CLASS(Color)) + RETURN_UNBOXED_STRUCT(Color, p_obj); + + if (tclass == CACHED_CLASS(Plane)) + RETURN_UNBOXED_STRUCT(Plane, p_obj); + + if (mono_class_is_enum(tclass->get_raw())) + return unbox<int32_t>(p_obj); + } break; + + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: { + MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class)); + + if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) + return mono_array_to_Array((MonoArray *)p_obj); + + if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) + return mono_array_to_PoolByteArray((MonoArray *)p_obj); + + if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) + return mono_array_to_PoolIntArray((MonoArray *)p_obj); + + if (array_type->eklass == REAL_T_MONOCLASS) + return mono_array_to_PoolRealArray((MonoArray *)p_obj); + + if (array_type->eklass == CACHED_CLASS_RAW(String)) + return mono_array_to_PoolStringArray((MonoArray *)p_obj); + + if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) + return mono_array_to_PoolVector2Array((MonoArray *)p_obj); + + if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) + return mono_array_to_PoolVector3Array((MonoArray *)p_obj); + + if (array_type->eklass == CACHED_CLASS_RAW(Color)) + return mono_array_to_PoolColorArray((MonoArray *)p_obj); + + ERR_EXPLAIN(String() + "Attempted to convert a managed array of unmarshallable element type to Variant."); + ERR_FAIL_V(Variant()); + } break; + + case MONO_TYPE_CLASS: { + GDMonoClass *type_class = p_type.type_class; + + // GodotObject + if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { + Object *ptr = unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj)); + return ptr ? Variant(ptr) : Variant(); + } + + if (CACHED_CLASS(NodePath) == type_class) { + NodePath *ptr = unbox<NodePath *>(CACHED_FIELD(NodePath, ptr)->get_value(p_obj)); + return ptr ? Variant(*ptr) : Variant(); + } + + if (CACHED_CLASS(RID) == type_class) { + RID *ptr = unbox<RID *>(CACHED_FIELD(RID, ptr)->get_value(p_obj)); + return ptr ? Variant(*ptr) : Variant(); + } + } break; + + case MONO_TYPE_GENERICINST: { + if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_raw()) { + return mono_object_to_Dictionary(p_obj); + } + } break; + } + + ERR_EXPLAIN(String() + "Attempted to convert an unmarshallable managed type to Variant. Name: \'" + + p_type.type_class->get_name() + "\' Encoding: " + itos(p_type.type_encoding)); + ERR_FAIL_V(Variant()); +} + +MonoArray *Array_to_mono_array(const Array &p_array) { + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_array.size()); + + for (int i = 0; i < p_array.size(); i++) { + MonoObject *boxed = variant_to_mono_object(p_array[i]); + mono_array_set(ret, MonoObject *, i, boxed); + } + + return ret; +} + +Array mono_array_to_Array(MonoArray *p_array) { + Array ret; + int length = mono_array_length(p_array); + + for (int i = 0; i < length; i++) { + MonoObject *elem = mono_array_get(p_array, MonoObject *, i); + ret.push_back(mono_object_to_variant(elem)); + } + + return ret; +} + +// TODO Optimize reading/writing from/to PoolArrays + +MonoArray *PoolIntArray_to_mono_array(const PoolIntArray &p_array) { + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), p_array.size()); + + for (int i = 0; i < p_array.size(); i++) { + mono_array_set(ret, int32_t, i, p_array[i]); + } + + return ret; +} + +PoolIntArray mono_array_to_PoolIntArray(MonoArray *p_array) { + PoolIntArray ret; + int length = mono_array_length(p_array); + + for (int i = 0; i < length; i++) { + int32_t elem = mono_array_get(p_array, int32_t, i); + ret.push_back(elem); + } + + return ret; +} + +MonoArray *PoolByteArray_to_mono_array(const PoolByteArray &p_array) { + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), p_array.size()); + + for (int i = 0; i < p_array.size(); i++) { + mono_array_set(ret, uint8_t, i, p_array[i]); + } + + return ret; +} + +PoolByteArray mono_array_to_PoolByteArray(MonoArray *p_array) { + PoolByteArray ret; + int length = mono_array_length(p_array); + + for (int i = 0; i < length; i++) { + uint8_t elem = mono_array_get(p_array, uint8_t, i); + ret.push_back(elem); + } + + return ret; +} + +MonoArray *PoolRealArray_to_mono_array(const PoolRealArray &p_array) { + MonoArray *ret = mono_array_new(mono_domain_get(), REAL_T_MONOCLASS, p_array.size()); + + for (int i = 0; i < p_array.size(); i++) { + mono_array_set(ret, real_t, i, p_array[i]); + } + + return ret; +} + +PoolRealArray mono_array_to_PoolRealArray(MonoArray *p_array) { + PoolRealArray ret; + int length = mono_array_length(p_array); + + for (int i = 0; i < length; i++) { + real_t elem = mono_array_get(p_array, real_t, i); + ret.push_back(elem); + } + + return ret; +} + +MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array) { + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), p_array.size()); + + for (int i = 0; i < p_array.size(); i++) { + MonoString *boxed = mono_string_from_godot(p_array[i]); + mono_array_set(ret, MonoString *, i, boxed); + } + + return ret; +} + +PoolStringArray mono_array_to_PoolStringArray(MonoArray *p_array) { + PoolStringArray ret; + int length = mono_array_length(p_array); + + for (int i = 0; i < length; i++) { + MonoString *elem = mono_array_get(p_array, MonoString *, i); + ret.push_back(mono_string_to_godot(elem)); + } + + return ret; +} + +MonoArray *PoolColorArray_to_mono_array(const PoolColorArray &p_array) { + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), p_array.size()); + + for (int i = 0; i < p_array.size(); i++) { +#ifdef YOLOCOPY + mono_array_set(ret, Color, i, p_array[i]); +#else + real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 4, i); + const Color &elem = p_array[i]; + raw[0] = elem.r; + raw[1] = elem.g; + raw[2] = elem.b; + raw[3] = elem.a; +#endif + } + + return ret; +} + +PoolColorArray mono_array_to_PoolColorArray(MonoArray *p_array) { + PoolColorArray ret; + int length = mono_array_length(p_array); + + for (int i = 0; i < length; i++) { + real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 4, i); + MARSHALLED_IN(Color, raw_elem, elem); + ret.push_back(elem); + } + + return ret; +} + +MonoArray *PoolVector2Array_to_mono_array(const PoolVector2Array &p_array) { + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), p_array.size()); + + for (int i = 0; i < p_array.size(); i++) { +#ifdef YOLOCOPY + mono_array_set(ret, Vector2, i, p_array[i]); +#else + real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 2, i); + const Vector2 &elem = p_array[i]; + raw[0] = elem.x; + raw[1] = elem.y; +#endif + } + + return ret; +} + +PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array) { + PoolVector2Array ret; + int length = mono_array_length(p_array); + + for (int i = 0; i < length; i++) { + real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 2, i); + MARSHALLED_IN(Vector2, raw_elem, elem); + ret.push_back(elem); + } + + return ret; +} + +MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array) { + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), p_array.size()); + + for (int i = 0; i < p_array.size(); i++) { +#ifdef YOLOCOPY + mono_array_set(ret, Vector3, i, p_array[i]); +#else + real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 3, i); + const Vector3 &elem = p_array[i]; + raw[0] = elem.x; + raw[1] = elem.y; + raw[2] = elem.z; +#endif + } + + return ret; +} + +PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) { + PoolVector3Array ret; + int length = mono_array_length(p_array); + + for (int i = 0; i < length; i++) { + real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 3, i); + MARSHALLED_IN(Vector3, raw_elem, elem); + ret.push_back(elem); + } + + return ret; +} + +MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) { + MonoArray *keys = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size()); + MonoArray *values = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size()); + + int i = 0; + const Variant *dkey = NULL; + while ((dkey = p_dict.next(dkey))) { + mono_array_set(keys, MonoObject *, i, variant_to_mono_object(dkey)); + mono_array_set(values, MonoObject *, i, variant_to_mono_object(p_dict[*dkey])); + i++; + } + + GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary); + + MonoObject *ex = NULL; + MonoObject *ret = arrays_to_dict(keys, values, &ex); + + if (ex) { + mono_print_unhandled_exception(ex); + ERR_FAIL_V(NULL); + } + + return ret; +} + +Dictionary mono_object_to_Dictionary(MonoObject *p_dict) { + Dictionary ret; + + GDMonoUtils::MarshalUtils_DictToArrays dict_to_arrays = CACHED_METHOD_THUNK(MarshalUtils, DictionaryToArrays); + + MonoArray *keys = NULL; + MonoArray *values = NULL; + MonoObject *ex = NULL; + dict_to_arrays(p_dict, &keys, &values, &ex); + + if (ex) { + mono_print_unhandled_exception(ex); + ERR_FAIL_V(Dictionary()); + } + + int length = mono_array_length(keys); + + for (int i = 0; i < length; i++) { + MonoObject *key_obj = mono_array_get(keys, MonoObject *, i); + MonoObject *value_obj = mono_array_get(values, MonoObject *, i); + + Variant key = key_obj ? mono_object_to_variant(key_obj) : Variant(); + Variant value = value_obj ? mono_object_to_variant(value_obj) : Variant(); + + ret[key] = value; + } + + return ret; +} +} diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h new file mode 100644 index 0000000000..9f403b787f --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -0,0 +1,219 @@ +/*************************************************************************/ +/* gd_mono_marshal.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GDMONOMARSHAL_H +#define GDMONOMARSHAL_H + +#include "gd_mono.h" +#include "gd_mono_utils.h" +#include "variant.h" + +namespace GDMonoMarshal { + +template <typename T> +T unbox(MonoObject *p_obj) { + return *(T *)mono_object_unbox(p_obj); +} + +#define BOX_DOUBLE(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(double), &x) +#define BOX_FLOAT(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(float), &x) +#define BOX_INT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int64_t), &x) +#define BOX_INT32(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int32_t), &x) +#define BOX_INT16(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int16_t), &x) +#define BOX_INT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int8_t), &x) +#define BOX_UINT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint64_t), &x) +#define BOX_UINT32(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint32_t), &x) +#define BOX_UINT16(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint16_t), &x) +#define BOX_UINT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), &x) +#define BOX_BOOLEAN(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(bool), &x) +#define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x) +#define BOX_ENUM(m_enum_class, x) mono_value_box(mono_domain_get(), m_enum_class, &x) + +Variant::Type managed_to_variant_type(const ManagedType &p_type); + +// String + +String mono_to_utf8_string(MonoString *p_mono_string); +String mono_to_utf16_string(MonoString *p_mono_string); + +_FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) { + if (sizeof(CharType) == 2) + return mono_to_utf16_string(p_mono_string); + + return mono_to_utf8_string(p_mono_string); +} + +_FORCE_INLINE_ MonoString *mono_from_utf8_string(const String &p_string) { + return mono_string_new(mono_domain_get(), p_string.utf8().get_data()); +} + +_FORCE_INLINE_ MonoString *mono_from_utf16_string(const String &p_string) { + return mono_string_from_utf16((mono_unichar2 *)p_string.c_str()); +} + +_FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) { + if (sizeof(CharType) == 2) + return mono_from_utf16_string(p_string); + + return mono_from_utf8_string(p_string); +} + +// Variant + +MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type); +MonoObject *variant_to_mono_object(const Variant *p_var); + +_FORCE_INLINE_ MonoObject *variant_to_mono_object(Variant p_var) { + return variant_to_mono_object(&p_var); +} + +Variant mono_object_to_variant(MonoObject *p_obj); +Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type); + +// Array + +MonoArray *Array_to_mono_array(const Array &p_array); +Array mono_array_to_Array(MonoArray *p_array); + +// PoolIntArray + +MonoArray *PoolIntArray_to_mono_array(const PoolIntArray &p_array); +PoolIntArray mono_array_to_PoolIntArray(MonoArray *p_array); + +// PoolByteArray + +MonoArray *PoolByteArray_to_mono_array(const PoolByteArray &p_array); +PoolByteArray mono_array_to_PoolByteArray(MonoArray *p_array); + +// PoolRealArray + +MonoArray *PoolRealArray_to_mono_array(const PoolRealArray &p_array); +PoolRealArray mono_array_to_PoolRealArray(MonoArray *p_array); + +// PoolStringArray + +MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array); +PoolStringArray mono_array_to_PoolStringArray(MonoArray *p_array); + +// PoolColorArray + +MonoArray *PoolColorArray_to_mono_array(const PoolColorArray &p_array); +PoolColorArray mono_array_to_PoolColorArray(MonoArray *p_array); + +// PoolVector2Array + +MonoArray *PoolVector2Array_to_mono_array(const PoolVector2Array &p_array); +PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array); + +// PoolVector3Array + +MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array); +PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array); + +// Dictionary + +MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict); +Dictionary mono_object_to_Dictionary(MonoObject *p_dict); + +#ifdef YOLO_COPY +#define MARSHALLED_OUT(m_t, m_in, m_out) m_t *m_out = (m_t *)&m_in; +#define MARSHALLED_IN(m_t, m_in, m_out) m_t m_out = *reinterpret_cast<m_t *>(m_in); +#else + +// Expects m_in to be of type float* + +#define MARSHALLED_OUT(m_t, m_in, m_out) MARSHALLED_OUT_##m_t(m_in, m_out) +#define MARSHALLED_IN(m_t, m_in, m_out) MARSHALLED_IN_##m_t(m_in, m_out) + +// Vector2 + +#define MARSHALLED_OUT_Vector2(m_in, m_out) real_t m_out[2] = { m_in.x, m_in.y }; +#define MARSHALLED_IN_Vector2(m_in, m_out) Vector2 m_out(m_in[0], m_in[1]); + +// Rect2 + +#define MARSHALLED_OUT_Rect2(m_in, m_out) real_t m_out[4] = { m_in.position.x, m_in.position.y, m_in.size.width, m_in.size.height }; +#define MARSHALLED_IN_Rect2(m_in, m_out) Rect2 m_out(m_in[0], m_in[1], m_in[2], m_in[3]); + +// Transform2D + +#define MARSHALLED_OUT_Transform2D(m_in, m_out) real_t m_out[6] = { m_in[0].x, m_in[0].y, m_in[1].x, m_in[1].y, m_in[2].x, m_in[2].y }; +#define MARSHALLED_IN_Transform2D(m_in, m_out) Transform2D m_out(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5]); + +// Vector3 + +#define MARSHALLED_OUT_Vector3(m_in, m_out) real_t m_out[3] = { m_in.x, m_in.y, m_in.z }; +#define MARSHALLED_IN_Vector3(m_in, m_out) Vector3 m_out(m_in[0], m_in[1], m_in[2]); + +// Basis + +#define MARSHALLED_OUT_Basis(m_in, m_out) real_t m_out[9] = { \ + m_in[0].x, m_in[0].y, m_in[0].z, \ + m_in[1].x, m_in[1].y, m_in[1].z, \ + m_in[2].x, m_in[2].y, m_in[2].z \ +}; +#define MARSHALLED_IN_Basis(m_in, m_out) Basis m_out(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5], m_in[6], m_in[7], m_in[8]); + +// Quat + +#define MARSHALLED_OUT_Quat(m_in, m_out) real_t m_out[4] = { m_in.x, m_in.y, m_in.z, m_in.w }; +#define MARSHALLED_IN_Quat(m_in, m_out) Quat m_out(m_in[0], m_in[1], m_in[2], m_in[3]); + +// Transform + +#define MARSHALLED_OUT_Transform(m_in, m_out) real_t m_out[12] = { \ + m_in.basis[0].x, m_in.basis[0].y, m_in.basis[0].z, \ + m_in.basis[1].x, m_in.basis[1].y, m_in.basis[1].z, \ + m_in.basis[2].x, m_in.basis[2].y, m_in.basis[2].z, \ + m_in.origin.x, m_in.origin.y, m_in.origin.z \ +}; +#define MARSHALLED_IN_Transform(m_in, m_out) Transform m_out( \ + Basis(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5], m_in[6], m_in[7], m_in[8]), \ + Vector3(m_in[9], m_in[10], m_in[11])); + +// Rect3 + +#define MARSHALLED_OUT_Rect3(m_in, m_out) real_t m_out[6] = { m_in.position.x, m_in.position.y, m_in.position.z, m_in.size.x, m_in.size.y, m_in.size.z }; +#define MARSHALLED_IN_Rect3(m_in, m_out) Rect3 m_out(Vector3(m_in[0], m_in[1], m_in[2]), Vector3(m_in[3], m_in[4], m_in[5])); + +// Color + +#define MARSHALLED_OUT_Color(m_in, m_out) real_t m_out[4] = { m_in.r, m_in.g, m_in.b, m_in.a }; +#define MARSHALLED_IN_Color(m_in, m_out) Color m_out(m_in[0], m_in[1], m_in[2], m_in[3]); + +// Plane + +#define MARSHALLED_OUT_Plane(m_in, m_out) real_t m_out[4] = { m_in.normal.x, m_in.normal.y, m_in.normal.z, m_in.d }; +#define MARSHALLED_IN_Plane(m_in, m_out) Plane m_out(m_in[0], m_in[1], m_in[2], m_in[3]); + +#endif + +} // GDMonoMarshal + +#endif // GDMONOMARSHAL_H diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp new file mode 100644 index 0000000000..eb97d62900 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -0,0 +1,227 @@ +/*************************************************************************/ +/* gd_mono_method.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "gd_mono_method.h" + +#include "gd_mono_class.h" +#include "gd_mono_marshal.h" + +void GDMonoMethod::_update_signature() { + // Apparently MonoMethodSignature needs not to be freed. + // mono_method_signature caches the result, we don't need to cache it ourselves. + + MonoMethodSignature *method_sig = mono_method_signature(mono_method); + _update_signature(method_sig); +} + +void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) { + is_instance = mono_signature_is_instance(p_method_sig); + params_count = mono_signature_get_param_count(p_method_sig); + + MonoType *ret_type = mono_signature_get_return_type(p_method_sig); + if (ret_type) { + return_type.type_encoding = mono_type_get_type(ret_type); + + if (return_type.type_encoding != MONO_TYPE_VOID) { + MonoClass *ret_type_class = mono_class_from_mono_type(ret_type); + return_type.type_class = GDMono::get_singleton()->get_class(ret_type_class); + } + } + + void *iter = NULL; + MonoType *param_raw_type; + while ((param_raw_type = mono_signature_get_params(p_method_sig, &iter)) != NULL) { + ManagedType param_type; + + param_type.type_encoding = mono_type_get_type(param_raw_type); + + if (param_type.type_encoding != MONO_TYPE_VOID) { + MonoClass *param_type_class = mono_class_from_mono_type(param_raw_type); + param_type.type_class = GDMono::get_singleton()->get_class(param_type_class); + } + + param_types.push_back(param_type); + } +} + +void *GDMonoMethod::get_thunk() { + return mono_method_get_unmanaged_thunk(mono_method); +} + +MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc) { + if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) { + MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count()); + + for (int i = 0; i < params_count; i++) { + MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object(p_params[i], param_types[i]); + mono_array_set(params, MonoObject *, i, boxed_param); + } + + MonoObject *exc = NULL; + MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, &exc); + + if (exc) { + if (r_exc) { + *r_exc = exc; + } else { + ERR_PRINT(GDMonoUtils::get_exception_name_and_message(exc).utf8()); + mono_print_unhandled_exception(exc); + } + } + + return ret; + } else { + MonoObject *exc = NULL; + mono_runtime_invoke(mono_method, p_object, NULL, &exc); + + if (exc) { + if (r_exc) { + *r_exc = exc; + } else { + ERR_PRINT(GDMonoUtils::get_exception_name_and_message(exc).utf8()); + mono_print_unhandled_exception(exc); + } + } + + return NULL; + } +} + +MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoObject **r_exc) { + ERR_FAIL_COND_V(get_parameters_count() > 0, NULL); + return invoke_raw(p_object, NULL, r_exc); +} + +MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc) { + MonoObject *exc = NULL; + MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, &exc); + + if (exc) { + if (r_exc) { + *r_exc = exc; + } else { + ERR_PRINT(GDMonoUtils::get_exception_name_and_message(exc).utf8()); + mono_print_unhandled_exception(exc); + } + } + + return ret; +} + +bool GDMonoMethod::has_attribute(GDMonoClass *p_attr_class) { + ERR_FAIL_NULL_V(p_attr_class, false); + + if (!attrs_fetched) + fetch_attributes(); + + if (!attributes) + return false; + + return mono_custom_attrs_has_attr(attributes, p_attr_class->get_raw()); +} + +MonoObject *GDMonoMethod::get_attribute(GDMonoClass *p_attr_class) { + ERR_FAIL_NULL_V(p_attr_class, NULL); + + if (!attrs_fetched) + fetch_attributes(); + + if (!attributes) + return NULL; + + return mono_custom_attrs_get_attr(attributes, p_attr_class->get_raw()); +} + +void GDMonoMethod::fetch_attributes() { + ERR_FAIL_COND(attributes != NULL); + attributes = mono_custom_attrs_from_method(mono_method); + attrs_fetched = true; +} + +String GDMonoMethod::get_full_name(bool p_signature) const { + char *res = mono_method_full_name(mono_method, p_signature); + String full_name(res); + mono_free(res); + return full_name; +} + +String GDMonoMethod::get_full_name_no_class() const { + String res; + + MonoMethodSignature *method_sig = mono_method_signature(mono_method); + + char *ret_str = mono_type_full_name(mono_signature_get_return_type(method_sig)); + res += ret_str; + mono_free(ret_str); + + res += " "; + res += name; + res += "("; + + char *sig_desc = mono_signature_get_desc(method_sig, true); + res += sig_desc; + mono_free(sig_desc); + + res += ")"; + + return res; +} + +String GDMonoMethod::get_ret_type_full_name() const { + MonoMethodSignature *method_sig = mono_method_signature(mono_method); + char *ret_str = mono_type_full_name(mono_signature_get_return_type(method_sig)); + String res = ret_str; + mono_free(ret_str); + return res; +} + +String GDMonoMethod::get_signature_desc(bool p_namespaces) const { + MonoMethodSignature *method_sig = mono_method_signature(mono_method); + char *sig_desc = mono_signature_get_desc(method_sig, p_namespaces); + String res = sig_desc; + mono_free(sig_desc); + return res; +} + +GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) { + name = p_name; + + mono_method = p_method; + + attrs_fetched = false; + attributes = NULL; + + _update_signature(); +} + +GDMonoMethod::~GDMonoMethod() { + if (attributes) { + mono_custom_attrs_free(attributes); + } +} diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h new file mode 100644 index 0000000000..ea4bc8e707 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* gd_mono_method.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GD_MONO_METHOD_H +#define GD_MONO_METHOD_H + +#include "gd_mono.h" +#include "gd_mono_header.h" + +class GDMonoMethod { + + StringName name; + + bool is_instance; + int params_count; + ManagedType return_type; + Vector<ManagedType> param_types; + + bool attrs_fetched; + MonoCustomAttrInfo *attributes; + + void _update_signature(); + void _update_signature(MonoMethodSignature *p_method_sig); + + friend class GDMonoClass; + + MonoMethod *mono_method; + +public: + _FORCE_INLINE_ StringName get_name() { return name; } + + _FORCE_INLINE_ bool is_static() { return !is_instance; } + _FORCE_INLINE_ int get_parameters_count() { return params_count; } + _FORCE_INLINE_ ManagedType get_return_type() { return return_type; } + + void *get_thunk(); + + MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc = NULL); + MonoObject *invoke(MonoObject *p_object, MonoObject **r_exc = NULL); + MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL); + + bool has_attribute(GDMonoClass *p_attr_class); + MonoObject *get_attribute(GDMonoClass *p_attr_class); + void fetch_attributes(); + + String get_full_name(bool p_signature = false) const; + String get_full_name_no_class() const; + String get_ret_type_full_name() const; + String get_signature_desc(bool p_namespaces = false) const; + + GDMonoMethod(StringName p_name, MonoMethod *p_method); + ~GDMonoMethod(); +}; + +#endif // GD_MONO_METHOD_H diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp new file mode 100644 index 0000000000..53e45002c4 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -0,0 +1,367 @@ +/*************************************************************************/ +/* gd_mono_utils.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "gd_mono_utils.h" + +#include "os/dir_access.h" +#include "project_settings.h" +#include "reference.h" + +#include "../csharp_script.h" +#include "gd_mono.h" +#include "gd_mono_class.h" +#include "gd_mono_marshal.h" + +namespace GDMonoUtils { + +MonoCache mono_cache; + +#define CACHE_AND_CHECK(m_var, m_val) \ + { \ + m_var = m_val; \ + if (!m_var) ERR_PRINT("Mono Cache: Member " #m_var " is null. This is really bad!"); \ + } + +#define CACHE_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.class_##m_class, m_val) +#define CACHE_NS_CLASS_AND_CHECK(m_ns, m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.class_##m_ns##_##m_class, m_val) +#define CACHE_RAW_MONO_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.rawclass_##m_class, m_val) +#define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.field_##m_class##_##m_field, m_val) +#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method, m_val) + +void MonoCache::clear_members() { + + class_MonoObject = NULL; + class_bool = NULL; + class_int8_t = NULL; + class_int16_t = NULL; + class_int32_t = NULL; + class_int64_t = NULL; + class_uint8_t = NULL; + class_uint16_t = NULL; + class_uint32_t = NULL; + class_uint64_t = NULL; + class_float = NULL; + class_double = NULL; + class_String = NULL; + class_IntPtr = NULL; + + rawclass_Dictionary = NULL; + + class_Vector2 = NULL; + class_Rect2 = NULL; + class_Transform2D = NULL; + class_Vector3 = NULL; + class_Basis = NULL; + class_Quat = NULL; + class_Transform = NULL; + class_Rect3 = NULL; + class_Color = NULL; + class_Plane = NULL; + class_NodePath = NULL; + class_RID = NULL; + class_GodotObject = NULL; + class_GodotReference = NULL; + class_Node = NULL; + class_Control = NULL; + class_Spatial = NULL; + class_WeakRef = NULL; + class_MarshalUtils = NULL; + + class_ExportAttribute = NULL; + field_ExportAttribute_hint = NULL; + field_ExportAttribute_hint_string = NULL; + class_ToolAttribute = NULL; + class_RemoteAttribute = NULL; + class_SyncAttribute = NULL; + class_MasterAttribute = NULL; + class_SlaveAttribute = NULL; + class_GodotMethodAttribute = NULL; + field_GodotMethodAttribute_methodName = NULL; + + field_GodotObject_ptr = NULL; + field_NodePath_ptr = NULL; + field_Image_ptr = NULL; + field_RID_ptr = NULL; + + methodthunk_MarshalUtils_DictionaryToArrays = NULL; + methodthunk_MarshalUtils_ArraysToDictionary = NULL; + methodthunk_SignalAwaiter_SignalCallback = NULL; + methodthunk_SignalAwaiter_FailureCallback = NULL; + methodthunk_GodotTaskScheduler_Activate = NULL; + + task_scheduler_handle = Ref<MonoGCHandle>(); +} + +#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) + +void update_corlib_cache() { + + CACHE_CLASS_AND_CHECK(MonoObject, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_object_class())); + CACHE_CLASS_AND_CHECK(bool, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_boolean_class())); + CACHE_CLASS_AND_CHECK(int8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_sbyte_class())); + CACHE_CLASS_AND_CHECK(int16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int16_class())); + CACHE_CLASS_AND_CHECK(int32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int32_class())); + CACHE_CLASS_AND_CHECK(int64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int64_class())); + CACHE_CLASS_AND_CHECK(uint8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_byte_class())); + CACHE_CLASS_AND_CHECK(uint16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint16_class())); + CACHE_CLASS_AND_CHECK(uint32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint32_class())); + CACHE_CLASS_AND_CHECK(uint64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint64_class())); + CACHE_CLASS_AND_CHECK(float, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_single_class())); + CACHE_CLASS_AND_CHECK(double, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_double_class())); + CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class())); + CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class())); +} + +void update_godot_api_cache() { + + CACHE_CLASS_AND_CHECK(Vector2, GODOT_API_CLASS(Vector2)); + CACHE_CLASS_AND_CHECK(Rect2, GODOT_API_CLASS(Rect2)); + CACHE_CLASS_AND_CHECK(Transform2D, GODOT_API_CLASS(Transform2D)); + CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3)); + CACHE_CLASS_AND_CHECK(Basis, GODOT_API_CLASS(Basis)); + CACHE_CLASS_AND_CHECK(Quat, GODOT_API_CLASS(Quat)); + CACHE_CLASS_AND_CHECK(Transform, GODOT_API_CLASS(Transform)); + CACHE_CLASS_AND_CHECK(Rect3, GODOT_API_CLASS(Rect3)); + CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color)); + CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane)); + CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath)); + CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(NodePath)); + CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object)); + CACHE_CLASS_AND_CHECK(GodotReference, GODOT_API_CLASS(Reference)); + CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node)); + CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control)); + CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial)); + CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef)); + CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils)); + + // Attributes + CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute)); + CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint")); + CACHE_FIELD_AND_CHECK(ExportAttribute, hint_string, CACHED_CLASS(ExportAttribute)->get_field("hint_string")); + CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute)); + CACHE_CLASS_AND_CHECK(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute)); + CACHE_CLASS_AND_CHECK(SyncAttribute, GODOT_API_CLASS(SyncAttribute)); + CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute)); + CACHE_CLASS_AND_CHECK(SlaveAttribute, GODOT_API_CLASS(SlaveAttribute)); + CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute)); + CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName")); + + CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD)); + CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD)); + CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD)); + + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryToArrays, (MarshalUtils_DictToArrays)CACHED_CLASS(MarshalUtils)->get_method("DictionaryToArrays", 3)->get_thunk()); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArraysToDictionary, (MarshalUtils_ArraysToDict)CACHED_CLASS(MarshalUtils)->get_method("ArraysToDictionary", 2)->get_thunk()); + CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1)->get_thunk()); + CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0)->get_thunk()); + CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)->get_thunk()); + + { + /* + * TODO Right now we only support Dictionary<object, object>. + * It would be great if we could support other key/value types + * without forcing the user to copy the entries. + */ + GDMonoMethod *method_get_dict_type = CACHED_CLASS(MarshalUtils)->get_method("GetDictionaryType", 0); + ERR_FAIL_NULL(method_get_dict_type); + MonoReflectionType *dict_refl_type = (MonoReflectionType *)method_get_dict_type->invoke(NULL); + ERR_FAIL_NULL(dict_refl_type); + MonoType *dict_type = mono_reflection_type_get_type(dict_refl_type); + ERR_FAIL_NULL(dict_type); + + CACHE_RAW_MONO_CLASS_AND_CHECK(Dictionary, mono_class_from_mono_type(dict_type)); + } + + MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_raw()); + mono_runtime_object_init(task_scheduler); + mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler); +} + +void clear_cache() { + mono_cache.cleanup(); + mono_cache.clear_members(); +} + +MonoObject *unmanaged_get_managed(Object *unmanaged) { + if (unmanaged) { + if (unmanaged->get_script_instance()) { + CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance()); + + if (cs_instance) { + return cs_instance->get_mono_object(); + } + } + + // Only called if the owner does not have a CSharpInstance + void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + + if (data) { + return ((Map<Object *, Ref<MonoGCHandle> >::Element *)data)->value()->get_target(); + } + } + + return NULL; +} + +void set_main_thread(MonoThread *p_thread) { + mono_thread_set_main(p_thread); +} + +void attach_current_thread() { + ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized()); + MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN); + ERR_FAIL_NULL(mono_thread); +} + +void detach_current_thread() { + ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized()); + MonoThread *mono_thread = mono_thread_current(); + ERR_FAIL_NULL(mono_thread); + mono_thread_detach(mono_thread); +} + +MonoThread *get_current_thread() { + return mono_thread_current(); +} + +GDMonoClass *get_object_class(MonoObject *p_object) { + return GDMono::get_singleton()->get_class(mono_object_get_class(p_object)); +} + +GDMonoClass *type_get_proxy_class(const StringName &p_type) { + String class_name = p_type; + + if (class_name[0] == '_') + class_name = class_name.substr(1, class_name.length()); + + GDMonoClass *klass = GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name); + +#ifdef TOOLS_ENABLED + if (!klass) { + return GDMono::get_singleton()->get_editor_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name); + } +#endif + + return klass; +} + +GDMonoClass *get_class_native_base(GDMonoClass *p_class) { + GDMonoClass *klass = p_class; + + do { + const GDMonoAssembly *assembly = klass->get_assembly(); + if (assembly == GDMono::get_singleton()->get_api_assembly()) + return klass; +#ifdef TOOLS_ENABLED + if (assembly == GDMono::get_singleton()->get_editor_api_assembly()) + return klass; +#endif + } while ((klass = klass->get_parent_class()) != NULL); + + return NULL; +} + +MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) { + String object_type = p_object->get_class_name(); + + if (object_type[0] == '_') + object_type = object_type.substr(1, object_type.length()); + + if (!ClassDB::is_parent_class(object_type, p_native)) { + ERR_EXPLAIN("Type inherits from native type '" + p_native + "', so it can't be instanced in object of type: '" + p_object->get_class() + "'"); + ERR_FAIL_V(NULL); + } + + MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_raw()); + ERR_FAIL_NULL_V(mono_object, NULL); + + CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object); + + // Construct + mono_runtime_object_init(mono_object); + + return mono_object; +} + +MonoObject *create_managed_from(const NodePath &p_from) { + MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(NodePath)); + ERR_FAIL_NULL_V(mono_object, NULL); + + // Construct + mono_runtime_object_init(mono_object); + + CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from))); + + return mono_object; +} + +MonoObject *create_managed_from(const RID &p_from) { + MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(RID)); + ERR_FAIL_NULL_V(mono_object, NULL); + + // Construct + mono_runtime_object_init(mono_object); + + CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from))); + + return mono_object; +} + +MonoDomain *create_domain(const String &p_friendly_name) { + MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), NULL); + + if (domain) { + // Workaround to avoid this exception: + // System.Configuration.ConfigurationErrorsException: Error Initializing the configuration system. + // ---> System.ArgumentException: The 'ExeConfigFilename' argument cannot be null. + mono_domain_set_config(domain, ".", ""); + } + + return domain; +} + +String get_exception_name_and_message(MonoObject *p_ex) { + String res; + + MonoClass *klass = mono_object_get_class(p_ex); + MonoType *type = mono_class_get_type(klass); + + char *full_name = mono_type_full_name(type); + res += full_name; + mono_free(full_name); + + res += ": "; + + MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); + MonoString *msg = (MonoString *)mono_property_get_value(prop, p_ex, NULL, NULL); + res += GDMonoMarshal::mono_string_to_godot(msg); + + return res; +} +} diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h new file mode 100644 index 0000000000..e3af57e78a --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -0,0 +1,182 @@ +/*************************************************************************/ +/* gd_mono_utils.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 GD_MONOUTILS_H +#define GD_MONOUTILS_H + +#include <mono/metadata/threads.h> + +#include "../mono_gc_handle.h" +#include "gd_mono_header.h" + +#include "object.h" +#include "reference.h" + +namespace GDMonoUtils { + +typedef MonoObject *(*MarshalUtils_DictToArrays)(MonoObject *, MonoArray **, MonoArray **, MonoObject **); +typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoObject **); +typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray **, MonoObject **); +typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **); +typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **); + +struct MonoCache { + // Format for cached classes in the Godot namespace: class_<Class> + // Macro: CACHED_CLASS(<Class>) + + // Format for cached classes in a different namespace: class_<Namespace>_<Class> + // Macro: CACHED_NS_CLASS(<Namespace>, <Class>) + + // ----------------------------------------------- + // corlib classes + + // Let's use the no-namespace format for these too + GDMonoClass *class_MonoObject; + GDMonoClass *class_bool; + GDMonoClass *class_int8_t; + GDMonoClass *class_int16_t; + GDMonoClass *class_int32_t; + GDMonoClass *class_int64_t; + GDMonoClass *class_uint8_t; + GDMonoClass *class_uint16_t; + GDMonoClass *class_uint32_t; + GDMonoClass *class_uint64_t; + GDMonoClass *class_float; + GDMonoClass *class_double; + GDMonoClass *class_String; + GDMonoClass *class_IntPtr; + + MonoClass *rawclass_Dictionary; + // ----------------------------------------------- + + GDMonoClass *class_Vector2; + GDMonoClass *class_Rect2; + GDMonoClass *class_Transform2D; + GDMonoClass *class_Vector3; + GDMonoClass *class_Basis; + GDMonoClass *class_Quat; + GDMonoClass *class_Transform; + GDMonoClass *class_Rect3; + GDMonoClass *class_Color; + GDMonoClass *class_Plane; + GDMonoClass *class_NodePath; + GDMonoClass *class_RID; + GDMonoClass *class_GodotObject; + GDMonoClass *class_GodotReference; + GDMonoClass *class_Node; + GDMonoClass *class_Control; + GDMonoClass *class_Spatial; + GDMonoClass *class_WeakRef; + GDMonoClass *class_MarshalUtils; + + GDMonoClass *class_ExportAttribute; + GDMonoField *field_ExportAttribute_hint; + GDMonoField *field_ExportAttribute_hint_string; + GDMonoClass *class_ToolAttribute; + GDMonoClass *class_RemoteAttribute; + GDMonoClass *class_SyncAttribute; + GDMonoClass *class_MasterAttribute; + GDMonoClass *class_SlaveAttribute; + GDMonoClass *class_GodotMethodAttribute; + GDMonoField *field_GodotMethodAttribute_methodName; + + GDMonoField *field_GodotObject_ptr; + GDMonoField *field_NodePath_ptr; + GDMonoField *field_Image_ptr; + GDMonoField *field_RID_ptr; + + MarshalUtils_DictToArrays methodthunk_MarshalUtils_DictionaryToArrays; + MarshalUtils_ArraysToDict methodthunk_MarshalUtils_ArraysToDictionary; + SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback; + SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback; + GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate; + + Ref<MonoGCHandle> task_scheduler_handle; + + void clear_members(); + void cleanup() {} + + MonoCache() { + clear_members(); + } +}; + +extern MonoCache mono_cache; + +void update_corlib_cache(); +void update_godot_api_cache(); +void clear_cache(); + +_FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) { + p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2); +} + +/** + * If the object has a csharp script, returns the target of the gchandle stored in the script instance + * Otherwise returns a newly constructed MonoObject* which is attached to the object + * Returns NULL on error + */ +MonoObject *unmanaged_get_managed(Object *unmanaged); + +void set_main_thread(MonoThread *p_thread); +void attach_current_thread(); +void detach_current_thread(); +MonoThread *get_current_thread(); + +GDMonoClass *get_object_class(MonoObject *p_object); +GDMonoClass *type_get_proxy_class(const StringName &p_type); +GDMonoClass *get_class_native_base(GDMonoClass *p_class); + +MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object); + +MonoObject *create_managed_from(const NodePath &p_from); +MonoObject *create_managed_from(const RID &p_from); + +MonoDomain *create_domain(const String &p_friendly_name); + +String get_exception_name_and_message(MonoObject *p_ex); + +} // GDMonoUtils + +#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL))) + +#define CACHED_CLASS(m_class) (GDMonoUtils::mono_cache.class_##m_class) +#define CACHED_CLASS_RAW(m_class) (GDMonoUtils::mono_cache.class_##m_class->get_raw()) +#define CACHED_NS_CLASS(m_ns, m_class) (GDMonoUtils::mono_cache.class_##m_ns##_##m_class) +#define CACHED_RAW_MONO_CLASS(m_class) (GDMonoUtils::mono_cache.rawclass_##m_class) +#define CACHED_FIELD(m_class, m_field) (GDMonoUtils::mono_cache.field_##m_class##_##m_field) +#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method) + +#ifdef REAL_T_IS_DOUBLE +#define REAL_T_MONOCLASS CACHED_CLASS_RAW(double) +#else +#define REAL_T_MONOCLASS CACHED_CLASS_RAW(float) +#endif + +#endif // GD_MONOUTILS_H diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/mono_reg_utils.py new file mode 100644 index 0000000000..8ddddb3a24 --- /dev/null +++ b/modules/mono/mono_reg_utils.py @@ -0,0 +1,114 @@ +import os +import platform + +from compat import decode_utf8 + +if os.name == 'nt': + import sys + if sys.version_info < (3,): + import _winreg as winreg + else: + import winreg + + +def _reg_open_key(key, subkey): + try: + return winreg.OpenKey(key, subkey) + except (WindowsError, OSError): + if platform.architecture()[0] == '32bit': + bitness_sam = winreg.KEY_WOW64_64KEY + else: + bitness_sam = winreg.KEY_WOW64_32KEY + return winreg.OpenKey(key, subkey, 0, winreg.KEY_READ | bitness_sam) + + +def _reg_open_key_bits(key, subkey, bits): + sam = winreg.KEY_READ + + if platform.architecture()[0] == '32bit': + if bits == '64': + # Force 32bit process to search in 64bit registry + sam |= winreg.KEY_WOW64_64KEY + else: + if bits == '32': + # Force 64bit process to search in 32bit registry + sam |= winreg.KEY_WOW64_32KEY + + return winreg.OpenKey(key, subkey, 0, sam) + + +def _find_mono_in_reg(subkey, bits): + try: + with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey: + value, regtype = winreg.QueryValueEx(hKey, 'SdkInstallRoot') + return value + except (WindowsError, OSError): + return None + + +def _find_mono_in_reg_old(subkey, bits): + try: + with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey: + default_clr, regtype = winreg.QueryValueEx(hKey, 'DefaultCLR') + if default_clr: + return _find_mono_in_reg(subkey + '\\' + default_clr, bits) + return None + except (WindowsError, EnvironmentError): + return None + + +def find_mono_root_dir(bits): + root_dir = _find_mono_in_reg(r'SOFTWARE\Mono', bits) + if root_dir is not None: + return root_dir + root_dir = _find_mono_in_reg_old(r'SOFTWARE\Novell\Mono', bits) + if root_dir is not None: + return root_dir + return '' + + +def find_msbuild_tools_path_reg(): + import subprocess + + vswhere = os.getenv('PROGRAMFILES(X86)') + if not vswhere: + vswhere = os.getenv('PROGRAMFILES') + vswhere += r'\Microsoft Visual Studio\Installer\vswhere.exe' + + vswhere_args = ['-latest', '-requires', 'Microsoft.Component.MSBuild'] + + try: + lines = subprocess.check_output([vswhere] + vswhere_args).splitlines() + + for line in lines: + parts = decode_utf8(line).split(':', 1) + + if len(parts) < 2 or parts[0] != 'installationPath': + continue + + val = parts[1].strip() + + if not val: + raise ValueError('Value of `installationPath` entry is empty') + + return os.path.join(val, "MSBuild\\15.0\\Bin") + + raise ValueError('Cannot find `installationPath` entry') + except ValueError as e: + print('Error reading output from vswhere: ' + e.message) + except WindowsError: + pass # Fine, vswhere not found + except (subprocess.CalledProcessError, OSError): + pass + + # Try to find 14.0 in the Registry + + try: + subkey = r'SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0' + with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey: + value, regtype = winreg.QueryValueEx(hKey, 'MSBuildToolsPath') + return value + except (WindowsError, OSError): + return '' + + return '' diff --git a/modules/mono/register_types.cpp b/modules/mono/register_types.cpp new file mode 100644 index 0000000000..2656de5b14 --- /dev/null +++ b/modules/mono/register_types.cpp @@ -0,0 +1,72 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "register_types.h" + +#include "project_settings.h" + +#include "csharp_script.h" + +CSharpLanguage *script_language_cs = NULL; +ResourceFormatLoaderCSharpScript *resource_loader_cs = NULL; +ResourceFormatSaverCSharpScript *resource_saver_cs = NULL; + +_GodotSharp *_godotsharp = NULL; + +void register_mono_types() { + ClassDB::register_class<CSharpScript>(); + + _godotsharp = memnew(_GodotSharp); + + ClassDB::register_class<_GodotSharp>(); + ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("GodotSharp", _GodotSharp::get_singleton())); + + script_language_cs = memnew(CSharpLanguage); + script_language_cs->set_language_index(ScriptServer::get_language_count()); + ScriptServer::register_language(script_language_cs); + + resource_loader_cs = memnew(ResourceFormatLoaderCSharpScript); + ResourceLoader::add_resource_format_loader(resource_loader_cs); + resource_saver_cs = memnew(ResourceFormatSaverCSharpScript); + ResourceSaver::add_resource_format_saver(resource_saver_cs); +} + +void unregister_mono_types() { + ScriptServer::unregister_language(script_language_cs); + + if (script_language_cs) + memdelete(script_language_cs); + if (resource_loader_cs) + memdelete(resource_loader_cs); + if (resource_saver_cs) + memdelete(resource_saver_cs); + + if (_godotsharp) + memdelete(_godotsharp); +} diff --git a/modules/mono/register_types.h b/modules/mono/register_types.h new file mode 100644 index 0000000000..6cf706b944 --- /dev/null +++ b/modules/mono/register_types.h @@ -0,0 +1,31 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ +void register_mono_types(); +void unregister_mono_types(); diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp new file mode 100644 index 0000000000..7e99df29a1 --- /dev/null +++ b/modules/mono/signal_awaiter_utils.cpp @@ -0,0 +1,144 @@ +/*************************************************************************/ +/* signal_awaiter_utils.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "signal_awaiter_utils.h" + +#include "csharp_script.h" +#include "mono_gd/gd_mono_class.h" +#include "mono_gd/gd_mono_marshal.h" +#include "mono_gd/gd_mono_utils.h" + +namespace SignalAwaiterUtils { + +Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p_target, MonoObject *p_awaiter) { + + ERR_FAIL_NULL_V(p_source, ERR_INVALID_DATA); + ERR_FAIL_NULL_V(p_target, ERR_INVALID_DATA); + + uint32_t awaiter_handle = MonoGCHandle::make_strong_handle(p_awaiter); + Ref<SignalAwaiterHandle> sa_con = memnew(SignalAwaiterHandle(awaiter_handle)); +#ifdef DEBUG_ENABLED + sa_con->set_connection_target(p_target); +#endif + + Vector<Variant> binds; + binds.push_back(sa_con); + + Error err = p_source->connect(p_signal, sa_con.ptr(), + CSharpLanguage::get_singleton()->get_string_names()._signal_callback, + binds, Object::CONNECT_ONESHOT); + + if (err != OK) { + // Set it as completed to prevent it from calling the failure callback when released. + // The awaiter will be aware of the failure by checking the returned error. + sa_con->set_completed(true); + } + + return err; +} +} + +Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error) { + +#ifdef DEBUG_ENABLED + if (conn_target_id && !ObjectDB::get_instance(conn_target_id)) { + ERR_EXPLAIN("Resumed after await, but class instance is gone"); + ERR_FAIL_V(Variant()); + } +#endif + + if (p_argcount < 1) { + r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + return Variant(); + } + + Ref<SignalAwaiterHandle> self = *p_args[p_argcount - 1]; + + if (self.is_null()) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = p_argcount - 1; + r_error.expected = Variant::OBJECT; + return Variant(); + } + + set_completed(true); + + int signal_argc = p_argcount - 1; + MonoArray *signal_args = mono_array_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(MonoObject), signal_argc); + + for (int i = 0; i < signal_argc; i++) { + MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_args[i]); + mono_array_set(signal_args, MonoObject *, i, boxed); + } + + GDMonoUtils::SignalAwaiter_SignalCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback); + + MonoObject *ex = NULL; + thunk(get_target(), &signal_args, &ex); + + if (ex) { + mono_print_unhandled_exception(ex); + ERR_FAIL_V(Variant()); + } + + return Variant(); +} + +void SignalAwaiterHandle::_bind_methods() { + + ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &SignalAwaiterHandle::_signal_callback, MethodInfo("_signal_callback")); +} + +SignalAwaiterHandle::SignalAwaiterHandle(uint32_t p_managed_handle) + : MonoGCHandle(p_managed_handle) { + +#ifdef DEBUG_ENABLED + conn_target_id = 0; +#endif +} + +SignalAwaiterHandle::~SignalAwaiterHandle() { + + if (!completed) { + GDMonoUtils::SignalAwaiter_FailureCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback); + + MonoObject *awaiter = get_target(); + + if (awaiter) { + MonoObject *ex = NULL; + thunk(awaiter, &ex); + + if (ex) { + mono_print_unhandled_exception(ex); + ERR_FAIL_V(); + } + } + } +} diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h new file mode 100644 index 0000000000..0d615b5826 --- /dev/null +++ b/modules/mono/signal_awaiter_utils.h @@ -0,0 +1,70 @@ +/*************************************************************************/ +/* signal_awaiter_utils.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 SIGNAL_AWAITER_UTILS_H +#define SIGNAL_AWAITER_UTILS_H + +#include "mono_gc_handle.h" +#include "reference.h" + +namespace SignalAwaiterUtils { + +Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p_target, MonoObject *p_awaiter); +} + +class SignalAwaiterHandle : public MonoGCHandle { + + GDCLASS(SignalAwaiterHandle, MonoGCHandle) + + bool completed; + +#ifdef DEBUG_ENABLED + ObjectID conn_target_id; +#endif + + Variant _signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error); + +protected: + static void _bind_methods(); + +public: + _FORCE_INLINE_ bool is_completed() { return completed; } + _FORCE_INLINE_ void set_completed(bool p_completed) { completed = p_completed; } + +#ifdef DEBUG_ENABLED + _FORCE_INLINE_ void set_connection_target(Object *p_target) { + conn_target_id = p_target->get_instance_id(); + } +#endif + + SignalAwaiterHandle(uint32_t p_managed_handle); + ~SignalAwaiterHandle(); +}; + +#endif // SIGNAL_AWAITER_UTILS_H diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp new file mode 100644 index 0000000000..2e90b3b716 --- /dev/null +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -0,0 +1,228 @@ +/*************************************************************************/ +/* mono_reg_utils.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "mono_reg_utils.h" + +#ifdef WINDOWS_ENABLED + +#include "os/os.h" + +// Here, after os/os.h +#include <windows.h> + +namespace MonoRegUtils { + +template <int> +REGSAM bitness_sam_impl(); + +template <> +REGSAM bitness_sam_impl<4>() { + return KEY_WOW64_64KEY; +} + +template <> +REGSAM bitness_sam_impl<8>() { + return KEY_WOW64_32KEY; +} + +REGSAM _get_bitness_sam() { + return bitness_sam_impl<sizeof(size_t)>(); +} + +LONG _RegOpenKey(HKEY hKey, LPCWSTR lpSubKey, PHKEY phkResult) { + + LONG res = RegOpenKeyExW(hKey, lpSubKey, 0, KEY_READ, phkResult); + + if (res != ERROR_SUCCESS) + res = RegOpenKeyExW(hKey, lpSubKey, 0, KEY_READ | _get_bitness_sam(), phkResult); + + return res; +} + +LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value) { + + Vector<WCHAR> buffer; + buffer.resize(512); + DWORD dwBufferSize = buffer.size(); + + LONG res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, NULL, (LPBYTE)buffer.ptr(), &dwBufferSize); + + if (res == ERROR_MORE_DATA) { + // dwBufferSize now contains the actual size + Vector<WCHAR> buffer; + buffer.resize(dwBufferSize); + res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, NULL, (LPBYTE)buffer.ptr(), &dwBufferSize); + } + + if (res == ERROR_SUCCESS) { + r_value = String(buffer.ptr(), buffer.size()); + } else { + r_value = String(); + } + + return res; +} + +LONG _find_mono_in_reg(const String &p_subkey, MonoRegInfo &r_info, bool p_old_reg = false) { + + HKEY hKey; + LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, p_subkey.c_str(), &hKey); + + if (res != ERROR_SUCCESS) + goto cleanup; + + if (!p_old_reg) { + res = _RegKeyQueryString(hKey, "Version", r_info.version); + if (res != ERROR_SUCCESS) + goto cleanup; + } + + res = _RegKeyQueryString(hKey, "SdkInstallRoot", r_info.install_root_dir); + if (res != ERROR_SUCCESS) + goto cleanup; + + res = _RegKeyQueryString(hKey, "FrameworkAssemblyDirectory", r_info.assembly_dir); + if (res != ERROR_SUCCESS) + goto cleanup; + + res = _RegKeyQueryString(hKey, "MonoConfigDir", r_info.config_dir); + if (res != ERROR_SUCCESS) + goto cleanup; + + if (r_info.install_root_dir.ends_with("\\")) + r_info.bin_dir = r_info.install_root_dir + "bin"; + else + r_info.bin_dir = r_info.install_root_dir + "\\bin"; + +cleanup: + RegCloseKey(hKey); + return res; +} + +LONG _find_mono_in_reg_old(const String &p_subkey, MonoRegInfo &r_info) { + + String default_clr; + + HKEY hKey; + LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, p_subkey.c_str(), &hKey); + + if (res != ERROR_SUCCESS) + goto cleanup; + + res = _RegKeyQueryString(hKey, "DefaultCLR", default_clr); + + if (res == ERROR_SUCCESS && default_clr.length()) { + r_info.version = default_clr; + res = _find_mono_in_reg(p_subkey + "\\" + default_clr, r_info, true); + } + +cleanup: + RegCloseKey(hKey); + return res; +} + +MonoRegInfo find_mono() { + + MonoRegInfo info; + + if (_find_mono_in_reg("Software\\Mono", info) == ERROR_SUCCESS) + return info; + + if (_find_mono_in_reg_old("Software\\Novell\\Mono", info) == ERROR_SUCCESS) + return info; + + ERR_PRINT("Cannot find mono in the registry"); + + return MonoRegInfo(); +} + +String find_msbuild_tools_path() { + + String msbuild_tools_path; + + // Try to find 15.0 with vswhere + + String vswhere_path = OS::get_singleton()->get_environment(sizeof(size_t) == 8 ? "ProgramFiles(x86)" : "ProgramFiles"); + vswhere_path += "\\Microsoft Visual Studio\\Installer\\vswhere.exe"; + + List<String> vswhere_args; + vswhere_args.push_back("-latest"); + vswhere_args.push_back("-requires"); + vswhere_args.push_back("Microsoft.Component.MSBuild"); + + String output; + int exit_code; + OS::get_singleton()->execute(vswhere_path, vswhere_args, true, NULL, &output, &exit_code); + + if (exit_code == 0) { + Vector<String> lines = output.split("\n"); + + for (int i = 0; i < lines.size(); i++) { + const String &line = lines[i]; + int sep_idx = line.find(":"); + + if (sep_idx > 0) { + String key = line.substr(0, sep_idx); // No need to trim + + if (key == "installationPath") { + String val = line.substr(sep_idx + 1, line.length()).strip_edges(); + + ERR_BREAK(val.empty()); + + if (!val.ends_with("\\")) { + val += "\\"; + } + + return val + "MSBuild\\15.0\\Bin"; + } + } + } + } + + // Try to find 14.0 in the Registry + + HKEY hKey; + LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\14.0", &hKey); + + if (res != ERROR_SUCCESS) + goto cleanup; + + res = _RegKeyQueryString(hKey, "MSBuildToolsPath", msbuild_tools_path); + + if (res != ERROR_SUCCESS) + goto cleanup; + +cleanup: + RegCloseKey(hKey); + + return msbuild_tools_path; +} +} // namespace MonoRegUtils + +#endif WINDOWS_ENABLED diff --git a/modules/mono/utils/mono_reg_utils.h b/modules/mono/utils/mono_reg_utils.h new file mode 100644 index 0000000000..4cc4965acb --- /dev/null +++ b/modules/mono/utils/mono_reg_utils.h @@ -0,0 +1,54 @@ +/*************************************************************************/ +/* mono_reg_utils.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 MONO_REG_UTILS_H +#define MONO_REG_UTILS_H + +#ifdef WINDOWS_ENABLED + +#include "ustring.h" + +struct MonoRegInfo { + + String version; + String install_root_dir; + String assembly_dir; + String config_dir; + String bin_dir; +}; + +namespace MonoRegUtils { + +MonoRegInfo find_mono(); +String find_msbuild_tools_path(); +} // MonoRegUtils + +#endif // WINDOWS_ENABLED + +#endif // MONO_REG_UTILS_H diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp new file mode 100644 index 0000000000..105c2c981e --- /dev/null +++ b/modules/mono/utils/path_utils.cpp @@ -0,0 +1,111 @@ +/*************************************************************************/ +/* path_utils.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "path_utils.h" + +#include "os/dir_access.h" +#include "os/file_access.h" +#include "os/os.h" +#include "project_settings.h" + +#ifdef WINDOWS_ENABLED +#define ENV_PATH_SEP ";" +#else +#define ENV_PATH_SEP ":" +#include <limits.h> +#endif + +#include <stdlib.h> + +String path_which(const String &p_name) { + +#ifdef WINDOWS_ENABLED + Vector<String> exts = OS::get_singleton()->get_environment("PATHEXT").split(ENV_PATH_SEP, false); +#endif + Vector<String> env_path = OS::get_singleton()->get_environment("PATH").split(ENV_PATH_SEP, false); + + if (env_path.empty()) + return String(); + + for (int i = 0; i < env_path.size(); i++) { + String p = path_join(env_path[i], p_name); + +#ifdef WINDOWS_ENABLED + for (int j = 0; j < exts.size(); j++) { + String p2 = p + exts[j]; + + if (FileAccess::exists(p2)) + return p2; + } +#else + if (FileAccess::exists(p)) + return p; +#endif + } + + return String(); +} + +void fix_path(const String &p_path, String &r_out) { + r_out = p_path.replace("\\", "/"); + + while (true) { // in case of using 2 or more slash + String compare = r_out.replace("//", "/"); + if (r_out == compare) + break; + else + r_out = compare; + } +} + +bool rel_path_to_abs(const String &p_existing_path, String &r_abs_path) { +#ifdef WINDOWS_ENABLED + CharType ret[_MAX_PATH]; + if (_wfullpath(ret, p_existing_path.c_str(), _MAX_PATH)) { + String abspath = String(ret).replace("\\", "/"); + int pos = abspath.find(":/"); + if (pos != -1) { + r_abs_path = abspath.substr(pos - 1, abspath.length()); + } else { + r_abs_path = abspath; + } + return true; + } +#else + char ret[PATH_MAX]; + if (realpath(p_existing_path.utf8().get_data(), ret)) { + String retstr; + if (!retstr.parse_utf8(ret)) { + r_abs_path = retstr; + return true; + } + } +#endif + return false; +} diff --git a/modules/mono/utils/path_utils.h b/modules/mono/utils/path_utils.h new file mode 100644 index 0000000000..445604300d --- /dev/null +++ b/modules/mono/utils/path_utils.h @@ -0,0 +1,53 @@ +/*************************************************************************/ +/* path_utils.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 PATH_UTILS_H +#define PATH_UTILS_H + +#include "ustring.h" + +_FORCE_INLINE_ String path_join(const String &e1, const String &e2) { + return e1.plus_file(e2); +} + +_FORCE_INLINE_ String path_join(const String &e1, const String &e2, const String &e3) { + return e1.plus_file(e2).plus_file(e3); +} + +_FORCE_INLINE_ String path_join(const String &e1, const String &e2, const String &e3, const String &e4) { + return e1.plus_file(e2).plus_file(e3).plus_file(e4); +} + +String path_which(const String &p_name); + +void fix_path(const String &p_path, String &r_out); + +bool rel_path_to_abs(const String &p_existing_path, String &r_abs_path); + +#endif // PATH_UTILS_H diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp new file mode 100644 index 0000000000..f26663ea11 --- /dev/null +++ b/modules/mono/utils/string_utils.cpp @@ -0,0 +1,157 @@ +/*************************************************************************/ +/* string_utils.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "string_utils.h" + +namespace { + +int sfind(const String &p_text, int p_from) { + if (p_from < 0) + return -1; + + int src_len = 2; + int len = p_text.length(); + + if (src_len == 0 || len == 0) + return -1; + + const CharType *src = p_text.c_str(); + + for (int i = p_from; i <= (len - src_len); i++) { + bool found = true; + + for (int j = 0; j < src_len; j++) { + int read_pos = i + j; + + if (read_pos >= len) { + ERR_PRINT("read_pos >= len"); + return -1; + }; + + switch (j) { + case 0: + found = src[read_pos] == '%'; + break; + case 1: { + CharType c = src[read_pos]; + found = src[read_pos] == 's' || (c >= '0' || c <= '4'); + break; + } + default: + found = false; + } + + if (!found) { + break; + } + } + + if (found) + return i; + } + + return -1; +} +} + +String sformat(const String &p_text, const Variant &p1, const Variant &p2, const Variant &p3, const Variant &p4, const Variant &p5) { + if (p_text.length() < 2) + return p_text; + + Array args; + + if (p1.get_type() != Variant::NIL) { + args.push_back(p1); + + if (p2.get_type() != Variant::NIL) { + args.push_back(p2); + + if (p3.get_type() != Variant::NIL) { + args.push_back(p3); + + if (p4.get_type() != Variant::NIL) { + args.push_back(p4); + + if (p5.get_type() != Variant::NIL) { + args.push_back(p5); + } + } + } + } + } + + String new_string; + + int findex = 0; + int search_from = 0; + int result = 0; + + while ((result = sfind(p_text, search_from)) >= 0) { + CharType c = p_text[result + 1]; + + int req_index = (c == 's' ? findex++ : c - '0'); + + new_string += p_text.substr(search_from, result - search_from); + new_string += args[req_index].operator String(); + search_from = result + 2; + } + + new_string += p_text.substr(search_from, p_text.length() - search_from); + + return new_string; +} + +bool is_csharp_keyword(const String &p_name) { + + // Reserved keywords + + return p_name == "abstract" || p_name == "as" || p_name == "base" || p_name == "bool" || + p_name == "break" || p_name == "byte" || p_name == "case" || p_name == "catch" || + p_name == "char" || p_name == "checked" || p_name == "class" || p_name == "const" || + p_name == "continue" || p_name == "decimal" || p_name == "default" || p_name == "delegate" || + p_name == "do" || p_name == "double" || p_name == "else" || p_name == "enum" || + p_name == "event" || p_name == "explicit" || p_name == "extern" || p_name == "false" || + p_name == "finally" || p_name == "fixed" || p_name == "float" || p_name == "for" || + p_name == "forech" || p_name == "goto" || p_name == "if" || p_name == "implicit" || + p_name == "in" || p_name == "int" || p_name == "interface" || p_name == "internal" || + p_name == "is" || p_name == "lock" || p_name == "long" || p_name == "namespace" || + p_name == "new" || p_name == "null" || p_name == "object" || p_name == "operator" || + p_name == "out" || p_name == "override" || p_name == "params" || p_name == "private" || + p_name == "protected" || p_name == "public" || p_name == "readonly" || p_name == "ref" || + p_name == "return" || p_name == "sbyte" || p_name == "sealed" || p_name == "short" || + p_name == "sizeof" || p_name == "stackalloc" || p_name == "static" || p_name == "string" || + p_name == "struct" || p_name == "switch" || p_name == "this" || p_name == "throw" || + p_name == "true" || p_name == "try" || p_name == "typeof" || p_name == "uint" || p_name == "ulong" || + p_name == "unchecked" || p_name == "unsafe" || p_name == "ushort" || p_name == "using" || + p_name == "virtual" || p_name == "volatile" || p_name == "void" || p_name == "while"; +} + +String escape_csharp_keyword(const String &p_name) { + return is_csharp_keyword(p_name) ? "@" + p_name : p_name; +} diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h new file mode 100644 index 0000000000..a0d66ebdc3 --- /dev/null +++ b/modules/mono/utils/string_utils.h @@ -0,0 +1,44 @@ +/*************************************************************************/ +/* string_utils.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 STRING_FORMAT_H +#define STRING_FORMAT_H + +#include "ustring.h" +#include "variant.h" + +String sformat(const String &p_text, const Variant &p1 = Variant(), const Variant &p2 = Variant(), const Variant &p3 = Variant(), const Variant &p4 = Variant(), const Variant &p5 = Variant()); + +#ifdef TOOLS_ENABLED +bool is_csharp_keyword(const String &p_name); + +String escape_csharp_keyword(const String &p_name); +#endif + +#endif // STRING_FORMAT_H diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub index 5eabaf6f2b..5e559bd4db 100644 --- a/modules/ogg/SCsub +++ b/modules/ogg/SCsub @@ -6,7 +6,7 @@ Import('env_modules') env_ogg = env_modules.Clone() # Thirdparty source files -if (env['builtin_libogg'] != 'no'): +if env['builtin_libogg']: thirdparty_dir = "#thirdparty/libogg/" thirdparty_sources = [ "bitwise.c", diff --git a/modules/openssl/SCsub b/modules/openssl/SCsub index add7d4dcfc..84c5e68439 100644 --- a/modules/openssl/SCsub +++ b/modules/openssl/SCsub @@ -6,7 +6,7 @@ Import('env_modules') env_openssl = env_modules.Clone() # Thirdparty source files -if (env['builtin_openssl'] != 'no'): +if env['builtin_openssl']: thirdparty_dir = "#thirdparty/openssl/" thirdparty_sources = [ @@ -655,7 +655,7 @@ if (env['builtin_openssl'] != 'no'): env_openssl.add_source_files(env.modules_sources, thirdparty_sources) - # FIXME: Clone the environment to make a env_openssl and not pollute the modules env + # FIXME: Clone the environment to make env_openssl and not pollute the modules env thirdparty_include_paths = [ "", "crypto", diff --git a/modules/openssl/stream_peer_openssl.cpp b/modules/openssl/stream_peer_openssl.cpp index d40bf73883..6d1d5485f3 100644 --- a/modules/openssl/stream_peer_openssl.cpp +++ b/modules/openssl/stream_peer_openssl.cpp @@ -29,6 +29,17 @@ /*************************************************************************/ #include "stream_peer_openssl.h" +// Compatibility with OpenSSL 1.1.0. +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) +#define BIO_set_num(b, n) +#else +#define BIO_set_num(b, n) ((b)->num = (n)) + +#define BIO_set_init(b, i) ((b)->init = (i)) +#define BIO_set_data(b, p) ((b)->ptr = (p)) +#define BIO_get_data(b) ((b)->ptr) +#endif + //hostname matching code from curl bool StreamPeerOpenSSL::_match_host_name(const char *name, const char *hostname) { @@ -157,10 +168,10 @@ int StreamPeerOpenSSL::_cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg } int StreamPeerOpenSSL::_bio_create(BIO *b) { - b->init = 1; - b->num = 0; - b->ptr = NULL; - b->flags = 0; + BIO_set_init(b, 1); + BIO_set_num(b, 0); + BIO_set_data(b, NULL); + BIO_clear_flags(b, ~0); return 1; } @@ -168,9 +179,9 @@ int StreamPeerOpenSSL::_bio_destroy(BIO *b) { if (b == NULL) return 0; - b->ptr = NULL; /* sb_tls_remove() will free it */ - b->init = 0; - b->flags = 0; + BIO_set_data(b, NULL); /* sb_tls_remove() will free it */ + BIO_set_init(b, 0); + BIO_clear_flags(b, ~0); return 1; } @@ -178,7 +189,7 @@ int StreamPeerOpenSSL::_bio_read(BIO *b, char *buf, int len) { if (buf == NULL || len <= 0) return 0; - StreamPeerOpenSSL *sp = (StreamPeerOpenSSL *)b->ptr; + StreamPeerOpenSSL *sp = (StreamPeerOpenSSL *)BIO_get_data(b); ERR_FAIL_COND_V(sp == NULL, 0); @@ -212,7 +223,7 @@ int StreamPeerOpenSSL::_bio_write(BIO *b, const char *buf, int len) { if (buf == NULL || len <= 0) return 0; - StreamPeerOpenSSL *sp = (StreamPeerOpenSSL *)b->ptr; + StreamPeerOpenSSL *sp = (StreamPeerOpenSSL *)BIO_get_data(b); ERR_FAIL_COND_V(sp == NULL, 0); @@ -258,6 +269,26 @@ int StreamPeerOpenSSL::_bio_puts(BIO *b, const char *str) { return _bio_write(b, str, strlen(str)); } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) +BIO_METHOD *StreamPeerOpenSSL::_bio_method = NULL; + +BIO_METHOD *StreamPeerOpenSSL::_get_bio_method() { + if (_bio_method) // already initialized. + return _bio_method; + + /* it's a source/sink BIO */ + _bio_method = BIO_meth_new(100 | 0x400, "streampeer glue"); + BIO_meth_set_write(_bio_method, _bio_write); + BIO_meth_set_read(_bio_method, _bio_read); + BIO_meth_set_puts(_bio_method, _bio_puts); + BIO_meth_set_gets(_bio_method, _bio_gets); + BIO_meth_set_ctrl(_bio_method, _bio_ctrl); + BIO_meth_set_create(_bio_method, _bio_create); + BIO_meth_set_destroy(_bio_method, _bio_destroy); + + return _bio_method; +} +#else BIO_METHOD StreamPeerOpenSSL::_bio_method = { /* it's a source/sink BIO */ (100 | 0x400), @@ -271,6 +302,11 @@ BIO_METHOD StreamPeerOpenSSL::_bio_method = { _bio_destroy }; +BIO_METHOD *StreamPeerOpenSSL::_get_bio_method() { + return &_bio_method; +} +#endif + Error StreamPeerOpenSSL::connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs, const String &p_for_hostname) { if (connected) @@ -330,8 +366,8 @@ Error StreamPeerOpenSSL::connect_to_stream(Ref<StreamPeer> p_base, bool p_valida } ssl = SSL_new(ctx); - bio = BIO_new(&_bio_method); - bio->ptr = this; + bio = BIO_new(_get_bio_method()); + BIO_set_data(bio, this); SSL_set_bio(ssl, bio, bio); if (p_for_hostname != String()) { @@ -532,7 +568,9 @@ void StreamPeerOpenSSL::initialize_ssl() { load_certs_func = _load_certs; _create = _create_func; +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use +#endif SSL_library_init(); // Initialize OpenSSL's SSL libraries SSL_load_error_strings(); // Load SSL error strings ERR_load_BIO_strings(); // Load BIO error strings diff --git a/modules/openssl/stream_peer_openssl.h b/modules/openssl/stream_peer_openssl.h index 1e445ef681..ad09564447 100644 --- a/modules/openssl/stream_peer_openssl.h +++ b/modules/openssl/stream_peer_openssl.h @@ -53,7 +53,12 @@ private: static int _bio_gets(BIO *b, char *buf, int len); static int _bio_puts(BIO *b, const char *str); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) + static BIO_METHOD *_bio_method; +#else static BIO_METHOD _bio_method; +#endif + static BIO_METHOD *_get_bio_method(); static bool _match_host_name(const char *name, const char *hostname); static Error _match_common_name(const char *hostname, const X509 *server_cert); diff --git a/modules/opus/SCsub b/modules/opus/SCsub index 4d3053c7ec..fee06bd267 100644 --- a/modules/opus/SCsub +++ b/modules/opus/SCsub @@ -6,7 +6,7 @@ Import('env_modules') env_opus = env_modules.Clone() # Thirdparty source files -if (env['builtin_opus'] != 'no'): +if env['builtin_opus']: thirdparty_dir = "#thirdparty/opus/" thirdparty_sources = [ @@ -209,7 +209,7 @@ if (env['builtin_opus'] != 'no'): env_opus.Append(CPPPATH=[thirdparty_dir + "/" + dir for dir in thirdparty_include_paths]) # also requires libogg - if (env['builtin_libogg'] != 'no'): + if env['builtin_libogg']: env_opus.Append(CPPPATH=["#thirdparty/libogg"]) # Module files diff --git a/modules/opus/audio_stream_opus.cpp b/modules/opus/audio_stream_opus.cpp index 1dac890eb8..06eab4c94d 100644 --- a/modules/opus/audio_stream_opus.cpp +++ b/modules/opus/audio_stream_opus.cpp @@ -56,7 +56,7 @@ int AudioStreamPlaybackOpus::_op_seek_func(void *_stream, opus_int64 _offset, in fa->seek(_offset); } break; case SEEK_CUR: { - fa->seek(fa->get_pos() + _offset); + fa->seek(fa->get_position() + _offset); } break; case SEEK_END: { fa->seek_end(_offset); @@ -83,7 +83,7 @@ int AudioStreamPlaybackOpus::_op_close_func(void *_stream) { opus_int64 AudioStreamPlaybackOpus::_op_tell_func(void *_stream) { FileAccess *_fa = (FileAccess *)_stream; - return (opus_int64)_fa->get_pos(); + return (opus_int64)_fa->get_position(); } void AudioStreamPlaybackOpus::_clear_stream() { @@ -247,7 +247,7 @@ void AudioStreamPlaybackOpus::play(float p_from) { frames_mixed = pre_skip; playing = true; if (p_from > 0) { - seek_pos(p_from); + seek(p_from); } } @@ -256,7 +256,7 @@ void AudioStreamPlaybackOpus::stop() { playing = false; } -void AudioStreamPlaybackOpus::seek_pos(float p_time) { +void AudioStreamPlaybackOpus::seek(float p_time) { if (!playing) return; ogg_int64_t pcm_offset = (ogg_int64_t)(p_time * osrate); bool ok = op_pcm_seek(opus_file, pcm_offset) == 0; @@ -267,7 +267,7 @@ void AudioStreamPlaybackOpus::seek_pos(float p_time) { frames_mixed = osrate * p_time; } -int AudioStreamPlaybackOpus::mix(int16_t *p_bufer, int p_frames) { +int AudioStreamPlaybackOpus::mix(int16_t *p_buffer, int p_frames) { if (!playing) return 0; @@ -281,7 +281,7 @@ int AudioStreamPlaybackOpus::mix(int16_t *p_bufer, int p_frames) { break; } - int ret = op_read(opus_file, (opus_int16 *)p_bufer, todo * stream_channels, ¤t_section); + int ret = op_read(opus_file, (opus_int16 *)p_buffer, todo * stream_channels, ¤t_section); if (ret < 0) { playing = false; ERR_EXPLAIN("Error reading Opus File: " + file); @@ -325,7 +325,7 @@ int AudioStreamPlaybackOpus::mix(int16_t *p_bufer, int p_frames) { frames_mixed += ret; - p_bufer += ret * stream_channels; + p_buffer += ret * stream_channels; p_frames -= ret; } @@ -340,7 +340,7 @@ float AudioStreamPlaybackOpus::get_length() const { return length; } -float AudioStreamPlaybackOpus::get_pos() const { +float AudioStreamPlaybackOpus::get_playback_position() const { int32_t frames = int32_t(frames_mixed); if (frames < 0) diff --git a/modules/opus/audio_stream_opus.h b/modules/opus/audio_stream_opus.h index ccfe04e84e..f8d8f585cf 100644 --- a/modules/opus/audio_stream_opus.h +++ b/modules/opus/audio_stream_opus.h @@ -99,15 +99,15 @@ public: virtual int get_loop_count() const { return repeats; } - virtual float get_pos() const; - virtual void seek_pos(float p_time); + virtual float get_playback_position() const; + virtual void seek(float p_time); virtual int get_channels() const { return stream_channels; } virtual int get_mix_rate() const { return osrate; } virtual int get_minimum_buffer_size() const; - virtual int mix(int16_t *p_bufer, int p_frames); + virtual int mix(int16_t *p_buffer, int p_frames); AudioStreamPlaybackOpus(); ~AudioStreamPlaybackOpus(); diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp index 9989785c70..03592047ad 100644 --- a/modules/pvr/texture_loader_pvr.cpp +++ b/modules/pvr/texture_loader_pvr.cpp @@ -74,15 +74,11 @@ RES ResourceFormatPVR::load(const String &p_path, const String &p_original_path, uint32_t mipmaps = f->get_32(); uint32_t flags = f->get_32(); uint32_t surfsize = f->get_32(); - uint32_t bpp = f->get_32(); - uint32_t rmask = f->get_32(); - uint32_t gmask = f->get_32(); - uint32_t bmask = f->get_32(); - uint32_t amask = f->get_32(); + f->seek(f->get_position() + 20); // bpp, rmask, gmask, bmask, amask uint8_t pvrid[5] = { 0, 0, 0, 0, 0 }; f->get_buffer(pvrid, 4); ERR_FAIL_COND_V(String((char *)pvrid) != "PVR!", RES()); - uint32_t surfcount = f->get_32(); + f->get_32(); // surfcount /* print_line("height: "+itos(height)); diff --git a/modules/recast/SCsub b/modules/recast/SCsub new file mode 100644 index 0000000000..500c0ec055 --- /dev/null +++ b/modules/recast/SCsub @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +Import('env') + +# Not building in a separate env as core needs it + +# Thirdparty source files +if env['builtin_recast']: + thirdparty_dir = "#thirdparty/recastnavigation/Recast/" + thirdparty_sources = [ + "Source/Recast.cpp", + "Source/RecastAlloc.cpp", + "Source/RecastArea.cpp", + "Source/RecastAssert.cpp", + "Source/RecastContour.cpp", + "Source/RecastFilter.cpp", + "Source/RecastLayers.cpp", + "Source/RecastMesh.cpp", + "Source/RecastMeshDetail.cpp", + "Source/RecastRasterization.cpp", + "Source/RecastRegion.cpp", + ] + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + + env.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "/Include"]) + + lib = env.Library("recast_builtin", thirdparty_sources) + env.Append(LIBS=[lib]) + +# Godot source files +env.add_source_files(env.modules_sources, "*.cpp") +env.Append(CCFLAGS=['-DRECAST_ENABLED']) + +Export('env') diff --git a/modules/nativescript/config.py b/modules/recast/config.py index 4f89ca0d4c..d42f07b2a9 100644 --- a/modules/nativescript/config.py +++ b/modules/recast/config.py @@ -1,8 +1,7 @@ - def can_build(platform): - return False + return platform != "android" def configure(env): - env.use_ptrcall = True + pass diff --git a/modules/recast/register_types.cpp b/modules/recast/register_types.cpp new file mode 100644 index 0000000000..654ceec373 --- /dev/null +++ b/modules/recast/register_types.cpp @@ -0,0 +1,34 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "register_types.h" + +void register_recast_types() {} +void unregister_recast_types() {} diff --git a/modules/recast/register_types.h b/modules/recast/register_types.h new file mode 100644 index 0000000000..90587bc11f --- /dev/null +++ b/modules/recast/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +void register_recast_types(); +void unregister_recast_types(); diff --git a/modules/regex/SCsub b/modules/regex/SCsub index 0882406761..18b4051afe 100644 --- a/modules/regex/SCsub +++ b/modules/regex/SCsub @@ -1,7 +1,53 @@ #!/usr/bin/env python Import('env') +Import('env_modules') -env.add_source_files(env.modules_sources, "*.cpp") +env_regex = env_modules.Clone() +env_regex.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=0"]) +env_regex.add_source_files(env.modules_sources, "*.cpp") -Export('env') +if env['builtin_pcre2']: + jit_blacklist = ['javascript', 'uwp'] + thirdparty_dir = '#thirdparty/pcre2/src/' + thirdparty_flags = ['-DPCRE2_STATIC', '-DHAVE_CONFIG_H'] + if 'platform' in env and env['platform'] not in jit_blacklist: + thirdparty_flags.append('-DSUPPORT_JIT') + thirdparty_sources = [ + "pcre2_auto_possess.c", + "pcre2_chartables.c", + "pcre2_compile.c", + "pcre2_config.c", + "pcre2_context.c", + "pcre2_dfa_match.c", + "pcre2_error.c", + "pcre2_find_bracket.c", + "pcre2_jit_compile.c", + "pcre2_maketables.c", + "pcre2_match.c", + "pcre2_match_data.c", + "pcre2_newline.c", + "pcre2_ord2utf.c", + "pcre2_pattern_info.c", + "pcre2_serialize.c", + "pcre2_string_utils.c", + "pcre2_study.c", + "pcre2_substitute.c", + "pcre2_substring.c", + "pcre2_tables.c", + "pcre2_ucd.c", + "pcre2_valid_utf.c", + "pcre2_xclass.c", + ] + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + env_regex.Append(CPPPATH=[thirdparty_dir]) + env_regex.Append(CPPFLAGS=thirdparty_flags) + def pcre2_builtin(width): + env_pcre2 = env_modules.Clone() + env_pcre2["OBJSUFFIX"] = "_" + width + env_pcre2["OBJSUFFIX"] + env_pcre2.Append(CPPPATH=[thirdparty_dir]) + env_pcre2.add_source_files(env.modules_sources, thirdparty_sources) + env_pcre2.Append(CPPFLAGS=thirdparty_flags) + env_pcre2.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=" + width]) + pcre2_builtin("16") + pcre2_builtin("32") diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp index de0a6b7e21..00e8ce0f54 100644 --- a/modules/regex/regex.cpp +++ b/modules/regex/regex.cpp @@ -29,1479 +29,469 @@ /*************************************************************************/ #include "regex.h" -#include <wchar.h> -#include <wctype.h> - -static int RegEx_hex2int(const CharType c) { - if ('0' <= c && c <= '9') - return int(c - '0'); - else if ('a' <= c && c <= 'f') - return int(c - 'a') + 10; - else if ('A' <= c && c <= 'F') - return int(c - 'A') + 10; - return -1; -} - -struct RegExSearch { - - Ref<RegExMatch> match; - const CharType *str; - int end; - int eof; - - // For standard quantifier behaviour, test_parent is used to check the - // rest of the pattern. If the pattern matches, to prevent the parent - // from testing again, the complete flag is used as a shortcut out. - bool complete; - - // With lookahead, the position needs to rewind to its starting position - // when test_parent is used. Due to functional programming, this state - // has to be kept as a parameter. - Vector<int> lookahead_pos; - - CharType at(int p_pos) { - return str[p_pos]; - } - - RegExSearch(Ref<RegExMatch> &p_match, int p_end, int p_lookahead) - : match(p_match) { - - str = p_match->string.c_str(); - end = p_end; - eof = p_match->string.length(); - complete = false; - lookahead_pos.resize(p_lookahead); - } -}; - -struct RegExNode { - - RegExNode *next; - RegExNode *previous; - RegExNode *parent; - bool quantifiable; - int length; - - RegExNode() { - - next = NULL; - previous = NULL; - parent = NULL; - quantifiable = false; - length = -1; - } - - virtual ~RegExNode() { - - if (next) - memdelete(next); - } +#include "core/os/memory.h" - // For avoiding RTTI - virtual bool is_look_behind() { return false; } - - virtual int test(RegExSearch &s, int pos) const { - - return next ? next->test(s, pos) : -1; - } - - virtual int test_parent(RegExSearch &s, int pos) const { - - if (next) - pos = next->test(s, pos); - - if (pos >= 0) { - s.complete = true; - if (parent) - pos = parent->test_parent(s, pos); - } - - if (pos < 0) - s.complete = false; - - return pos; - } - - void increment_length(int amount, bool subtract = false) { - - if (amount >= 0 && length >= 0) { - if (!subtract) - length += amount; - else - length -= amount; - } else { - length = -1; - } +extern "C" { +#include <pcre2.h> +} - if (parent) - parent->increment_length(amount, subtract); - } -}; +static void *_regex_malloc(PCRE2_SIZE size, void *user) { -struct RegExNodeChar : public RegExNode { + return memalloc(size); +} - CharType ch; +static void _regex_free(void *ptr, void *user) { - RegExNodeChar(CharType p_char) { + memfree(ptr); +} - length = 1; - quantifiable = true; - ch = p_char; - } +int RegExMatch::_find(const Variant &p_name) const { - virtual int test(RegExSearch &s, int pos) const { + if (p_name.is_num()) { - if (s.end <= pos || 0 > pos || s.at(pos) != ch) + int i = (int)p_name; + if (i >= data.size()) return -1; + return i; - return next ? next->test(s, pos + 1) : pos + 1; - } + } else if (p_name.get_type() == Variant::STRING) { - static CharType parse_escape(const CharType *&c) { - - int point = 0; - switch (c[1]) { - case 'x': - for (int i = 2; i <= 3; ++i) { - int res = RegEx_hex2int(c[i]); - if (res == -1) - return '\0'; - point = (point << 4) + res; - } - c = &c[3]; - return CharType(point); - case 'u': - for (int i = 2; i <= 5; ++i) { - int res = RegEx_hex2int(c[i]); - if (res == -1) - return '\0'; - point = (point << 4) + res; - } - c = &c[5]; - return CharType(point); - case '0': ++c; return '\0'; - case 'a': ++c; return '\a'; - case 'e': ++c; return '\e'; - case 'f': ++c; return '\f'; - case 'n': ++c; return '\n'; - case 'r': ++c; return '\r'; - case 't': ++c; return '\t'; - case 'v': ++c; return '\v'; - case 'b': ++c; return '\b'; - default: break; - } - return (++c)[0]; + const Map<String, int>::Element *found = names.find((String)p_name); + if (found) + return found->value(); } -}; -struct RegExNodeRange : public RegExNode { + return -1; +} - CharType start; - CharType end; +String RegExMatch::get_subject() const { - RegExNodeRange(CharType p_start, CharType p_end) { + return subject; +} - length = 1; - quantifiable = true; - start = p_start; - end = p_end; - } +int RegExMatch::get_group_count() const { - virtual int test(RegExSearch &s, int pos) const { + if (data.size() == 0) + return 0; + return data.size() - 1; +} - if (s.end <= pos || 0 > pos) - return -1; +Dictionary RegExMatch::get_names() const { - CharType c = s.at(pos); - if (c < start || end < c) - return -1; + Dictionary result; - return next ? next->test(s, pos + 1) : pos + 1; + for (const Map<String, int>::Element *i = names.front(); i != NULL; i = i->next()) { + result[i->key()] = i->value(); } -}; - -struct RegExNodeShorthand : public RegExNode { - CharType repr; + return result; +} - RegExNodeShorthand(CharType p_repr) { +Array RegExMatch::get_strings() const { - length = 1; - quantifiable = true; - repr = p_repr; - } + Array result; - virtual int test(RegExSearch &s, int pos) const { + int size = data.size(); - if (s.end <= pos || 0 > pos) - return -1; + for (int i = 0; i < size; i++) { - bool found = false; - bool invert = false; - CharType c = s.at(pos); - switch (repr) { - case '.': - found = true; - break; - case 'W': - invert = true; - case 'w': - found = (c == '_' || iswalnum(c) != 0); - break; - case 'D': - invert = true; - case 'd': - found = ('0' <= c && c <= '9'); - break; - case 'S': - invert = true; - case 's': - found = (iswspace(c) != 0); - break; - default: - break; - } + int start = data[i].start; - if (found == invert) - return -1; - - return next ? next->test(s, pos + 1) : pos + 1; - } -}; - -struct RegExNodeClass : public RegExNode { - - enum Type { - Type_none, - Type_alnum, - Type_alpha, - Type_ascii, - Type_blank, - Type_cntrl, - Type_digit, - Type_graph, - Type_lower, - Type_print, - Type_punct, - Type_space, - Type_upper, - Type_xdigit, - Type_word - }; - - Type type; - - bool test_class(CharType c) const { - - static Vector<CharType> REGEX_NODE_SPACE = String(" \t\r\n\f"); - static Vector<CharType> REGEX_NODE_PUNCT = String("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"); - - switch (type) { - case Type_alnum: - if ('0' <= c && c <= '9') return true; - if ('a' <= c && c <= 'z') return true; - if ('A' <= c && c <= 'Z') return true; - return false; - case Type_alpha: - if ('a' <= c && c <= 'z') return true; - if ('A' <= c && c <= 'Z') return true; - return false; - case Type_ascii: - return (0x00 <= c && c <= 0x7F); - case Type_blank: - return (c == ' ' || c == '\t'); - case Type_cntrl: - return ((0x00 <= c && c <= 0x1F) || c == 0x7F); - case Type_digit: - return ('0' <= c && c <= '9'); - case Type_graph: - return (0x20 < c && c < 0x7F); - case Type_lower: - return ('a' <= c && c <= 'z'); - case Type_print: - return (0x20 < c && c < 0x7f); - case Type_punct: - return (REGEX_NODE_PUNCT.find(c) >= 0); - case Type_space: - return (REGEX_NODE_SPACE.find(c) >= 0); - case Type_upper: - return ('A' <= c && c <= 'Z'); - case Type_xdigit: - if ('0' <= c && c <= '9') return true; - if ('a' <= c && c <= 'f') return true; - if ('A' <= c && c <= 'F') return true; - return false; - case Type_word: - if ('0' <= c && c <= '9') return true; - if ('a' <= c && c <= 'z') return true; - if ('A' <= c && c <= 'Z') return true; - return (c == '_'); - default: - return false; + if (start == -1) { + result.append(String()); + continue; } - return false; - } - - RegExNodeClass(Type p_type) { - - length = 1; - quantifiable = true; - type = p_type; - } - - virtual int test(RegExSearch &s, int pos) const { - - if (s.end <= pos || 0 > pos) - return -1; - - if (!test_class(s.at(pos))) - return -1; - - return next ? next->test(s, pos + 1) : pos + 1; - } - -#define REGEX_CMP_CLASS(POS, NAME) \ - if (cmp_class(POS, #NAME)) return Type_##NAME - - static Type parse_type(const CharType *&p_pos) { - - REGEX_CMP_CLASS(p_pos, alnum); - REGEX_CMP_CLASS(p_pos, alpha); - REGEX_CMP_CLASS(p_pos, ascii); - REGEX_CMP_CLASS(p_pos, blank); - REGEX_CMP_CLASS(p_pos, cntrl); - REGEX_CMP_CLASS(p_pos, digit); - REGEX_CMP_CLASS(p_pos, graph); - REGEX_CMP_CLASS(p_pos, lower); - REGEX_CMP_CLASS(p_pos, print); - REGEX_CMP_CLASS(p_pos, punct); - REGEX_CMP_CLASS(p_pos, space); - REGEX_CMP_CLASS(p_pos, upper); - REGEX_CMP_CLASS(p_pos, xdigit); - REGEX_CMP_CLASS(p_pos, word); - return Type_none; - } - - static bool cmp_class(const CharType *&p_pos, const char *p_text) { - - unsigned int i = 0; - for (i = 0; p_text[i] != '\0'; ++i) - if (p_pos[i] != p_text[i]) - return false; - - if (p_pos[i++] != ':' || p_pos[i] != ']') - return false; - - p_pos = &p_pos[i]; - return true; - } -}; - -struct RegExNodeAnchorStart : public RegExNode { - RegExNodeAnchorStart() { + int length = data[i].end - start; - length = 0; + result.append(subject.substr(start, length)); } - virtual int test(RegExSearch &s, int pos) const { + return result; +} - if (pos != 0) - return -1; +String RegExMatch::get_string(const Variant &p_name) const { - return next ? next->test(s, pos) : pos; - } -}; + int id = _find(p_name); -struct RegExNodeAnchorEnd : public RegExNode { + if (id < 0) + return String(); - RegExNodeAnchorEnd() { + int start = data[id].start; - length = 0; - } + if (start == -1) + return String(); - virtual int test(RegExSearch &s, int pos) const { + int length = data[id].end - start; - if (pos != s.eof) - return -1; + return subject.substr(start, length); +} - return next ? next->test(s, pos) : pos; - } -}; +int RegExMatch::get_start(const Variant &p_name) const { -struct RegExNodeWordBoundary : public RegExNode { + int id = _find(p_name); - bool inverse; + if (id < 0) + return -1; - RegExNodeWordBoundary(bool p_inverse) { + return data[id].start; +} - length = 0; - inverse = p_inverse; - } +int RegExMatch::get_end(const Variant &p_name) const { - virtual int test(RegExSearch &s, int pos) const { + int id = _find(p_name); - bool left = false; - bool right = false; + if (id < 0) + return -1; - if (pos != 0) { - CharType c = s.at(pos - 1); - if (c == '_' || iswalnum(c)) - left = true; - } + return data[id].end; +} - if (pos != s.eof) { - CharType c = s.at(pos); - if (c == '_' || iswalnum(c)) - right = true; - } +void RegExMatch::_bind_methods() { - if ((left == right) != inverse) - return -1; + ClassDB::bind_method(D_METHOD("get_subject"), &RegExMatch::get_subject); + ClassDB::bind_method(D_METHOD("get_group_count"), &RegExMatch::get_group_count); + ClassDB::bind_method(D_METHOD("get_names"), &RegExMatch::get_names); + ClassDB::bind_method(D_METHOD("get_strings"), &RegExMatch::get_strings); + ClassDB::bind_method(D_METHOD("get_string", "name"), &RegExMatch::get_string, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_start", "name"), &RegExMatch::get_start, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_end", "name"), &RegExMatch::get_end, DEFVAL(0)); +} - return next ? next->test(s, pos) : pos; - } -}; +void RegEx::_pattern_info(uint32_t what, void *where) const { -struct RegExNodeQuantifier : public RegExNode { + if (sizeof(CharType) == 2) { - int min; - int max; - bool greedy; - RegExNode *child; + pcre2_pattern_info_16((pcre2_code_16 *)code, what, where); - RegExNodeQuantifier(int p_min, int p_max) { + } else { - min = p_min; - max = p_max; - greedy = true; - child = NULL; + pcre2_pattern_info_32((pcre2_code_32 *)code, what, where); } +} - ~RegExNodeQuantifier() { - - if (child) - memdelete(child); - } +void RegEx::clear() { - virtual int test(RegExSearch &s, int pos) const { + if (sizeof(CharType) == 2) { - return test_step(s, pos, 0, pos); - } + if (code) + pcre2_code_free_16((pcre2_code_16 *)code); - virtual int test_parent(RegExSearch &s, int pos) const { + } else { - s.complete = false; - return pos; + if (code) + pcre2_code_free_32((pcre2_code_32 *)code); } +} - int test_step(RegExSearch &s, int pos, int level, int start) const { - - if (pos > s.end) - return -1; - - if (!greedy && level > min) { - int res = next ? next->test(s, pos) : pos; - if (s.complete) - return res; - - if (res >= 0 && parent->test_parent(s, res) >= 0) - return res; - } - - if (max >= 0 && level > max) - return -1; +Error RegEx::compile(const String &p_pattern) { - int res = pos; - if (level >= 1) { - if (level > min + 1 && pos == start) - return -1; + pattern = p_pattern; + clear(); - res = child->test(s, pos); - if (s.complete) - return res; - } + int err; + PCRE2_SIZE offset; + uint32_t flags = PCRE2_DUPNAMES; - if (res >= 0) { + if (sizeof(CharType) == 2) { - int res_step = test_step(s, res, level + 1, start); - if (res_step >= 0) - return res_step; + pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx; + pcre2_compile_context_16 *cctx = pcre2_compile_context_create_16(gctx); + PCRE2_SPTR16 p = (PCRE2_SPTR16)pattern.c_str(); - if (greedy && level >= min) { - if (next) - res = next->test(s, res); - if (s.complete) - return res; + code = pcre2_compile_16(p, pattern.length(), flags, &err, &offset, cctx); - if (res >= 0 && parent->test_parent(s, res) >= 0) - return res; - } + if (!code) { + PCRE2_UCHAR16 buf[256]; + pcre2_get_error_message_16(err, buf, 256); + String message = String::num(offset) + ": " + String((const CharType *)buf); + ERR_PRINT(message.utf8()); + return FAILED; } - return -1; - } -}; - -struct RegExNodeBackReference : public RegExNode { - int id; + } else { - RegExNodeBackReference(int p_id) { - - length = -1; - quantifiable = true; - id = p_id; - } + pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; + pcre2_compile_context_32 *cctx = pcre2_compile_context_create_32(gctx); + PCRE2_SPTR32 p = (PCRE2_SPTR32)pattern.c_str(); - virtual int test(RegExSearch &s, int pos) const { + code = pcre2_compile_32(p, pattern.length(), flags, &err, &offset, cctx); - RegExMatch::Group &ref = s.match->captures[id]; - for (int i = 0; i < ref.length; ++i) { - - if (pos + i >= s.end) - return -1; - - if (s.at(ref.start + i) != s.at(pos + i)) - return -1; + if (!code) { + PCRE2_UCHAR32 buf[256]; + pcre2_get_error_message_32(err, buf, 256); + String message = String::num(offset) + ": " + String((const CharType *)buf); + ERR_PRINT(message.utf8()); + return FAILED; } - return next ? next->test(s, pos + ref.length) : pos + ref.length; } -}; - -struct RegExNodeGroup : public RegExNode { - - bool inverse; - bool reset_pos; - Vector<RegExNode *> childset; - RegExNode *back; - - RegExNodeGroup() { - - length = 0; - quantifiable = true; - inverse = false; - reset_pos = false; - back = NULL; - } - - virtual ~RegExNodeGroup() { - - for (int i = 0; i < childset.size(); ++i) - memdelete(childset[i]); - } - - virtual void test_success(RegExSearch &s, int pos) const { + return OK; +} - return; - } +Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end) const { - virtual int test(RegExSearch &s, int pos) const { + ERR_FAIL_COND_V(!is_valid(), NULL); - for (int i = 0; i < childset.size(); ++i) { + Ref<RegExMatch> result = memnew(RegExMatch); - s.complete = false; + int length = p_subject.length(); + if (p_end >= 0 && p_end < length) + length = p_end; - int res = childset[i]->test(s, pos); + if (sizeof(CharType) == 2) { - if (inverse) { - s.complete = false; - if (res < 0) - res = pos + 1; - else - return -1; + pcre2_code_16 *c = (pcre2_code_16 *)code; + pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx; + pcre2_match_context_16 *mctx = pcre2_match_context_create_16(gctx); + PCRE2_SPTR16 s = (PCRE2_SPTR16)p_subject.c_str(); - if (i + 1 < childset.size()) - continue; - } + pcre2_match_data_16 *match = pcre2_match_data_create_from_pattern_16(c, gctx); - if (s.complete) - return res; + int res = pcre2_match_16(c, s, length, p_offset, 0, match, mctx); - if (res >= 0) { - if (reset_pos) - res = pos; - this->test_success(s, res); - return next ? next->test(s, res) : res; - } + if (res < 0) { + pcre2_match_data_free_16(match); + return NULL; } - return -1; - } - - void add_child(RegExNode *node) { - node->parent = this; - node->previous = back; + uint32_t size = pcre2_get_ovector_count_16(match); + PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_16(match); - if (back) - back->next = node; - else - childset.push_back(node); + result->data.resize(size); - increment_length(node->length); + for (uint32_t i = 0; i < size; i++) { - back = node; - } - - void add_childset() { - - if (childset.size() > 0) - length = -1; - back = NULL; - } - - RegExNode *swap_back(RegExNode *node) { - - RegExNode *old = back; - - if (old) { - if (!old->previous) - childset.remove(childset.size() - 1); - back = old->previous; - increment_length(old->length, true); + result->data[i].start = ovector[i * 2]; + result->data[i].end = ovector[i * 2 + 1]; } - add_child(node); + pcre2_match_data_free_16(match); + pcre2_match_context_free_16(mctx); - return old; - } -}; + } else { -struct RegExNodeCapturing : public RegExNodeGroup { + pcre2_code_32 *c = (pcre2_code_32 *)code; + pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; + pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx); + PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.c_str(); - int id; + pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx); - RegExNodeCapturing(int p_id = 0) { + int res = pcre2_match_32(c, s, length, p_offset, 0, match, mctx); - id = p_id; - } - - virtual void test_success(RegExSearch &s, int pos) const { - - RegExMatch::Group &ref = s.match->captures[id]; - ref.length = pos - ref.start; - } - - virtual int test(RegExSearch &s, int pos) const { - - RegExMatch::Group &ref = s.match->captures[id]; - int old_start = ref.start; - ref.start = pos; - - int res = RegExNodeGroup::test(s, pos); - - if (res < 0) - ref.start = old_start; - return res; - } - - virtual int test_parent(RegExSearch &s, int pos) const { - - RegExMatch::Group &ref = s.match->captures[id]; - ref.length = pos - ref.start; - return RegExNode::test_parent(s, pos); - } - - static Variant parse_name(const CharType *&c, bool p_allow_numeric) { - - if (c[1] == '0') { - return -1; - } else if ('1' <= c[1] && c[1] <= '9') { - if (!p_allow_numeric) - return -1; - int res = (++c)[0] - '0'; - while ('0' <= c[1] && c[1] <= '9') - res = res * 10 + int((++c)[0] - '0'); - if ((++c)[0] != '>') - return -1; - return res; - } else if (iswalnum(c[1])) { - String res(++c, 1); - while (iswalnum(c[1])) - res += String(++c, 1); - if ((++c)[0] != '>') - return -1; - return res; + if (res < 0) { + pcre2_match_data_free_32(match); + return NULL; } - return -1; - } -}; - -struct RegExNodeLookAhead : public RegExNodeGroup { - - int id; - RegExNodeLookAhead(bool p_inverse, int p_id = 0) { + uint32_t size = pcre2_get_ovector_count_32(match); + PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_32(match); - quantifiable = false; - inverse = p_inverse; - reset_pos = true; - id = p_id; - } - - virtual int test(RegExSearch &s, int pos) const { + result->data.resize(size); - s.lookahead_pos[id] = pos; - return RegExNodeGroup::test(s, pos); - } + for (uint32_t i = 0; i < size; i++) { - virtual int test_parent(RegExSearch &s, int pos) const { + result->data[i].start = ovector[i * 2]; + result->data[i].end = ovector[i * 2 + 1]; + } - return RegExNode::test_parent(s, s.lookahead_pos[id]); + pcre2_match_data_free_32(match); + pcre2_match_context_free_32(mctx); } -}; -struct RegExNodeLookBehind : public RegExNodeGroup { + result->subject = p_subject; - RegExNodeLookBehind(bool p_inverse, int p_id = 0) { + uint32_t count; + const CharType *table; + uint32_t entry_size; - quantifiable = false; - inverse = p_inverse; - reset_pos = true; - } + _pattern_info(PCRE2_INFO_NAMECOUNT, &count); + _pattern_info(PCRE2_INFO_NAMETABLE, &table); + _pattern_info(PCRE2_INFO_NAMEENTRYSIZE, &entry_size); - virtual bool is_look_behind() { return true; } + for (uint32_t i = 0; i < count; i++) { - virtual int test(RegExSearch &s, int pos) const { + CharType id = table[i * entry_size]; + if (result->data[id].start == -1) + continue; + String name = &table[i * entry_size + 1]; + if (result->names.has(name)) + continue; - if (pos < length) - return -1; - return RegExNodeGroup::test(s, pos - length); + result->names.insert(name, id); } -}; -struct RegExNodeBracket : public RegExNode { + return result; +} - bool inverse; - Vector<RegExNode *> children; +String RegEx::sub(const String &p_subject, const String &p_replacement, bool p_all, int p_offset, int p_end) const { - RegExNodeBracket() { + ERR_FAIL_COND_V(!is_valid(), String()); - length = 1; - quantifiable = true; - inverse = false; - } + String output; + output.resize(p_subject.length()); - virtual ~RegExNodeBracket() { + uint32_t flags = PCRE2_SUBSTITUTE_OVERFLOW_LENGTH; + if (p_all) + flags |= PCRE2_SUBSTITUTE_GLOBAL; - for (int i = 0; i < children.size(); ++i) - memdelete(children[i]); - } + PCRE2_SIZE olength = output.length(); - virtual int test(RegExSearch &s, int pos) const { + PCRE2_SIZE length = p_subject.length(); + if (p_end >= 0 && (uint32_t)p_end < length) + length = p_end; - for (int i = 0; i < children.size(); ++i) { + if (sizeof(CharType) == 2) { - int res = children[i]->test(s, pos); + pcre2_code_16 *c = (pcre2_code_16 *)code; + pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx; + pcre2_match_context_16 *mctx = pcre2_match_context_create_16(gctx); + PCRE2_SPTR16 s = (PCRE2_SPTR16)p_subject.c_str(); + PCRE2_SPTR16 r = (PCRE2_SPTR16)p_replacement.c_str(); + PCRE2_UCHAR16 *o = (PCRE2_UCHAR16 *)output.c_str(); - if (inverse) { - if (res < 0) - res = pos + 1; - else - return -1; + pcre2_match_data_16 *match = pcre2_match_data_create_from_pattern_16(c, gctx); - if (i + 1 < children.size()) - continue; - } + int res = pcre2_substitute_16(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); - if (res >= 0) - return next ? next->test(s, res) : res; + if (res == PCRE2_ERROR_NOMEMORY) { + output.resize(olength); + o = (PCRE2_UCHAR16 *)output.c_str(); + res = pcre2_substitute_16(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); } - return -1; - } - void add_child(RegExNode *node) { - - node->parent = this; - children.push_back(node); - } + pcre2_match_data_free_16(match); + pcre2_match_context_free_16(mctx); - void pop_back() { - - memdelete(children[children.size() - 1]); - children.remove(children.size() - 1); - } -}; - -#define REGEX_EXPAND_FAIL(MSG) \ - { \ - ERR_PRINT(MSG); \ - return String(); \ - } - -String RegExMatch::expand(const String &p_template) const { - - String res; - for (const CharType *c = p_template.c_str(); *c != '\0'; ++c) { - if (c[0] == '\\') { - if (('1' <= c[1] && c[1] <= '9') || (c[1] == 'g' && c[2] == '{')) { - - int ref = 0; - bool unclosed = false; - - if (c[1] == 'g') { - unclosed = true; - c = &c[2]; - } - - while ('0' <= c[1] && c[1] <= '9') { - ref = ref * 10 + int(c[1] - '0'); - ++c; - } - - if (unclosed) { - if (c[1] != '}') - REGEX_EXPAND_FAIL("unclosed backreference '{'"); - ++c; - } - - res += get_string(ref); - - } else if (c[1] == 'g' && c[2] == '<') { - - const CharType *d = &c[2]; + if (res < 0) + return String(); - Variant name = RegExNodeCapturing::parse_name(d, true); - if (name == Variant(-1)) - REGEX_EXPAND_FAIL("unrecognised character for group name"); + } else { - c = d; + pcre2_code_32 *c = (pcre2_code_32 *)code; + pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; + pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx); + PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.c_str(); + PCRE2_SPTR32 r = (PCRE2_SPTR32)p_replacement.c_str(); + PCRE2_UCHAR32 *o = (PCRE2_UCHAR32 *)output.c_str(); - res += get_string(name); + pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx); - } else { + int res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); - const CharType *d = c; - CharType ch = RegExNodeChar::parse_escape(d); - if (c == d) - REGEX_EXPAND_FAIL("invalid escape token"); - res += String(&ch, 1); - c = d; - } - } else { - res += String(c, 1); + if (res == PCRE2_ERROR_NOMEMORY) { + output.resize(olength); + o = (PCRE2_UCHAR32 *)output.c_str(); + res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); } - } - return res; -} -int RegExMatch::get_group_count() const { + pcre2_match_data_free_32(match); + pcre2_match_context_free_32(mctx); - int count = 0; - for (int i = 1; i < captures.size(); ++i) - if (captures[i].name.get_type() == Variant::INT) - ++count; - return count; -} - -Array RegExMatch::get_group_array() const { - - Array res; - for (int i = 1; i < captures.size(); ++i) { - const RegExMatch::Group &capture = captures[i]; - if (capture.name.get_type() != Variant::INT) - continue; - - if (capture.start >= 0) - res.push_back(string.substr(capture.start, capture.length)); - else - res.push_back(String()); - } - return res; -} - -Array RegExMatch::get_names() const { - - Array res; - for (int i = 1; i < captures.size(); ++i) - if (captures[i].name.get_type() == Variant::STRING) - res.push_back(captures[i].name); - return res; -} - -Dictionary RegExMatch::get_name_dict() const { - - Dictionary res; - for (int i = 1; i < captures.size(); ++i) { - const RegExMatch::Group &capture = captures[i]; - if (capture.name.get_type() != Variant::STRING) - continue; - - if (capture.start >= 0) - res[capture.name] = string.substr(capture.start, capture.length); - else - res[capture.name] = String(); - } - return res; -} - -String RegExMatch::get_string(const Variant &p_name) const { - - for (int i = 0; i < captures.size(); ++i) { - - const RegExMatch::Group &capture = captures[i]; - - if (capture.name != p_name) - continue; - - if (capture.start == -1) + if (res < 0) return String(); - - return string.substr(capture.start, capture.length); } - return String(); -} - -int RegExMatch::get_start(const Variant &p_name) const { - for (int i = 0; i < captures.size(); ++i) - if (captures[i].name == p_name) - return captures[i].start; - return -1; + return output; } -int RegExMatch::get_end(const Variant &p_name) const { +bool RegEx::is_valid() const { - for (int i = 0; i < captures.size(); ++i) - if (captures[i].name == p_name) - return captures[i].start + captures[i].length; - return -1; + return (code != NULL); } -RegExMatch::RegExMatch() { -} +String RegEx::get_pattern() const { -static bool RegEx_is_shorthand(CharType ch) { - - switch (ch) { - case 'w': - case 'W': - case 'd': - case 'D': - case 's': - case 'S': - return true; - default: - break; - } - return false; + return pattern; } -#define REGEX_COMPILE_FAIL(MSG) \ - { \ - ERR_PRINT(MSG); \ - clear(); \ - return FAILED; \ - } +int RegEx::get_group_count() const { -Error RegEx::compile(const String &p_pattern) { + ERR_FAIL_COND_V(!is_valid(), 0); - ERR_FAIL_COND_V(p_pattern.length() == 0, FAILED); + uint32_t count; - if (pattern == p_pattern && root) - return OK; + _pattern_info(PCRE2_INFO_CAPTURECOUNT, &count); - clear(); - pattern = p_pattern; - group_names.push_back(0); - RegExNodeGroup *root_group = memnew(RegExNodeCapturing(0)); - root = root_group; - Vector<RegExNodeGroup *> stack; - stack.push_back(root_group); - int lookahead_level = 0; - int numeric_groups = 0; - const int numeric_max = 9; - - for (const CharType *c = p_pattern.c_str(); *c != '\0'; ++c) { - - switch (c[0]) { - case '(': - if (c[1] == '?') { - - RegExNodeGroup *group = NULL; - switch (c[2]) { - case ':': - c = &c[2]; - group = memnew(RegExNodeGroup()); - break; - case '!': - case '=': - group = memnew(RegExNodeLookAhead((c[2] == '!'), lookahead_level++)); - if (lookahead_depth < lookahead_level) - lookahead_depth = lookahead_level; - c = &c[2]; - break; - case '<': - if (c[3] == '!' || c[3] == '=') { - group = memnew(RegExNodeLookBehind((c[3] == '!'), lookahead_level++)); - c = &c[3]; - } - break; - case 'P': - if (c[3] == '<') { - const CharType *d = &c[3]; - Variant name = RegExNodeCapturing::parse_name(d, false); - if (name == Variant(-1)) - REGEX_COMPILE_FAIL("unrecognised character for group name"); - group = memnew(RegExNodeCapturing(group_names.size())); - group_names.push_back(name); - c = d; - } - default: - break; - } - if (!group) - REGEX_COMPILE_FAIL("unrecognised qualifier for group"); - stack[0]->add_child(group); - stack.insert(0, group); - - } else if (numeric_groups < numeric_max) { - - RegExNodeCapturing *group = memnew(RegExNodeCapturing(group_names.size())); - group_names.push_back(++numeric_groups); - stack[0]->add_child(group); - stack.insert(0, group); - - } else { - - RegExNodeGroup *group = memnew(RegExNodeGroup()); - stack[0]->add_child(group); - stack.insert(0, group); - } - break; - case ')': - if (stack.size() == 1) - REGEX_COMPILE_FAIL("unexpected ')'"); - stack.remove(0); - break; - case '\\': - if (('1' <= c[1] && c[1] <= '9') || (c[1] == 'g' && c[2] == '{')) { - - int ref = 0; - bool unclosed = false; - - if (c[1] == 'g') { - unclosed = true; - c = &c[2]; - } - - while ('0' <= c[1] && c[1] <= '9') { - ref = ref * 10 + int(c[1] - '0'); - ++c; - } - - if (unclosed) { - if (c[1] != '}') - REGEX_COMPILE_FAIL("unclosed backreference '{'"); - ++c; - } - - if (ref > numeric_groups || ref <= 0) - REGEX_COMPILE_FAIL("backreference not found"); - - for (int i = 0; i < stack.size(); ++i) - if (stack[i]->is_look_behind()) - REGEX_COMPILE_FAIL("backreferences inside lookbehind not supported"); - - for (int i = 0; i < group_names.size(); ++i) { - if (group_names[i].get_type() == Variant::INT && int(group_names[i]) == ref) { - ref = group_names[i]; - break; - } - } - - stack[0]->add_child(memnew(RegExNodeBackReference(ref))); - } - if (c[1] == 'g' && c[2] == '<') { - - const CharType *d = &c[2]; - - Variant name = RegExNodeCapturing::parse_name(d, true); - if (name == Variant(-1)) - REGEX_COMPILE_FAIL("unrecognised character for group name"); - - c = d; - - for (int i = 0; i < stack.size(); ++i) - if (stack[i]->is_look_behind()) - REGEX_COMPILE_FAIL("backreferences inside lookbehind not supported"); - - int ref = -1; - - for (int i = 0; i < group_names.size(); ++i) { - if (group_names[i].get_type() == Variant::INT && int(group_names[i]) == ref) { - ref = group_names[i]; - break; - } - } - - if (ref == -1) - REGEX_COMPILE_FAIL("backreference not found"); - - stack[0]->add_child(memnew(RegExNodeBackReference(ref))); - - } else if (c[1] == 'b' || c[1] == 'B') { - - stack[0]->add_child(memnew(RegExNodeWordBoundary(*(++c) == 'B'))); - - } else if (RegEx_is_shorthand(c[1])) { - - stack[0]->add_child(memnew(RegExNodeShorthand(*(++c)))); - - } else { - - const CharType *d = c; - CharType ch = RegExNodeChar::parse_escape(d); - if (c == d) - REGEX_COMPILE_FAIL("invalid escape token"); - stack[0]->add_child(memnew(RegExNodeChar(ch))); - c = d; - } - break; - case '[': { - RegExNodeBracket *bracket = memnew(RegExNodeBracket()); - stack[0]->add_child(bracket); - if (c[1] == '^') { - bracket->inverse = true; - ++c; - } - bool first_child = true; - CharType previous_child; - bool previous_child_single = false; - while (true) { - ++c; - if (!first_child && c[0] == ']') { - - break; - - } else if (c[0] == '\0') { - - REGEX_COMPILE_FAIL("unclosed bracket expression '['"); - - } else if (c[0] == '\\') { - - if (RegEx_is_shorthand(c[1])) { - bracket->add_child(memnew(RegExNodeShorthand(*(++c)))); - } else { - const CharType *d = c; - CharType ch = RegExNodeChar::parse_escape(d); - if (c == d) - REGEX_COMPILE_FAIL("invalid escape token"); - bracket->add_child(memnew(RegExNodeChar(ch))); - c = d; - previous_child = ch; - previous_child_single = true; - } - - } else if (c[0] == ']' && c[1] == ':') { - - const CharType *d = &c[2]; - RegExNodeClass::Type type = RegExNodeClass::parse_type(d); - if (type != RegExNodeClass::Type_none) { - - c = d; - previous_child_single = false; - - } else { - - bracket->add_child(memnew(RegExNodeChar('['))); - previous_child = '['; - previous_child_single = true; - } - } else if (previous_child_single && c[0] == '-') { - - if (c[1] != '\0' && c[1] != ']') { - - CharType next; - - if (c[1] == '\\') { - const CharType *d = ++c; - next = RegExNodeChar::parse_escape(d); - if (c == d) - REGEX_COMPILE_FAIL("invalid escape token"); - } else { - next = *(++c); - } - - if (next < previous_child) - REGEX_COMPILE_FAIL("text range out of order"); - - bracket->pop_back(); - bracket->add_child(memnew(RegExNodeRange(previous_child, next))); - previous_child_single = false; - } else { - - bracket->add_child(memnew(RegExNodeChar('-'))); - previous_child = '-'; - previous_child_single = true; - } - } else { - - bracket->add_child(memnew(RegExNodeChar(c[0]))); - previous_child = c[0]; - previous_child_single = true; - } - first_child = false; - } - } break; - case '|': - for (int i = 0; i < stack.size(); ++i) - if (stack[i]->is_look_behind()) - REGEX_COMPILE_FAIL("alternations inside lookbehind not supported"); - stack[0]->add_childset(); - break; - case '^': - stack[0]->add_child(memnew(RegExNodeAnchorStart())); - break; - case '$': - stack[0]->add_child(memnew(RegExNodeAnchorEnd())); - break; - case '.': - stack[0]->add_child(memnew(RegExNodeShorthand('.'))); - break; - case '?': - case '*': - case '+': - case '{': { - int min_val = 0; - int max_val = -1; - bool valid = true; - const CharType *d = c; - bool max_set = true; - switch (c[0]) { - case '?': - min_val = 0; - max_val = 1; - break; - case '*': - min_val = 0; - max_val = -1; - break; - case '+': - min_val = 1; - max_val = -1; - break; - case '{': - max_set = false; - while (valid) { - ++d; - if (d[0] == '}') { - break; - } else if (d[0] == ',') { - max_set = true; - } else if ('0' <= d[0] && d[0] <= '9') { - if (max_set) { - if (max_val < 0) - max_val = int(d[0] - '0'); - else - max_val = max_val * 10 + int(d[0] - '0'); - } else { - min_val = min_val * 10 + int(d[0] - '0'); - } - } else { - valid = false; - } - } - break; - default: - break; - } - - if (!max_set) - max_val = min_val; - - if (valid) { - - c = d; - - if (stack[0]->back == NULL || !stack[0]->back->quantifiable) - REGEX_COMPILE_FAIL("element not quantifiable"); - - if (min_val != max_val) - for (int i = 0; i < stack.size(); ++i) - if (stack[i]->is_look_behind()) - REGEX_COMPILE_FAIL("variable length quantifiers inside lookbehind not supported"); - - RegExNodeQuantifier *quant = memnew(RegExNodeQuantifier(min_val, max_val)); - quant->child = stack[0]->swap_back(quant); - quant->child->previous = NULL; - quant->child->parent = quant; - - if (min_val == max_val && quant->child->length >= 0) - quant->length = max_val * quant->child->length; - - if (c[1] == '?') { - quant->greedy = false; - ++c; - } - break; - } - } - default: - stack[0]->add_child(memnew(RegExNodeChar(c[0]))); - break; - } - } - if (stack.size() > 1) - REGEX_COMPILE_FAIL("unclosed group '('"); - return OK; + return count; } -Ref<RegExMatch> RegEx::search(const String &p_text, int p_start, int p_end) const { - - ERR_FAIL_COND_V(!is_valid(), NULL); - ERR_FAIL_COND_V(p_start < 0, NULL); - ERR_FAIL_COND_V(p_start >= p_text.length(), NULL); - ERR_FAIL_COND_V(p_end > p_text.length(), NULL); - ERR_FAIL_COND_V(p_end != -1 && p_end < p_start, NULL); +Array RegEx::get_names() const { - Ref<RegExMatch> res = memnew(RegExMatch()); + Array result; - for (int i = 0; i < group_names.size(); ++i) { - RegExMatch::Group group; - group.name = group_names[i]; - res->captures.push_back(group); - } + ERR_FAIL_COND_V(!is_valid(), result); - res->string = p_text; + uint32_t count; + const CharType *table; + uint32_t entry_size; - if (p_end == -1) - p_end = p_text.length(); + _pattern_info(PCRE2_INFO_NAMECOUNT, &count); + _pattern_info(PCRE2_INFO_NAMETABLE, &table); + _pattern_info(PCRE2_INFO_NAMEENTRYSIZE, &entry_size); - RegExSearch s(res, p_end, lookahead_depth); + for (uint32_t i = 0; i < count; i++) { - for (int i = p_start; i <= s.end; ++i) { - for (int c = 0; c < group_names.size(); ++c) { - res->captures[c].start = -1; - res->captures[c].length = 0; + String name = &table[i * entry_size + 1]; + if (result.find(name) < 0) { + result.append(name); } - if (root->test(s, i) >= 0) - break; } - if (res->captures[0].start >= 0) - return res; - return NULL; + return result; } -String RegEx::sub(const String &p_text, const String &p_replacement, bool p_all, int p_start, int p_end) const { - - ERR_FAIL_COND_V(!is_valid(), p_text); - ERR_FAIL_COND_V(p_start < 0, p_text); - ERR_FAIL_COND_V(p_start >= p_text.length(), p_text); - ERR_FAIL_COND_V(p_end > p_text.length(), p_text); - ERR_FAIL_COND_V(p_end != -1 && p_end < p_start, p_text); - - String text = p_text; - int start = p_start; - - if (p_end == -1) - p_end = p_text.length(); - - while (start < text.length() && (p_all || start == p_start)) { - - Ref<RegExMatch> m = search(text, start, p_end); - - RegExMatch::Group &s = m->captures[0]; - - if (s.start < 0) - break; - - String res = text.substr(0, s.start) + m->expand(p_replacement); - - start = res.length(); +RegEx::RegEx() { - if (s.length == 0) - ++start; + if (sizeof(CharType) == 2) { - int sub_end = s.start + s.length; - if (sub_end < text.length()) - res += text.substr(sub_end, text.length() - sub_end); + general_ctx = pcre2_general_context_create_16(&_regex_malloc, &_regex_free, NULL); - p_end += res.length() - text.length(); + } else { - text = res; + general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, NULL); } - return text; + code = NULL; } -void RegEx::clear() { - - if (root) - memdelete(root); - - root = NULL; - group_names.clear(); - lookahead_depth = 0; -} - -bool RegEx::is_valid() const { - - return (root != NULL); -} - -String RegEx::get_pattern() const { - - return pattern; -} - -int RegEx::get_group_count() const { - - int count = 0; - for (int i = 1; i < group_names.size(); ++i) - if (group_names[i].get_type() == Variant::INT) - ++count; - return count; -} - -Array RegEx::get_names() const { +RegEx::RegEx(const String &p_pattern) { - Array res; - for (int i = 1; i < group_names.size(); ++i) - if (group_names[i].get_type() == Variant::STRING) - res.push_back(group_names[i]); - return res; -} + if (sizeof(CharType) == 2) { -RegEx::RegEx() { + general_ctx = pcre2_general_context_create_16(&_regex_malloc, &_regex_free, NULL); - root = NULL; - lookahead_depth = 0; -} + } else { -RegEx::RegEx(const String &p_pattern) { - - root = NULL; + general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, NULL); + } + code = NULL; compile(p_pattern); } RegEx::~RegEx() { - if (root) - memdelete(root); -} + if (sizeof(CharType) == 2) { -void RegExMatch::_bind_methods() { + if (code) + pcre2_code_free_16((pcre2_code_16 *)code); + pcre2_general_context_free_16((pcre2_general_context_16 *)general_ctx); - ClassDB::bind_method(D_METHOD("expand", "template"), &RegExMatch::expand); - ClassDB::bind_method(D_METHOD("get_group_count"), &RegExMatch::get_group_count); - ClassDB::bind_method(D_METHOD("get_group_array"), &RegExMatch::get_group_array); - ClassDB::bind_method(D_METHOD("get_names"), &RegExMatch::get_names); - ClassDB::bind_method(D_METHOD("get_name_dict"), &RegExMatch::get_name_dict); - ClassDB::bind_method(D_METHOD("get_string", "name"), &RegExMatch::get_string, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("get_start", "name"), &RegExMatch::get_start, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("get_end", "name"), &RegExMatch::get_end, DEFVAL(0)); + } else { + + if (code) + pcre2_code_free_32((pcre2_code_32 *)code); + pcre2_general_context_free_32((pcre2_general_context_32 *)general_ctx); + } } void RegEx::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &RegEx::clear); ClassDB::bind_method(D_METHOD("compile", "pattern"), &RegEx::compile); - ClassDB::bind_method(D_METHOD("search", "text", "start", "end"), &RegEx::search, DEFVAL(0), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("sub", "text", "replacement", "all", "start", "end"), &RegEx::sub, DEFVAL(false), DEFVAL(0), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("search", "subject", "offset", "end"), &RegEx::search, DEFVAL(0), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("sub", "subject", "replacement", "all", "offset", "end"), &RegEx::sub, DEFVAL(false), DEFVAL(0), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("is_valid"), &RegEx::is_valid); ClassDB::bind_method(D_METHOD("get_pattern"), &RegEx::get_pattern); ClassDB::bind_method(D_METHOD("get_group_count"), &RegEx::get_group_count); ClassDB::bind_method(D_METHOD("get_names"), &RegEx::get_names); - - ADD_PROPERTY(PropertyInfo(Variant::STRING, "pattern"), "compile", "get_pattern"); } diff --git a/modules/regex/regex.h b/modules/regex/regex.h index 8c76035b82..bfa9c84042 100644 --- a/modules/regex/regex.h +++ b/modules/regex/regex.h @@ -31,59 +31,53 @@ #ifndef REGEX_H #define REGEX_H +#include "core/array.h" #include "core/dictionary.h" +#include "core/map.h" #include "core/reference.h" -#include "core/resource.h" #include "core/ustring.h" #include "core/vector.h" -class RegExNode; - class RegExMatch : public Reference { GDCLASS(RegExMatch, Reference); - struct Group { - Variant name; + struct Range { int start; - int length; + int end; }; - Vector<Group> captures; - String string; + String subject; + Vector<Range> data; + Map<String, int> names; friend class RegEx; - friend class RegExSearch; - friend class RegExNodeCapturing; - friend class RegExNodeBackReference; protected: static void _bind_methods(); -public: - String expand(const String &p_template) const; + int _find(const Variant &p_name) const; +public: + String get_subject() const; int get_group_count() const; - Array get_group_array() const; - - Array get_names() const; - Dictionary get_name_dict() const; + Dictionary get_names() const; + Array get_strings() const; String get_string(const Variant &p_name) const; int get_start(const Variant &p_name) const; int get_end(const Variant &p_name) const; - - RegExMatch(); }; -class RegEx : public Resource { +class RegEx : public Reference { - GDCLASS(RegEx, Resource); + GDCLASS(RegEx, Reference); - RegExNode *root; - Vector<Variant> group_names; + void *general_ctx; + void *code; String pattern; - int lookahead_depth; + + void _pattern_info(uint32_t what, void *where) const; protected: static void _bind_methods(); @@ -91,9 +85,10 @@ protected: public: void clear(); Error compile(const String &p_pattern); + void _init(const String &p_pattern = ""); - Ref<RegExMatch> search(const String &p_text, int p_start = 0, int p_end = -1) const; - String sub(const String &p_text, const String &p_replacement, bool p_all = false, int p_start = 0, int p_end = -1) const; + Ref<RegExMatch> search(const String &p_subject, int p_offset = 0, int p_end = -1) const; + String sub(const String &p_subject, const String &p_replacement, bool p_all = false, int p_offset = 0, int p_end = -1) const; bool is_valid() const; String get_pattern() const; diff --git a/modules/squish/SCsub b/modules/squish/SCsub index cca7c038f1..127f22d798 100644 --- a/modules/squish/SCsub +++ b/modules/squish/SCsub @@ -6,7 +6,7 @@ Import('env_modules') env_squish = env_modules.Clone() # Thirdparty source files -if (env['builtin_squish'] != 'no'): +if env['builtin_squish']: thirdparty_dir = "#thirdparty/squish/" thirdparty_sources = [ "alpha.cpp", diff --git a/modules/squish/config.py b/modules/squish/config.py index cc8f098010..9b7729bda4 100644 --- a/modules/squish/config.py +++ b/modules/squish/config.py @@ -6,6 +6,6 @@ def can_build(platform): def configure(env): # Tools only, disabled for non-tools # TODO: Find a cleaner way to achieve that - if (env["tools"] == "no"): - env["module_squish_enabled"] = "no" + if not env['tools']: + env['module_squish_enabled'] = False env.disabled_modules.append("squish") diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_compress_squish.cpp index 29073a8499..072f18b990 100644 --- a/modules/squish/image_compress_squish.cpp +++ b/modules/squish/image_compress_squish.cpp @@ -91,7 +91,7 @@ void image_compress_squish(Image *p_image, Image::CompressSource p_source) { if (p_image->get_format() <= Image::FORMAT_RGBA8) { int squish_comp = squish::kColourRangeFit; - Image::Format target_format; + Image::Format target_format = Image::FORMAT_RGBA8; Image::DetectChannels dc = p_image->get_detected_channels(); @@ -140,6 +140,10 @@ void image_compress_squish(Image *p_image, Image::CompressSource p_source) { squish_comp |= squish::kDxt5; } break; + default: { + ERR_PRINT("Unknown image format, defaulting to RGBA8"); + break; + } } PoolVector<uint8_t> data; diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp index 49d959203c..5c252bda86 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp @@ -31,7 +31,10 @@ #include "os/file_access.h" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #include "thirdparty/misc/stb_vorbis.c" +#pragma GCC diagnostic pop void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) { @@ -42,6 +45,12 @@ void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fra while (todo && active) { int mixed = stb_vorbis_get_samples_float_interleaved(ogg_stream, 2, (float *)p_buffer, todo * 2); + if (vorbis_stream->channels == 1 && mixed > 0) { + //mix mono to stereo + for (int i = 0; i < mixed; i++) { + p_buffer[i].r = p_buffer[i].l; + } + } todo -= mixed; frames_mixed += mixed; @@ -49,7 +58,7 @@ void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fra //end of file! if (vorbis_stream->loop) { //loop - seek_pos(vorbis_stream->loop_offset); + seek(vorbis_stream->loop_offset); loops++; } else { for (int i = mixed; i < p_frames; i++) { @@ -69,7 +78,7 @@ float AudioStreamPlaybackOGGVorbis::get_stream_sampling_rate() { void AudioStreamPlaybackOGGVorbis::start(float p_from_pos) { active = true; - seek_pos(p_from_pos); + seek(p_from_pos); loops = 0; _begin_resample(); } @@ -88,11 +97,11 @@ int AudioStreamPlaybackOGGVorbis::get_loop_count() const { return loops; } -float AudioStreamPlaybackOGGVorbis::get_pos() const { +float AudioStreamPlaybackOGGVorbis::get_playback_position() const { return float(frames_mixed) / vorbis_stream->sample_rate; } -void AudioStreamPlaybackOGGVorbis::seek_pos(float p_time) { +void AudioStreamPlaybackOGGVorbis::seek(float p_time) { if (!active) return; @@ -112,15 +121,14 @@ float AudioStreamPlaybackOGGVorbis::get_length() const { AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() { if (ogg_alloc.alloc_buffer) { - AudioServer::get_singleton()->audio_data_free(ogg_alloc.alloc_buffer); stb_vorbis_close(ogg_stream); + AudioServer::get_singleton()->audio_data_free(ogg_alloc.alloc_buffer); } } Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instance_playback() { Ref<AudioStreamPlaybackOGGVorbis> ovs; - printf("instance at %p, data %p\n", this, data); ERR_FAIL_COND_V(data == NULL, ovs); @@ -199,8 +207,6 @@ void AudioStreamOGGVorbis::set_data(const PoolVector<uint8_t> &p_data) { break; } } - - printf("create at %p, data %p\n", this, data); } PoolVector<uint8_t> AudioStreamOGGVorbis::get_data() const { diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.h b/modules/stb_vorbis/audio_stream_ogg_vorbis.h index a459e6f31d..f4d381897b 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.h +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.h @@ -34,7 +34,10 @@ #include "servers/audio/audio_stream.h" #define STB_VORBIS_HEADER_ONLY +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #include "thirdparty/misc/stb_vorbis.c" +#pragma GCC diagnostic pop #undef STB_VORBIS_HEADER_ONLY class AudioStreamOGGVorbis; @@ -64,8 +67,8 @@ public: virtual int get_loop_count() const; //times it looped - virtual float get_pos() const; - virtual void seek_pos(float p_time); + virtual float get_playback_position() const; + virtual void seek(float p_time); virtual float get_length() const; //if supported, otherwise return 0 diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index 086cc202f9..9c198ea98e 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -47,7 +47,55 @@ SVGRasterizer::~SVGRasterizer() { SVGRasterizer ImageLoaderSVG::rasterizer; -Error ImageLoaderSVG::_create_image(Ref<Image> p_image, const PoolVector<uint8_t> *p_data, float p_scale, bool upsample) { +inline void change_nsvg_paint_color(NSVGpaint *p_paint, const uint32_t p_old, const uint32_t p_new) { + + if (p_paint->type == NSVG_PAINT_COLOR) { + if (p_paint->color << 8 == p_old << 8) { + p_paint->color = (p_paint->color & 0xFF000000) | (p_new & 0x00FFFFFF); + } + } + + if (p_paint->type == NSVG_PAINT_LINEAR_GRADIENT || p_paint->type == NSVG_PAINT_RADIAL_GRADIENT) { + for (int stop_index = 0; stop_index < p_paint->gradient->nstops; stop_index++) { + if (p_paint->gradient->stops[stop_index].color << 8 == p_old << 8) { + p_paint->gradient->stops[stop_index].color = p_new; + } + } + } +} + +void ImageLoaderSVG::_convert_colors(NSVGimage *p_svg_image) { + + for (NSVGshape *shape = p_svg_image->shapes; shape != NULL; shape = shape->next) { + + for (int i = 0; i < replace_colors.old_colors.size(); i++) { + change_nsvg_paint_color(&(shape->stroke), replace_colors.old_colors[i], replace_colors.new_colors[i]); + change_nsvg_paint_color(&(shape->fill), replace_colors.old_colors[i], replace_colors.new_colors[i]); + } + } +} + +void ImageLoaderSVG::set_convert_colors(Dictionary *p_replace_color) { + + if (p_replace_color) { + Dictionary replace_color = *p_replace_color; + for (int i = 0; i < replace_color.keys().size(); i++) { + Variant o_c = replace_color.keys()[i]; + Variant n_c = replace_color[replace_color.keys()[i]]; + if (o_c.get_type() == Variant::COLOR && n_c.get_type() == Variant::COLOR) { + Color old_color = o_c; + Color new_color = n_c; + replace_colors.old_colors.push_back(old_color.to_abgr32()); + replace_colors.new_colors.push_back(new_color.to_abgr32()); + } + } + } else { + replace_colors.old_colors.clear(); + replace_colors.new_colors.clear(); + } +} + +Error ImageLoaderSVG::_create_image(Ref<Image> p_image, const PoolVector<uint8_t> *p_data, float p_scale, bool upsample, bool convert_colors) { NSVGimage *svg_image; PoolVector<uint8_t>::Read src_r = p_data->read(); svg_image = nsvgParse((char *)src_r.ptr(), "px", 96); @@ -55,6 +103,8 @@ Error ImageLoaderSVG::_create_image(Ref<Image> p_image, const PoolVector<uint8_t ERR_PRINT("SVG Corrupted"); return ERR_FILE_CORRUPT; } + if (convert_colors) + _convert_colors(svg_image); float upscale = upsample ? 2.0 : 1.0; @@ -78,15 +128,15 @@ Error ImageLoaderSVG::_create_image(Ref<Image> p_image, const PoolVector<uint8_t return OK; } -Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, const char *svg_str, float p_scale, bool upsample) { +Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, const char *p_svg_str, float p_scale, bool upsample, bool convert_colors) { - size_t str_len = strlen(svg_str); + size_t str_len = strlen(p_svg_str); PoolVector<uint8_t> src_data; src_data.resize(str_len + 1); PoolVector<uint8_t>::Write src_w = src_data.write(); - memcpy(src_w.ptr(), svg_str, str_len + 1); + memcpy(src_w.ptr(), p_svg_str, str_len + 1); - return _create_image(p_image, &src_data, p_scale, upsample); + return _create_image(p_image, &src_data, p_scale, upsample, convert_colors); } Error ImageLoaderSVG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) { @@ -109,3 +159,5 @@ void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const ImageLoaderSVG::ImageLoaderSVG() { } + +ImageLoaderSVG::ReplaceColors ImageLoaderSVG::replace_colors; diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h index 1f2ec3c1c2..cf44cd8c50 100644 --- a/modules/svg/image_loader_svg.h +++ b/modules/svg/image_loader_svg.h @@ -52,16 +52,21 @@ public: }; class ImageLoaderSVG : public ImageFormatLoader { - + static struct ReplaceColors { + List<uint32_t> old_colors; + List<uint32_t> new_colors; + } replace_colors; static SVGRasterizer rasterizer; - static Error _create_image(Ref<Image> p_image, const PoolVector<uint8_t> *p_data, float p_scale, bool upsample); + static void _convert_colors(NSVGimage *p_svg_image); + static Error _create_image(Ref<Image> p_image, const PoolVector<uint8_t> *p_data, float p_scale, bool upsample, bool convert_colors = false); public: - static Error create_image_from_string(Ref<Image> p_image, const char *p_svg_str, float p_scale, bool upsample); + static void set_convert_colors(Dictionary *p_replace_color = NULL); + static Error create_image_from_string(Ref<Image> p_image, const char *p_svg_str, float p_scale, bool upsample, bool convert_colors = false); virtual Error load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale); virtual void get_recognized_extensions(List<String> *p_extensions) const; ImageLoaderSVG(); }; -#endif
\ No newline at end of file +#endif diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp index 379c894550..d7a1ce7308 100644 --- a/modules/tga/image_loader_tga.cpp +++ b/modules/tga/image_loader_tga.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_loader_jpegd.cpp */ +/* image_loader_tga.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -53,19 +53,19 @@ Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t count = (c & 0x7f) + 1; if (c & 0x80) { - for (int i = 0; i < p_pixel_size; i++) { + for (size_t i = 0; i < p_pixel_size; i++) { pixels_w.ptr()[i] = p_compressed_buffer[compressed_pos]; compressed_pos += 1; } - for (int i = 0; i < count; i++) { - for (int j = 0; j < p_pixel_size; j++) { + for (size_t i = 0; i < count; i++) { + for (size_t j = 0; j < p_pixel_size; j++) { p_uncompressed_buffer[output_pos + j] = pixels_w.ptr()[j]; } output_pos += p_pixel_size; } } else { count *= p_pixel_size; - for (int i = 0; i < count; i++) { + for (size_t i = 0; i < count; i++) { p_uncompressed_buffer[output_pos] = p_compressed_buffer[compressed_pos]; compressed_pos += 1; output_pos += 1; @@ -208,7 +208,7 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force PoolVector<uint8_t> src_image; int src_image_len = f->get_len(); ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT); - ERR_FAIL_COND_V(src_image_len < sizeof(tga_header_s), ERR_FILE_CORRUPT); + ERR_FAIL_COND_V(src_image_len < (int)sizeof(tga_header_s), ERR_FILE_CORRUPT); src_image.resize(src_image_len); Error err = OK; @@ -253,7 +253,7 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force err = FAILED; if (err == OK) { - f->seek(f->get_pos() + tga_header.id_length); + f->seek(f->get_position() + tga_header.id_length); PoolVector<uint8_t> palette; @@ -269,7 +269,7 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force } PoolVector<uint8_t>::Write src_image_w = src_image.write(); - f->get_buffer(&src_image_w[0], src_image_len - f->get_pos()); + f->get_buffer(&src_image_w[0], src_image_len - f->get_position()); PoolVector<uint8_t>::Read src_image_r = src_image.read(); diff --git a/modules/tga/image_loader_tga.h b/modules/tga/image_loader_tga.h index 8689a1773b..7905ab37a7 100644 --- a/modules/tga/image_loader_tga.h +++ b/modules/tga/image_loader_tga.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_loader_jpegd.h */ +/* image_loader_tga.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/modules/theora/SCsub b/modules/theora/SCsub index 2de4d29640..9015c2c354 100644 --- a/modules/theora/SCsub +++ b/modules/theora/SCsub @@ -6,7 +6,7 @@ Import('env_modules') env_theora = env_modules.Clone() # Thirdparty source files -if (env['builtin_libtheora'] != 'no'): +if env['builtin_libtheora']: thirdparty_dir = "#thirdparty/libtheora/" thirdparty_sources = [ #"analyze.c", @@ -74,9 +74,9 @@ if (env['builtin_libtheora'] != 'no'): env_theora.Append(CPPPATH=[thirdparty_dir]) # also requires libogg and libvorbis - if (env['builtin_libogg'] != 'no'): + if env['builtin_libogg']: env_theora.Append(CPPPATH=["#thirdparty/libogg"]) - if (env['builtin_libvorbis'] != 'no'): + if env['builtin_libvorbis']: env_theora.Append(CPPPATH=["#thirdparty/libvorbis"]) # Godot source files diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 2a24f8d4d1..c75bec31df 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -414,7 +414,8 @@ void VideoStreamPlaybackTheora::update(float p_delta) { bool buffer_full = false; /* if there's pending, decoded audio, grab it */ - if ((ret = vorbis_synthesis_pcmout(&vd, &pcm)) > 0) { + ret = vorbis_synthesis_pcmout(&vd, &pcm); + if (ret > 0) { const int AUXBUF_LEN = 4096; int to_read = ret; @@ -633,12 +634,12 @@ int VideoStreamPlaybackTheora::get_loop_count() const { return 0; }; -float VideoStreamPlaybackTheora::get_pos() const { +float VideoStreamPlaybackTheora::get_playback_position() const { return get_time(); }; -void VideoStreamPlaybackTheora::seek_pos(float p_time){ +void VideoStreamPlaybackTheora::seek(float p_time){ // no }; diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index f04e49c662..484a1a7fb9 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -140,8 +140,8 @@ public: virtual int get_loop_count() const; - virtual float get_pos() const; - virtual void seek_pos(float p_time); + virtual float get_playback_position() const; + virtual void seek(float p_time); void set_file(const String &p_file); diff --git a/modules/tinyexr/config.py b/modules/tinyexr/config.py index 2e4b96a6b0..3e16fd725e 100644 --- a/modules/tinyexr/config.py +++ b/modules/tinyexr/config.py @@ -6,6 +6,6 @@ def can_build(platform): def configure(env): # Tools only, disabled for non-tools # TODO: Find a cleaner way to achieve that - if (env["tools"] == "no"): - env["module_tinyexr_enabled"] = "no" + if not env['tools']: + env['module_tinyexr_enabled'] = False env.disabled_modules.append("tinyexr") diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp index 57826d69fb..49a4db237a 100644 --- a/modules/tinyexr/image_loader_tinyexr.cpp +++ b/modules/tinyexr/image_loader_tinyexr.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_loader_jpegd.cpp */ +/* image_loader_tinyexr.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/modules/tinyexr/image_loader_tinyexr.h b/modules/tinyexr/image_loader_tinyexr.h index adecba5d9d..53a81597af 100644 --- a/modules/tinyexr/image_loader_tinyexr.h +++ b/modules/tinyexr/image_loader_tinyexr.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_loader_jpegd.h */ +/* image_loader_tinyexr.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 88012d2031..765fe4c2f2 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -125,6 +125,7 @@ void VisualScriptNode::_bind_methods() { ClassDB::bind_method(D_METHOD("get_visual_script"), &VisualScriptNode::get_visual_script); ClassDB::bind_method(D_METHOD("set_default_input_value", "port_idx", "value"), &VisualScriptNode::set_default_input_value); ClassDB::bind_method(D_METHOD("get_default_input_value", "port_idx"), &VisualScriptNode::get_default_input_value); + ClassDB::bind_method(D_METHOD("ports_changed_notify"), &VisualScriptNode::ports_changed_notify); ClassDB::bind_method(D_METHOD("_set_default_input_values", "values"), &VisualScriptNode::_set_default_input_values); ClassDB::bind_method(D_METHOD("_get_default_input_values"), &VisualScriptNode::_get_default_input_values); @@ -423,7 +424,7 @@ Ref<VisualScriptNode> VisualScript::get_node(const StringName &p_func, int p_id) return func.nodes[p_id].node; } -void VisualScript::set_node_pos(const StringName &p_func, int p_id, const Point2 &p_pos) { +void VisualScript::set_node_position(const StringName &p_func, int p_id, const Point2 &p_pos) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!functions.has(p_func)); @@ -433,7 +434,7 @@ void VisualScript::set_node_pos(const StringName &p_func, int p_id, const Point2 func.nodes[p_id].pos = p_pos; } -Point2 VisualScript::get_node_pos(const StringName &p_func, int p_id) const { +Point2 VisualScript::get_node_position(const StringName &p_func, int p_id) const { ERR_FAIL_COND_V(!functions.has(p_func), Point2()); const Function &func = functions[p_func]; @@ -973,11 +974,6 @@ bool VisualScript::is_tool() const { return false; } -String VisualScript::get_node_type() const { - - return String(); -} - ScriptLanguage *VisualScript::get_language() const { return VisualScriptLanguage::singleton; @@ -1273,14 +1269,14 @@ void VisualScript::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function_scroll", "name", "ofs"), &VisualScript::set_function_scroll); ClassDB::bind_method(D_METHOD("get_function_scroll", "name"), &VisualScript::get_function_scroll); - ClassDB::bind_method(D_METHOD("add_node", "func", "id", "node", "pos"), &VisualScript::add_node, DEFVAL(Point2())); + ClassDB::bind_method(D_METHOD("add_node", "func", "id", "node", "position"), &VisualScript::add_node, DEFVAL(Point2())); ClassDB::bind_method(D_METHOD("remove_node", "func", "id"), &VisualScript::remove_node); ClassDB::bind_method(D_METHOD("get_function_node_id", "name"), &VisualScript::get_function_node_id); ClassDB::bind_method(D_METHOD("get_node", "func", "id"), &VisualScript::get_node); ClassDB::bind_method(D_METHOD("has_node", "func", "id"), &VisualScript::has_node); - ClassDB::bind_method(D_METHOD("set_node_pos", "func", "id", "pos"), &VisualScript::set_node_pos); - ClassDB::bind_method(D_METHOD("get_node_pos", "func", "id"), &VisualScript::get_node_pos); + ClassDB::bind_method(D_METHOD("set_node_position", "func", "id", "position"), &VisualScript::set_node_position); + ClassDB::bind_method(D_METHOD("get_node_position", "func", "id"), &VisualScript::get_node_position); ClassDB::bind_method(D_METHOD("sequence_connect", "func", "from_node", "from_output", "to_node"), &VisualScript::sequence_connect); ClassDB::bind_method(D_METHOD("sequence_disconnect", "func", "from_node", "from_output", "to_node"), &VisualScript::sequence_disconnect); @@ -2008,8 +2004,8 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o Node *node = Object::cast_to<Node>(p_owner); if (p_script->functions.has("_process")) node->set_process(true); - if (p_script->functions.has("_fixed_process")) - node->set_fixed_process(true); + if (p_script->functions.has("_physics_process")) + node->set_physics_process(true); if (p_script->functions.has("_input")) node->set_process_input(true); if (p_script->functions.has("_unhandled_input")) @@ -2411,6 +2407,10 @@ bool VisualScriptLanguage::has_named_classes() const { return false; } +bool VisualScriptLanguage::supports_builtin_mode() const { + + return true; +} int VisualScriptLanguage::find_function(const String &p_function, const String &p_code) const { return -1; diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index b2e7a6aa27..0f60b103c9 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -278,8 +278,8 @@ public: void remove_node(const StringName &p_func, int p_id); bool has_node(const StringName &p_func, int p_id) const; Ref<VisualScriptNode> get_node(const StringName &p_func, int p_id) const; - void set_node_pos(const StringName &p_func, int p_id, const Point2 &p_pos); - Point2 get_node_pos(const StringName &p_func, int p_id) const; + void set_node_position(const StringName &p_func, int p_id, const Point2 &p_pos); + Point2 get_node_position(const StringName &p_func, int p_id) const; void get_node_list(const StringName &p_func, List<int> *r_nodes) const; void sequence_connect(const StringName &p_func, int p_from_node, int p_from_output, int p_to_node); @@ -339,8 +339,6 @@ public: virtual bool is_tool() const; - virtual String get_node_type() const; - virtual ScriptLanguage *get_language() const; virtual bool has_script_signal(const StringName &p_signal) const; @@ -569,9 +567,9 @@ public: virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const; virtual Script *create_script() const; virtual bool has_named_classes() const; + virtual bool supports_builtin_mode() const; virtual int find_function(const String &p_function, const String &p_code) const; virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const; - virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return ERR_UNAVAILABLE; } virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const; virtual void add_global_constant(const StringName &p_variable, const Variant &p_value); diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index 2c8796820e..4b294325dc 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -65,6 +65,8 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX "decimals", "stepify", "lerp", + "inverse_lerp", + "range_lerp", "dectime", "randomize", "randi", @@ -194,9 +196,12 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) { case COLORN: return 2; case MATH_LERP: + case MATH_INVERSE_LERP: case MATH_DECTIME: case LOGIC_CLAMP: return 3; + case MATH_RANGE_LERP: + return 5; case FUNC_MAX: { } } @@ -297,7 +302,26 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const return PropertyInfo(Variant::REAL, "to"); else return PropertyInfo(Variant::REAL, "weight"); - + } break; + case MATH_INVERSE_LERP: { + if (p_idx == 0) + return PropertyInfo(Variant::REAL, "from"); + else if (p_idx == 1) + return PropertyInfo(Variant::REAL, "to"); + else + return PropertyInfo(Variant::REAL, "value"); + } break; + case MATH_RANGE_LERP: { + if (p_idx == 0) + return PropertyInfo(Variant::REAL, "value"); + else if (p_idx == 1) + return PropertyInfo(Variant::REAL, "istart"); + else if (p_idx == 2) + return PropertyInfo(Variant::REAL, "istop"); + else if (p_idx == 3) + return PropertyInfo(Variant::REAL, "ostart"); + else + return PropertyInfo(Variant::REAL, "ostop"); } break; case MATH_DECTIME: { if (p_idx == 0) @@ -495,6 +519,8 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons } break; case MATH_STEPIFY: case MATH_LERP: + case MATH_INVERSE_LERP: + case MATH_RANGE_LERP: case MATH_DECTIME: { t = Variant::REAL; @@ -795,6 +821,22 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in VALIDATE_ARG_NUM(2); *r_return = Math::lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); } break; + case VisualScriptBuiltinFunc::MATH_INVERSE_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::inverse_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case VisualScriptBuiltinFunc::MATH_RANGE_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + VALIDATE_ARG_NUM(3); + VALIDATE_ARG_NUM(4); + *r_return = Math::range_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]); + } break; case VisualScriptBuiltinFunc::MATH_DECTIME: { VALIDATE_ARG_NUM(0); @@ -1175,6 +1217,72 @@ void VisualScriptBuiltinFunc::_bind_methods() { cc += func_name[i]; } ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, cc), "set_func", "get_func"); + + BIND_ENUM_CONSTANT(MATH_SIN); + BIND_ENUM_CONSTANT(MATH_COS); + BIND_ENUM_CONSTANT(MATH_TAN); + BIND_ENUM_CONSTANT(MATH_SINH); + BIND_ENUM_CONSTANT(MATH_COSH); + BIND_ENUM_CONSTANT(MATH_TANH); + BIND_ENUM_CONSTANT(MATH_ASIN); + BIND_ENUM_CONSTANT(MATH_ACOS); + BIND_ENUM_CONSTANT(MATH_ATAN); + BIND_ENUM_CONSTANT(MATH_ATAN2); + BIND_ENUM_CONSTANT(MATH_SQRT); + BIND_ENUM_CONSTANT(MATH_FMOD); + BIND_ENUM_CONSTANT(MATH_FPOSMOD); + BIND_ENUM_CONSTANT(MATH_FLOOR); + BIND_ENUM_CONSTANT(MATH_CEIL); + BIND_ENUM_CONSTANT(MATH_ROUND); + BIND_ENUM_CONSTANT(MATH_ABS); + BIND_ENUM_CONSTANT(MATH_SIGN); + BIND_ENUM_CONSTANT(MATH_POW); + BIND_ENUM_CONSTANT(MATH_LOG); + BIND_ENUM_CONSTANT(MATH_EXP); + BIND_ENUM_CONSTANT(MATH_ISNAN); + BIND_ENUM_CONSTANT(MATH_ISINF); + BIND_ENUM_CONSTANT(MATH_EASE); + BIND_ENUM_CONSTANT(MATH_DECIMALS); + BIND_ENUM_CONSTANT(MATH_STEPIFY); + BIND_ENUM_CONSTANT(MATH_LERP); + BIND_ENUM_CONSTANT(MATH_INVERSE_LERP); + BIND_ENUM_CONSTANT(MATH_RANGE_LERP); + BIND_ENUM_CONSTANT(MATH_DECTIME); + BIND_ENUM_CONSTANT(MATH_RANDOMIZE); + BIND_ENUM_CONSTANT(MATH_RAND); + BIND_ENUM_CONSTANT(MATH_RANDF); + BIND_ENUM_CONSTANT(MATH_RANDOM); + BIND_ENUM_CONSTANT(MATH_SEED); + BIND_ENUM_CONSTANT(MATH_RANDSEED); + BIND_ENUM_CONSTANT(MATH_DEG2RAD); + BIND_ENUM_CONSTANT(MATH_RAD2DEG); + BIND_ENUM_CONSTANT(MATH_LINEAR2DB); + BIND_ENUM_CONSTANT(MATH_DB2LINEAR); + BIND_ENUM_CONSTANT(LOGIC_MAX); + BIND_ENUM_CONSTANT(LOGIC_MIN); + BIND_ENUM_CONSTANT(LOGIC_CLAMP); + BIND_ENUM_CONSTANT(LOGIC_NEAREST_PO2); + BIND_ENUM_CONSTANT(OBJ_WEAKREF); + BIND_ENUM_CONSTANT(FUNC_FUNCREF); + BIND_ENUM_CONSTANT(TYPE_CONVERT); + BIND_ENUM_CONSTANT(TYPE_OF); + BIND_ENUM_CONSTANT(TYPE_EXISTS); + BIND_ENUM_CONSTANT(TEXT_CHAR); + BIND_ENUM_CONSTANT(TEXT_STR); + BIND_ENUM_CONSTANT(TEXT_PRINT); + BIND_ENUM_CONSTANT(TEXT_PRINTERR); + BIND_ENUM_CONSTANT(TEXT_PRINTRAW); + BIND_ENUM_CONSTANT(VAR_TO_STR); + BIND_ENUM_CONSTANT(STR_TO_VAR); + BIND_ENUM_CONSTANT(VAR_TO_BYTES); + BIND_ENUM_CONSTANT(BYTES_TO_VAR); + BIND_ENUM_CONSTANT(COLORN); + BIND_ENUM_CONSTANT(FUNC_MAX); +} + +VisualScriptBuiltinFunc::VisualScriptBuiltinFunc(VisualScriptBuiltinFunc::BuiltinFunc func) { + + this->func = func; } VisualScriptBuiltinFunc::VisualScriptBuiltinFunc() { @@ -1185,9 +1293,7 @@ VisualScriptBuiltinFunc::VisualScriptBuiltinFunc() { template <VisualScriptBuiltinFunc::BuiltinFunc func> static Ref<VisualScriptNode> create_builtin_func_node(const String &p_name) { - Ref<VisualScriptBuiltinFunc> node; - node.instance(); - node->set_func(func); + Ref<VisualScriptBuiltinFunc> node = memnew(VisualScriptBuiltinFunc(func)); return node; } @@ -1223,6 +1329,8 @@ void register_visual_script_builtin_func_node() { VisualScriptLanguage::singleton->add_register_func("functions/built_in/decimals", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECIMALS>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/stepify", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_STEPIFY>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LERP>); + VisualScriptLanguage::singleton->add_register_func("functions/built_in/inverse_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_INVERSE_LERP>); + VisualScriptLanguage::singleton->add_register_func("functions/built_in/range_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANGE_LERP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/dectime", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECTIME>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/randomize", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOMIZE>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/rand", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAND>); diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h index 97ab307039..5758d23e8f 100644 --- a/modules/visual_script/visual_script_builtin_funcs.h +++ b/modules/visual_script/visual_script_builtin_funcs.h @@ -64,6 +64,8 @@ public: MATH_DECIMALS, MATH_STEPIFY, MATH_LERP, + MATH_INVERSE_LERP, + MATH_RANGE_LERP, MATH_DECTIME, MATH_RANDOMIZE, MATH_RAND, @@ -130,6 +132,7 @@ public: virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance); + VisualScriptBuiltinFunc(VisualScriptBuiltinFunc::BuiltinFunc func); VisualScriptBuiltinFunc(); }; diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 7ab2a93b55..03015df844 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* visual_script_editor.h */ +/* visual_script_editor.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -331,44 +331,83 @@ public: VisualScriptEditorVariableEdit() { undo_redo = NULL; } }; -static Color _color_from_type(Variant::Type p_type) { +static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) { Color color; - switch (p_type) { - case Variant::NIL: color = Color::html("69ecbd"); break; - - case Variant::BOOL: color = Color::html("8da6f0"); break; - case Variant::INT: color = Color::html("7dc6ef"); break; - case Variant::REAL: color = Color::html("61daf4"); break; - case Variant::STRING: color = Color::html("6ba7ec"); break; - - case Variant::VECTOR2: color = Color::html("bd91f1"); break; - case Variant::RECT2: color = Color::html("f191a5"); break; - case Variant::VECTOR3: color = Color::html("d67dee"); break; - case Variant::TRANSFORM2D: color = Color::html("c4ec69"); break; - case Variant::PLANE: color = Color::html("f77070"); break; - case Variant::QUAT: color = Color::html("ec69a3"); break; - case Variant::RECT3: color = Color::html("ee7991"); break; - case Variant::BASIS: color = Color::html("e3ec69"); break; - case Variant::TRANSFORM: color = Color::html("f6a86e"); break; - - case Variant::COLOR: color = Color::html("9dff70"); break; - case Variant::NODE_PATH: color = Color::html("6993ec"); break; - case Variant::_RID: color = Color::html("69ec9a"); break; - case Variant::OBJECT: color = Color::html("79f3e8"); break; - case Variant::DICTIONARY: color = Color::html("77edb1"); break; - - case Variant::ARRAY: color = Color::html("e0e0e0"); break; - case Variant::POOL_BYTE_ARRAY: color = Color::html("aaf4c8"); break; - case Variant::POOL_INT_ARRAY: color = Color::html("afdcf5"); break; - case Variant::POOL_REAL_ARRAY: color = Color::html("97e7f8"); break; - case Variant::POOL_STRING_ARRAY: color = Color::html("9dc4f2"); break; - case Variant::POOL_VECTOR2_ARRAY: color = Color::html("d1b3f5"); break; - case Variant::POOL_VECTOR3_ARRAY: color = Color::html("df9bf2"); break; - case Variant::POOL_COLOR_ARRAY: color = Color::html("e9ff97"); break; - - default: - color.set_hsv(p_type / float(Variant::VARIANT_MAX), 0.7, 0.7); - } + if (dark_theme) + switch (p_type) { + case Variant::NIL: color = Color::html("#69ecbd"); break; + + case Variant::BOOL: color = Color::html("#8da6f0"); break; + case Variant::INT: color = Color::html("#7dc6ef"); break; + case Variant::REAL: color = Color::html("#61daf4"); break; + case Variant::STRING: color = Color::html("#6ba7ec"); break; + + case Variant::VECTOR2: color = Color::html("#bd91f1"); break; + case Variant::RECT2: color = Color::html("#f191a5"); break; + case Variant::VECTOR3: color = Color::html("#d67dee"); break; + case Variant::TRANSFORM2D: color = Color::html("#c4ec69"); break; + case Variant::PLANE: color = Color::html("#f77070"); break; + case Variant::QUAT: color = Color::html("#ec69a3"); break; + case Variant::RECT3: color = Color::html("#ee7991"); break; + case Variant::BASIS: color = Color::html("#e3ec69"); break; + case Variant::TRANSFORM: color = Color::html("#f6a86e"); break; + + case Variant::COLOR: color = Color::html("#9dff70"); break; + case Variant::NODE_PATH: color = Color::html("#6993ec"); break; + case Variant::_RID: color = Color::html("#69ec9a"); break; + case Variant::OBJECT: color = Color::html("#79f3e8"); break; + case Variant::DICTIONARY: color = Color::html("#77edb1"); break; + + case Variant::ARRAY: color = Color::html("#e0e0e0"); break; + case Variant::POOL_BYTE_ARRAY: color = Color::html("#aaf4c8"); break; + case Variant::POOL_INT_ARRAY: color = Color::html("#afdcf5"); break; + case Variant::POOL_REAL_ARRAY: color = Color::html("#97e7f8"); break; + case Variant::POOL_STRING_ARRAY: color = Color::html("#9dc4f2"); break; + case Variant::POOL_VECTOR2_ARRAY: color = Color::html("#d1b3f5"); break; + case Variant::POOL_VECTOR3_ARRAY: color = Color::html("#df9bf2"); break; + case Variant::POOL_COLOR_ARRAY: color = Color::html("#e9ff97"); break; + + default: + color.set_hsv(p_type / float(Variant::VARIANT_MAX), 0.7, 0.7); + } + else + switch (p_type) { + case Variant::NIL: color = Color::html("#25e3a0"); break; + + case Variant::BOOL: color = Color::html("#6d8eeb"); break; + case Variant::INT: color = Color::html("#4fb2e9"); break; + case Variant::REAL: color = Color::html("#27ccf0"); break; + case Variant::STRING: color = Color::html("#4690e7"); break; + + case Variant::VECTOR2: color = Color::html("#ad76ee"); break; + case Variant::RECT2: color = Color::html("#ee758e"); break; + case Variant::VECTOR3: color = Color::html("#dc6aed"); break; + case Variant::TRANSFORM2D: color = Color::html("#96ce1a"); break; + case Variant::PLANE: color = Color::html("#f77070"); break; + case Variant::QUAT: color = Color::html("#ec69a3"); break; + case Variant::RECT3: color = Color::html("#ee7991"); break; + case Variant::BASIS: color = Color::html("#b2bb19"); break; + case Variant::TRANSFORM: color = Color::html("#f49047"); break; + + case Variant::COLOR: color = Color::html("#3cbf00"); break; + case Variant::NODE_PATH: color = Color::html("#6993ec"); break; + case Variant::_RID: color = Color::html("#2ce573"); break; + case Variant::OBJECT: color = Color::html("#12d5c3"); break; + case Variant::DICTIONARY: color = Color::html("#57e99f"); break; + + case Variant::ARRAY: color = Color::html("#737373"); break; + case Variant::POOL_BYTE_ARRAY: color = Color::html("#61ea98"); break; + case Variant::POOL_INT_ARRAY: color = Color::html("#61baeb"); break; + case Variant::POOL_REAL_ARRAY: color = Color::html("#40d3f2"); break; + case Variant::POOL_STRING_ARRAY: color = Color::html("#609fea"); break; + case Variant::POOL_VECTOR2_ARRAY: color = Color::html("#9d5dea"); break; + case Variant::POOL_VECTOR3_ARRAY: color = Color::html("#ca5aea"); break; + case Variant::POOL_COLOR_ARRAY: color = Color::html("#92ba00"); break; + + default: + color.set_hsv(p_type / float(Variant::VARIANT_MAX), 0.3, 0.3); + } + return color; } @@ -481,7 +520,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) { continue; Ref<VisualScriptNode> node = script->get_node(edited_func, E->get()); - Vector2 pos = script->get_node_pos(edited_func, E->get()); + Vector2 pos = script->get_node_position(edited_func, E->get()); GraphNode *gnode = memnew(GraphNode); gnode->set_title(node->get_caption()); @@ -491,11 +530,6 @@ void VisualScriptEditor::_update_graph(int p_only_id) { gnode->set_overlay(GraphNode::OVERLAY_BREAKPOINT); } - if (EditorSettings::get_singleton()->has("editors/visual_script/color_" + node->get_category())) { - Color c = EditorSettings::get_singleton()->get("editors/visual_script/color_" + node->get_category()); - gnode->set_self_modulate(c); - } - gnode->set_meta("__vnode", node); gnode->set_name(itos(E->get())); gnode->connect("dragged", this, "_node_moved", varray(E->get())); @@ -506,7 +540,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) { gnode->set_show_close_button(true); } - if (Object::cast_to<VisualScriptExpression>(*node)) { + if (Object::cast_to<VisualScriptExpression>(node.ptr())) { LineEdit *line_edit = memnew(LineEdit); line_edit->set_text(node->get_text()); @@ -520,18 +554,39 @@ void VisualScriptEditor::_update_graph(int p_only_id) { gnode->add_child(text); } - if (Object::cast_to<VisualScriptExpression>(*node)) { + if (Object::cast_to<VisualScriptComment>(node.ptr())) { Ref<VisualScriptComment> vsc = node; gnode->set_comment(true); - gnode->set_resizeable(true); + gnode->set_resizable(true); gnode->set_custom_minimum_size(vsc->get_size() * EDSCALE); gnode->connect("resize_request", this, "_comment_node_resized", varray(E->get())); } + if (node_styles.has(node->get_category())) { + Ref<StyleBoxFlat> sbf = node_styles[node->get_category()]; + if (gnode->is_comment()) + sbf = EditorNode::get_singleton()->get_theme_base()->get_theme()->get_stylebox("comment", "GraphNode"); + + Color c = sbf->get_border_color(MARGIN_TOP); + c.a = 1; + if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { + Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0); + mono_color.a = 0.85; + c = mono_color; + } + + gnode->add_color_override("title_color", c); + c.a = 0.7; + gnode->add_color_override("close_color", c); + gnode->add_style_override("frame", sbf); + } + + const Color mono_color = get_color("mono_color", "Editor"); + int slot_idx = 0; bool single_seq_output = node->get_output_sequence_port_count() == 1 && node->get_output_sequence_port_text(0) == String(); - gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, Color(1, 1, 1, 1), single_seq_output, TYPE_SEQUENCE, Color(1, 1, 1, 1), seq_port, seq_port); + gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port); gnode->set_offset(pos * EDSCALE); slot_idx++; @@ -548,7 +603,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) { text2->set_text(node->get_output_sequence_port_text(i)); text2->set_align(Label::ALIGN_RIGHT); gnode->add_child(text2); - gnode->set_slot(slot_idx, false, 0, Color(), true, TYPE_SEQUENCE, Color(1, 1, 1, 1), seq_port, seq_port); + gnode->set_slot(slot_idx, false, 0, Color(), true, TYPE_SEQUENCE, mono_color, seq_port, seq_port); slot_idx++; } } @@ -663,10 +718,11 @@ void VisualScriptEditor::_update_graph(int p_only_id) { gnode->add_child(hbc); + bool dark_theme = get_constant("dark_theme", "Editor"); if (i < mixed_seq_ports) { - gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type), true, TYPE_SEQUENCE, Color(1, 1, 1, 1), Ref<Texture>(), seq_port); + gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), true, TYPE_SEQUENCE, mono_color, Ref<Texture>(), seq_port); } else { - gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type), right_ok, right_type, _color_from_type(right_type)); + gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), right_ok, right_type, _color_from_type(right_type, dark_theme)); } slot_idx++; @@ -696,7 +752,7 @@ void VisualScriptEditor::_update_members() { functions->set_text(0, TTR("Functions:")); functions->add_button(0, Control::get_icon("Override", "EditorIcons"), 1); functions->add_button(0, Control::get_icon("Add", "EditorIcons"), 0); - functions->set_custom_bg_color(0, Control::get_color("prop_section", "Editor")); + functions->set_custom_color(0, Control::get_color("mono_color", "Editor")); List<StringName> func_names; script->get_function_list(&func_names); @@ -705,13 +761,7 @@ void VisualScriptEditor::_update_members() { ti->set_text(0, E->get()); ti->set_selectable(0, true); ti->set_editable(0, true); - //ti->add_button(0,Control::get_icon("Edit","EditorIcons"),0); function arguments are in the node now - //ti->add_button(0, Control::get_icon("Del", "EditorIcons"), 1); ti->set_metadata(0, E->get()); - if (E->get() == edited_func) { - ti->set_custom_bg_color(0, get_color("prop_category", "Editor")); - ti->set_custom_color(0, Color(1, 1, 1, 1)); - } if (selected == E->get()) ti->select(0); } @@ -720,7 +770,7 @@ void VisualScriptEditor::_update_members() { variables->set_selectable(0, false); variables->set_text(0, TTR("Variables:")); variables->add_button(0, Control::get_icon("Add", "EditorIcons")); - variables->set_custom_bg_color(0, Control::get_color("prop_section", "Editor")); + variables->set_custom_color(0, Control::get_color("mono_color", "Editor")); Ref<Texture> type_icons[Variant::VARIANT_MAX] = { Control::get_icon("MiniVariant", "EditorIcons"), @@ -759,13 +809,11 @@ void VisualScriptEditor::_update_members() { ti->set_text(0, E->get()); Variant var = script->get_variable_default_value(E->get()); - ti->set_suffix(0, "=" + String(var)); + ti->set_suffix(0, "= " + String(var)); ti->set_icon(0, type_icons[script->get_variable_info(E->get()).type]); ti->set_selectable(0, true); ti->set_editable(0, true); - //ti->add_button(0, Control::get_icon("Edit", "EditorIcons"), 0); - //ti->add_button(0, Control::get_icon("Del", "EditorIcons"), 1); ti->set_metadata(0, E->get()); if (selected == E->get()) ti->select(0); @@ -775,7 +823,7 @@ void VisualScriptEditor::_update_members() { _signals->set_selectable(0, false); _signals->set_text(0, TTR("Signals:")); _signals->add_button(0, Control::get_icon("Add", "EditorIcons")); - _signals->set_custom_bg_color(0, Control::get_color("prop_section", "Editor")); + _signals->set_custom_color(0, Control::get_color("mono_color", "Editor")); List<StringName> signal_names; script->get_custom_signal_list(&signal_names); @@ -784,8 +832,6 @@ void VisualScriptEditor::_update_members() { ti->set_text(0, E->get()); ti->set_selectable(0, true); ti->set_editable(0, true); - //ti->add_button(0, Control::get_icon("Edit", "EditorIcons"), 0); - //ti->add_button(0, Control::get_icon("Del", "EditorIcons"), 1); ti->set_metadata(0, E->get()); if (selected == E->get()) ti->select(0); @@ -886,8 +932,8 @@ void VisualScriptEditor::_member_edited() { undo_redo->add_undo_method(this, "_update_members"); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->add_do_method(this, "emit_signal", "script_changed"); - undo_redo->add_undo_method(this, "emit_signal", "script_changed"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); // _update_graph(); @@ -903,8 +949,8 @@ void VisualScriptEditor::_member_edited() { undo_redo->add_undo_method(script.ptr(), "rename_variable", new_name, name); undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); - undo_redo->add_do_method(this, "emit_signal", "script_changed"); - undo_redo->add_undo_method(this, "emit_signal", "script_changed"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); return; //or crash because it will become invalid @@ -918,56 +964,14 @@ void VisualScriptEditor::_member_edited() { undo_redo->add_undo_method(script.ptr(), "rename_custom_signal", new_name, name); undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); - undo_redo->add_do_method(this, "emit_signal", "script_changed"); - undo_redo->add_undo_method(this, "emit_signal", "script_changed"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); return; //or crash because it will become invalid } } -void VisualScriptEditor::_override_pressed(int p_id) { - - //override a virtual function or method from base type - - ERR_FAIL_COND(!virtuals_in_menu.has(p_id)); - - VirtualInMenu vim = virtuals_in_menu[p_id]; - - String name = _validate_name(vim.name); - selected = name; - edited_func = selected; - Ref<VisualScriptFunction> func_node; - func_node.instance(); - func_node->set_name(vim.name); - - undo_redo->create_action(TTR("Add Function")); - undo_redo->add_do_method(script.ptr(), "add_function", name); - for (int i = 0; i < vim.args.size(); i++) { - func_node->add_argument(vim.args[i].first, vim.args[i].second); - } - - undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node); - if (vim.ret != Variant::NIL || vim.ret_variant) { - Ref<VisualScriptReturn> ret_node; - ret_node.instance(); - ret_node->set_return_type(vim.ret); - ret_node->set_enable_return_value(true); - ret_node->set_name(vim.name); - undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id() + 1, ret_node, Vector2(500, 0)); - } - - undo_redo->add_undo_method(script.ptr(), "remove_function", name); - undo_redo->add_do_method(this, "_update_members"); - undo_redo->add_undo_method(this, "_update_members"); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - - undo_redo->commit_action(); - - _update_graph(); -} - void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_button) { TreeItem *ti = Object::cast_to<TreeItem>(p_item); @@ -980,64 +984,9 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt //add function, this one uses menu if (p_button == 1) { - new_function_menu->clear(); - new_function_menu->set_size(Size2(0, 0)); - int idx = 0; - - virtuals_in_menu.clear(); - - List<MethodInfo> mi; - ClassDB::get_method_list(script->get_instance_base_type(), &mi); - for (List<MethodInfo>::Element *E = mi.front(); E; E = E->next()) { - MethodInfo mi = E->get(); - if (mi.flags & METHOD_FLAG_VIRTUAL) { - - VirtualInMenu vim; - vim.name = mi.name; - vim.ret = mi.return_val.type; - if (mi.return_val.name != String()) - vim.ret_variant = true; - else - vim.ret_variant = false; - - String desc; - - if (mi.return_val.type == Variant::NIL) - desc = "var"; - else - desc = Variant::get_type_name(mi.return_val.type); - desc += " " + mi.name + " ( "; - - for (int i = 0; i < mi.arguments.size(); i++) { - - if (i > 0) - desc += ", "; - - if (mi.arguments[i].type == Variant::NIL) - desc += "var "; - else - desc += Variant::get_type_name(mi.arguments[i].type) + " "; - - desc += mi.arguments[i].name; - - Pair<Variant::Type, String> p; - p.first = mi.arguments[i].type; - p.second = mi.arguments[i].name; - vim.args.push_back(p); - } - - desc += " )"; - - virtuals_in_menu[idx] = vim; - new_function_menu->add_item(desc, idx); - idx++; - } - } + new_virtual_method_select->select_method_from_base_type(script->get_instance_base_type(), String(), true); - Rect2 pos = members->get_item_rect(ti); - new_function_menu->set_position(members->get_global_position() + pos.position + Vector2(0, pos.size.y)); - new_function_menu->popup(); return; } else if (p_button == 0) { @@ -1057,8 +1006,8 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt undo_redo->add_undo_method(this, "_update_members"); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->add_do_method(this, "emit_signal", "script_changed"); - undo_redo->add_undo_method(this, "emit_signal", "script_changed"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); _update_graph(); @@ -1077,8 +1026,8 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt undo_redo->add_undo_method(script.ptr(), "remove_variable", name); undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); - undo_redo->add_do_method(this, "emit_signal", "script_changed"); - undo_redo->add_undo_method(this, "emit_signal", "script_changed"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); return; //or crash because it will become invalid } @@ -1093,8 +1042,8 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt undo_redo->add_undo_method(script.ptr(), "remove_custom_signal", name); undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); - undo_redo->add_do_method(this, "emit_signal", "script_changed"); - undo_redo->add_undo_method(this, "emit_signal", "script_changed"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); return; //or crash because it will become invalid } @@ -1148,7 +1097,7 @@ void VisualScriptEditor::_available_node_doubleclicked() { List<int> existing; script->get_node_list(edited_func, &existing); for (List<int>::Element *E = existing.front(); E; E = E->next()) { - Point2 pos = script->get_node_pos(edited_func, E->get()); + Point2 pos = script->get_node_position(edited_func, E->get()); if (pos.distance_to(ofs) < 15) { ofs += Vector2(graph->get_snap(), graph->get_snap()); exists = true; @@ -1269,7 +1218,7 @@ void VisualScriptEditor::_on_nodes_delete() { for (List<int>::Element *F = to_erase.front(); F; F = F->next()) { undo_redo->add_do_method(script.ptr(), "remove_node", edited_func, F->get()); - undo_redo->add_undo_method(script.ptr(), "add_node", edited_func, F->get(), script->get_node(edited_func, F->get()), script->get_node_pos(edited_func, F->get())); + undo_redo->add_undo_method(script.ptr(), "add_node", edited_func, F->get(), script->get_node(edited_func, F->get()), script->get_node_position(edited_func, F->get())); List<VisualScript::SequenceConnection> sequence_conns; script->get_sequence_connection_list(edited_func, &sequence_conns); @@ -1326,7 +1275,7 @@ void VisualScriptEditor::_on_nodes_duplicate() { int new_id = idc++; to_select.insert(new_id); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, dupe, script->get_node_pos(edited_func, F->get()) + Vector2(20, 20)); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, dupe, script->get_node_position(edited_func, F->get()) + Vector2(20, 20)); undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); } undo_redo->add_do_method(this, "_update_graph"); @@ -1360,7 +1309,7 @@ Variant VisualScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_f if (p_from == nodes) { - TreeItem *it = nodes->get_item_at_pos(p_point); + TreeItem *it = nodes->get_item_at_position(p_point); if (!it) return Variant(); String type = it->get_metadata(0); @@ -1379,7 +1328,7 @@ Variant VisualScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_f if (p_from == members) { - TreeItem *it = members->get_item_at_pos(p_point); + TreeItem *it = members->get_item_at_position(p_point); if (!it) return Variant(); @@ -1495,415 +1444,424 @@ static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { - if (p_from == graph) { + if (p_from != graph) { + return; + } - Dictionary d = p_data; - if (d.has("type") && String(d["type"]) == "visual_script_node_drag") { + Dictionary d = p_data; - Vector2 ofs = graph->get_scroll_ofs() + p_point; + if (!d.has("type")) { + return; + } - if (graph->is_using_snap()) { - int snap = graph->get_snap(); - ofs = ofs.snapped(Vector2(snap, snap)); - } + if (String(d["type"]) == "visual_script_node_drag") { + if (!d.has("node_type") || String(d["node_type"]) == "Null") { + return; + } - ofs /= EDSCALE; + Vector2 ofs = graph->get_scroll_ofs() + p_point; - Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(d["node_type"]); - int new_id = script->get_available_id(); + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } - undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); + ofs /= EDSCALE; - Node *node = graph->get_node(itos(new_id)); - if (node) { - graph->set_selected(node); - _node_selected(node); - } + Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(d["node_type"]); + int new_id = script->get_available_id(); + + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + + Node *node = graph->get_node(itos(new_id)); + if (node) { + graph->set_selected(node); + _node_selected(node); } + } - if (d.has("type") && String(d["type"]) == "visual_script_variable_drag") { + if (String(d["type"]) == "visual_script_variable_drag") { #ifdef OSX_ENABLED - bool use_set = Input::get_singleton()->is_key_pressed(KEY_META); + bool use_set = Input::get_singleton()->is_key_pressed(KEY_META); #else - bool use_set = Input::get_singleton()->is_key_pressed(KEY_CONTROL); + bool use_set = Input::get_singleton()->is_key_pressed(KEY_CONTROL); #endif - Vector2 ofs = graph->get_scroll_ofs() + p_point; - if (graph->is_using_snap()) { - int snap = graph->get_snap(); - ofs = ofs.snapped(Vector2(snap, snap)); - } + Vector2 ofs = graph->get_scroll_ofs() + p_point; + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } - ofs /= EDSCALE; + ofs /= EDSCALE; - Ref<VisualScriptNode> vnode; - if (use_set) { - Ref<VisualScriptVariableSet> vnodes; - vnodes.instance(); - vnodes->set_variable(d["variable"]); - vnode = vnodes; - } else { + Ref<VisualScriptNode> vnode; + if (use_set) { + Ref<VisualScriptVariableSet> vnodes; + vnodes.instance(); + vnodes->set_variable(d["variable"]); + vnode = vnodes; + } else { - Ref<VisualScriptVariableGet> vnodeg; - vnodeg.instance(); - vnodeg->set_variable(d["variable"]); - vnode = vnodeg; - } + Ref<VisualScriptVariableGet> vnodeg; + vnodeg.instance(); + vnodeg->set_variable(d["variable"]); + vnode = vnodeg; + } - int new_id = script->get_available_id(); + int new_id = script->get_available_id(); - undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); - Node *node = graph->get_node(itos(new_id)); - if (node) { - graph->set_selected(node); - _node_selected(node); - } + Node *node = graph->get_node(itos(new_id)); + if (node) { + graph->set_selected(node); + _node_selected(node); } + } - if (d.has("type") && String(d["type"]) == "visual_script_function_drag") { + if (String(d["type"]) == "visual_script_function_drag") { - Vector2 ofs = graph->get_scroll_ofs() + p_point; - if (graph->is_using_snap()) { - int snap = graph->get_snap(); - ofs = ofs.snapped(Vector2(snap, snap)); - } + Vector2 ofs = graph->get_scroll_ofs() + p_point; + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } - ofs /= EDSCALE; + ofs /= EDSCALE; - Ref<VisualScriptFunctionCall> vnode; - vnode.instance(); - vnode->set_call_mode(VisualScriptFunctionCall::CALL_MODE_SELF); + Ref<VisualScriptFunctionCall> vnode; + vnode.instance(); + vnode->set_call_mode(VisualScriptFunctionCall::CALL_MODE_SELF); - int new_id = script->get_available_id(); + int new_id = script->get_available_id(); - undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_do_method(vnode.ptr(), "set_base_type", script->get_instance_base_type()); - undo_redo->add_do_method(vnode.ptr(), "set_function", d["function"]); + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); + undo_redo->add_do_method(vnode.ptr(), "set_base_type", script->get_instance_base_type()); + undo_redo->add_do_method(vnode.ptr(), "set_function", d["function"]); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); - Node *node = graph->get_node(itos(new_id)); - if (node) { - graph->set_selected(node); - _node_selected(node); - } + Node *node = graph->get_node(itos(new_id)); + if (node) { + graph->set_selected(node); + _node_selected(node); } + } - if (d.has("type") && String(d["type"]) == "visual_script_signal_drag") { + if (String(d["type"]) == "visual_script_signal_drag") { - Vector2 ofs = graph->get_scroll_ofs() + p_point; - if (graph->is_using_snap()) { - int snap = graph->get_snap(); - ofs = ofs.snapped(Vector2(snap, snap)); - } + Vector2 ofs = graph->get_scroll_ofs() + p_point; + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } - ofs /= EDSCALE; + ofs /= EDSCALE; - Ref<VisualScriptEmitSignal> vnode; - vnode.instance(); - vnode->set_signal(d["signal"]); + Ref<VisualScriptEmitSignal> vnode; + vnode.instance(); + vnode->set_signal(d["signal"]); - int new_id = script->get_available_id(); + int new_id = script->get_available_id(); - undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); - Node *node = graph->get_node(itos(new_id)); - if (node) { - graph->set_selected(node); - _node_selected(node); - } + Node *node = graph->get_node(itos(new_id)); + if (node) { + graph->set_selected(node); + _node_selected(node); } + } - if (d.has("type") && String(d["type"]) == "resource") { + if (String(d["type"]) == "resource") { - Vector2 ofs = graph->get_scroll_ofs() + p_point; - if (graph->is_using_snap()) { - int snap = graph->get_snap(); - ofs = ofs.snapped(Vector2(snap, snap)); - } + Vector2 ofs = graph->get_scroll_ofs() + p_point; + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } - ofs /= EDSCALE; + ofs /= EDSCALE; - Ref<VisualScriptPreload> prnode; - prnode.instance(); - prnode->set_preload(d["resource"]); + Ref<VisualScriptPreload> prnode; + prnode.instance(); + prnode->set_preload(d["resource"]); - int new_id = script->get_available_id(); + int new_id = script->get_available_id(); - undo_redo->create_action(TTR("Add Preload Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, prnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); + undo_redo->create_action(TTR("Add Preload Node")); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, prnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); - Node *node = graph->get_node(itos(new_id)); - if (node) { - graph->set_selected(node); - _node_selected(node); - } + Node *node = graph->get_node(itos(new_id)); + if (node) { + graph->set_selected(node); + _node_selected(node); } + } - if (d.has("type") && String(d["type"]) == "files") { + if (String(d["type"]) == "files") { - Vector2 ofs = graph->get_scroll_ofs() + p_point; - if (graph->is_using_snap()) { - int snap = graph->get_snap(); - ofs = ofs.snapped(Vector2(snap, snap)); - } - - ofs /= EDSCALE; + Vector2 ofs = graph->get_scroll_ofs() + p_point; + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } - Array files = d["files"]; + ofs /= EDSCALE; - List<int> new_ids; - int new_id = script->get_available_id(); + Array files = d["files"]; - if (files.size()) { - undo_redo->create_action(TTR("Add Preload Node")); + List<int> new_ids; + int new_id = script->get_available_id(); - for (int i = 0; i < files.size(); i++) { + if (files.size()) { + undo_redo->create_action(TTR("Add Preload Node")); - Ref<Resource> res = ResourceLoader::load(files[i]); - if (!res.is_valid()) - continue; + for (int i = 0; i < files.size(); i++) { - Ref<VisualScriptPreload> prnode; - prnode.instance(); - prnode->set_preload(res); + Ref<Resource> res = ResourceLoader::load(files[i]); + if (!res.is_valid()) + continue; - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, prnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - new_ids.push_back(new_id); - new_id++; - ofs += Vector2(20, 20) * EDSCALE; - } + Ref<VisualScriptPreload> prnode; + prnode.instance(); + prnode->set_preload(res); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, prnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + new_ids.push_back(new_id); + new_id++; + ofs += Vector2(20, 20) * EDSCALE; } - for (List<int>::Element *E = new_ids.front(); E; E = E->next()) { + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + } - Node *node = graph->get_node(itos(E->get())); - if (node) { - graph->set_selected(node); - _node_selected(node); - } + for (List<int>::Element *E = new_ids.front(); E; E = E->next()) { + + Node *node = graph->get_node(itos(E->get())); + if (node) { + graph->set_selected(node); + _node_selected(node); } } + } - if (d.has("type") && String(d["type"]) == "nodes") { + if (String(d["type"]) == "nodes") { - Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script); + Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script); - if (!sn) { - EditorNode::get_singleton()->show_warning("Can't drop nodes because script '" + get_name() + "' is not used in this scene."); - return; - } + if (!sn) { + EditorNode::get_singleton()->show_warning("Can't drop nodes because script '" + get_name() + "' is not used in this scene."); + return; + } #ifdef OSX_ENABLED - bool use_node = Input::get_singleton()->is_key_pressed(KEY_META); + bool use_node = Input::get_singleton()->is_key_pressed(KEY_META); #else - bool use_node = Input::get_singleton()->is_key_pressed(KEY_CONTROL); + bool use_node = Input::get_singleton()->is_key_pressed(KEY_CONTROL); #endif - Array nodes = d["nodes"]; - - Vector2 ofs = graph->get_scroll_ofs() + p_point; + Array nodes = d["nodes"]; - if (graph->is_using_snap()) { - int snap = graph->get_snap(); - ofs = ofs.snapped(Vector2(snap, snap)); - } - ofs /= EDSCALE; + Vector2 ofs = graph->get_scroll_ofs() + p_point; - undo_redo->create_action(TTR("Add Node(s) From Tree")); - int base_id = script->get_available_id(); + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } + ofs /= EDSCALE; - if (nodes.size() > 1) { - use_node = true; - } + undo_redo->create_action(TTR("Add Node(s) From Tree")); + int base_id = script->get_available_id(); - for (int i = 0; i < nodes.size(); i++) { + if (nodes.size() > 1) { + use_node = true; + } - NodePath np = nodes[i]; - Node *node = get_node(np); - if (!node) { - continue; - } + for (int i = 0; i < nodes.size(); i++) { - Ref<VisualScriptNode> n; + NodePath np = nodes[i]; + Node *node = get_node(np); + if (!node) { + continue; + } - if (use_node) { - Ref<VisualScriptSceneNode> scene_node; - scene_node.instance(); - scene_node->set_node_path(sn->get_path_to(node)); - n = scene_node; + Ref<VisualScriptNode> n; - } else { - Ref<VisualScriptFunctionCall> call; - call.instance(); - call->set_call_mode(VisualScriptFunctionCall::CALL_MODE_NODE_PATH); - call->set_base_path(sn->get_path_to(node)); - call->set_base_type(node->get_class()); - n = call; - - method_select->select_method_from_instance(node); - selecting_method_id = base_id; - } + if (use_node) { + Ref<VisualScriptSceneNode> scene_node; + scene_node.instance(); + scene_node->set_node_path(sn->get_path_to(node)); + n = scene_node; - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, base_id, n, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, base_id); + } else { + Ref<VisualScriptFunctionCall> call; + call.instance(); + call->set_call_mode(VisualScriptFunctionCall::CALL_MODE_NODE_PATH); + call->set_base_path(sn->get_path_to(node)); + call->set_base_type(node->get_class()); + n = call; - base_id++; - ofs += Vector2(25, 25); + method_select->select_method_from_instance(node); + selecting_method_id = base_id; } - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); + + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, base_id, n, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, base_id); + + base_id++; + ofs += Vector2(25, 25); } + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + } - if (d.has("type") && String(d["type"]) == "obj_property") { + if (String(d["type"]) == "obj_property") { - Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script); + Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script); - if (!sn && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { - EditorNode::get_singleton()->show_warning("Can't drop properties because script '" + get_name() + "' is not used in this scene.\nDrop holding 'Shift' to just copy the signature."); - return; - } + if (!sn && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { + EditorNode::get_singleton()->show_warning("Can't drop properties because script '" + get_name() + "' is not used in this scene.\nDrop holding 'Shift' to just copy the signature."); + return; + } - Object *obj = d["object"]; + Object *obj = d["object"]; - if (!obj) - return; + if (!obj) + return; - Node *node = Object::cast_to<Node>(obj); - Vector2 ofs = graph->get_scroll_ofs() + p_point; + Node *node = Object::cast_to<Node>(obj); + Vector2 ofs = graph->get_scroll_ofs() + p_point; - if (graph->is_using_snap()) { - int snap = graph->get_snap(); - ofs = ofs.snapped(Vector2(snap, snap)); - } + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } - ofs /= EDSCALE; + ofs /= EDSCALE; #ifdef OSX_ENABLED - bool use_get = Input::get_singleton()->is_key_pressed(KEY_META); + bool use_get = Input::get_singleton()->is_key_pressed(KEY_META); #else - bool use_get = Input::get_singleton()->is_key_pressed(KEY_CONTROL); + bool use_get = Input::get_singleton()->is_key_pressed(KEY_CONTROL); #endif - if (!node || Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { + if (!node || Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { - if (use_get) - undo_redo->create_action(TTR("Add Getter Property")); - else - undo_redo->create_action(TTR("Add Setter Property")); + if (use_get) + undo_redo->create_action(TTR("Add Getter Property")); + else + undo_redo->create_action(TTR("Add Setter Property")); - int base_id = script->get_available_id(); + int base_id = script->get_available_id(); - Ref<VisualScriptNode> vnode; + Ref<VisualScriptNode> vnode; - if (!use_get) { + if (!use_get) { - Ref<VisualScriptPropertySet> pset; - pset.instance(); - pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); - pset->set_base_type(obj->get_class()); - /*if (use_value) { + Ref<VisualScriptPropertySet> pset; + pset.instance(); + pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); + pset->set_base_type(obj->get_class()); + /*if (use_value) { pset->set_use_builtin_value(true); pset->set_builtin_value(d["value"]); }*/ - vnode = pset; - } else { - - Ref<VisualScriptPropertyGet> pget; - pget.instance(); - pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); - pget->set_base_type(obj->get_class()); + vnode = pset; + } else { - vnode = pget; - } + Ref<VisualScriptPropertyGet> pget; + pget.instance(); + pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); + pget->set_base_type(obj->get_class()); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, base_id, vnode, ofs); - undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]); - if (!use_get) { - undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]); - } + vnode = pget; + } - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, base_id); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, base_id, vnode, ofs); + undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]); + if (!use_get) { + undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]); + } - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, base_id); - } else { + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); - if (use_get) - undo_redo->create_action(TTR("Add Getter Property")); - else - undo_redo->create_action(TTR("Add Setter Property")); + } else { - int base_id = script->get_available_id(); + if (use_get) + undo_redo->create_action(TTR("Add Getter Property")); + else + undo_redo->create_action(TTR("Add Setter Property")); - Ref<VisualScriptNode> vnode; + int base_id = script->get_available_id(); - if (!use_get) { + Ref<VisualScriptNode> vnode; - Ref<VisualScriptPropertySet> pset; - pset.instance(); - if (sn == node) { - pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_SELF); - } else { - pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH); - pset->set_base_path(sn->get_path_to(node)); - } + if (!use_get) { - vnode = pset; + Ref<VisualScriptPropertySet> pset; + pset.instance(); + if (sn == node) { + pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_SELF); } else { - - Ref<VisualScriptPropertyGet> pget; - pget.instance(); - if (sn == node) { - pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_SELF); - } else { - pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_NODE_PATH); - pget->set_base_path(sn->get_path_to(node)); - } - vnode = pget; - } - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, base_id, vnode, ofs); - undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]); - if (!use_get) { - undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]); + pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH); + pset->set_base_path(sn->get_path_to(node)); } - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, base_id); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); + vnode = pset; + } else { + + Ref<VisualScriptPropertyGet> pget; + pget.instance(); + if (sn == node) { + pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_SELF); + } else { + pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_NODE_PATH); + pget->set_base_path(sn->get_path_to(node)); + } + vnode = pget; + } + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, base_id, vnode, ofs); + undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]); + if (!use_get) { + undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]); } + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, base_id); + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); } } } @@ -2271,7 +2229,7 @@ void VisualScriptEditor::_move_node(String func, int p_id, const Vector2 &p_to) if (Object::cast_to<GraphNode>(node)) Object::cast_to<GraphNode>(node)->set_offset(p_to); } - script->set_node_pos(edited_func, p_id, p_to / EDSCALE); + script->set_node_position(edited_func, p_id, p_to / EDSCALE); } void VisualScriptEditor::_node_moved(Vector2 p_from, Vector2 p_to, int p_id) { @@ -2285,7 +2243,7 @@ void VisualScriptEditor::_remove_node(int p_id) { undo_redo->create_action(TTR("Remove VisualScript Node")); undo_redo->add_do_method(script.ptr(), "remove_node", edited_func, p_id); - undo_redo->add_undo_method(script.ptr(), "add_node", edited_func, p_id, script->get_node(edited_func, p_id), script->get_node_pos(edited_func, p_id)); + undo_redo->add_undo_method(script.ptr(), "add_node", edited_func, p_id, script->get_node(edited_func, p_id), script->get_node_position(edited_func, p_id)); List<VisualScript::SequenceConnection> sequence_conns; script->get_sequence_connection_list(edited_func, &sequence_conns); @@ -2686,21 +2644,21 @@ void VisualScriptEditor::_selected_connect_node_method_or_setget(const String &p Ref<VisualScriptNode> vsn = script->get_node(edited_func, port_action_new_node); - if (Object::cast_to<VisualScriptFunctionCall>(*vsn)) { + if (Object::cast_to<VisualScriptFunctionCall>(vsn.ptr())) { Ref<VisualScriptFunctionCall> vsfc = vsn; vsfc->set_function(p_text); script->data_connect(edited_func, port_action_node, port_action_output, port_action_new_node, 0); } - if (Object::cast_to<VisualScriptPropertySet>(*vsn)) { + if (Object::cast_to<VisualScriptPropertySet>(vsn.ptr())) { Ref<VisualScriptPropertySet> vsp = vsn; vsp->set_property(p_text); script->data_connect(edited_func, port_action_node, port_action_output, port_action_new_node, 0); } - if (Object::cast_to<VisualScriptPropertyGet>(*vsn)) { + if (Object::cast_to<VisualScriptPropertyGet>(vsn.ptr())) { Ref<VisualScriptPropertyGet> vsp = vsn; vsp->set_property(p_text); @@ -2711,6 +2669,63 @@ void VisualScriptEditor::_selected_connect_node_method_or_setget(const String &p _update_graph_connections(); } +void VisualScriptEditor::_selected_new_virtual_method(const String &p_text) { + + String name = p_text; + if (script->has_function(name)) { + EditorNode::get_singleton()->show_warning(vformat(TTR("Script already has function '%s'"), name)); + return; + } + + MethodInfo minfo; + { + List<MethodInfo> methods; + bool found = false; + ClassDB::get_virtual_methods(script->get_instance_base_type(), &methods); + for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { + if (E->get().name == name) { + minfo = E->get(); + found = true; + } + } + + ERR_FAIL_COND(!found); + } + + selected = name; + edited_func = selected; + Ref<VisualScriptFunction> func_node; + func_node.instance(); + func_node->set_name(name); + + undo_redo->create_action(TTR("Add Function")); + undo_redo->add_do_method(script.ptr(), "add_function", name); + + for (int i = 0; i < minfo.arguments.size(); i++) { + func_node->add_argument(minfo.arguments[i].type, minfo.arguments[i].name); + } + + undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node); + if (minfo.return_val.type != Variant::NIL || minfo.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { + Ref<VisualScriptReturn> ret_node; + ret_node.instance(); + ret_node->set_return_type(minfo.return_val.type); + ret_node->set_enable_return_value(true); + ret_node->set_name(name); + undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id() + 1, ret_node, Vector2(500, 0)); + } + + undo_redo->add_undo_method(script.ptr(), "remove_function", name); + undo_redo->add_do_method(this, "_update_members"); + undo_redo->add_undo_method(this, "_update_members"); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + + undo_redo->commit_action(); + + _update_graph(); +} + void VisualScriptEditor::_cancel_connect_node_method_or_setget() { script->remove_node(edited_func, port_action_new_node); @@ -2783,6 +2798,39 @@ void VisualScriptEditor::_notification(int p_what) { node_filter->add_icon_override("right_icon", Control::get_icon("Search", "EditorIcons")); variable_editor->connect("changed", this, "_update_members"); signal_editor->connect("changed", this, "_update_members"); + + Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme(); + + bool dark_theme = tm->get_constant("dark_theme", "Editor"); + + List<Pair<String, Color> > colors; + if (dark_theme) { + colors.push_back(Pair<String, Color>("flow_control", Color::html("#f4f4f4"))); + colors.push_back(Pair<String, Color>("functions", Color::html("#f58581"))); + colors.push_back(Pair<String, Color>("data", Color::html("#80f6cf"))); + colors.push_back(Pair<String, Color>("operators", Color::html("#ab97df"))); + colors.push_back(Pair<String, Color>("custom", Color::html("#80bbf6"))); + colors.push_back(Pair<String, Color>("constants", Color::html("#f680b0"))); + } else { + colors.push_back(Pair<String, Color>("flow_control", Color::html("#424242"))); + colors.push_back(Pair<String, Color>("functions", Color::html("#f26661"))); + colors.push_back(Pair<String, Color>("data", Color::html("#13bb83"))); + colors.push_back(Pair<String, Color>("operators", Color::html("#8265d0"))); + colors.push_back(Pair<String, Color>("custom", Color::html("#4ea0f2"))); + colors.push_back(Pair<String, Color>("constants", Color::html("#f02f7d"))); + } + + for (List<Pair<String, Color> >::Element *E = colors.front(); E; E = E->next()) { + Ref<StyleBoxFlat> sb = tm->get_stylebox("frame", "GraphNode"); + if (!sb.is_null()) { + Ref<StyleBoxFlat> frame_style = sb->duplicate(); + Color c = sb->get_border_color(MARGIN_TOP); + Color cn = E->get().second; + cn.a = c.a; + frame_style->set_border_color_all(cn); + node_styles[E->get().first] = frame_style; + } + } } if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { left_vsplit->set_visible(is_visible_in_tree()); @@ -2891,7 +2939,7 @@ void VisualScriptEditor::_menu_option(int p_what) { } if (node.is_valid()) { clipboard->nodes[id] = node->duplicate(); - clipboard->nodes_positions[id] = script->get_node_pos(edited_func, id); + clipboard->nodes_positions[id] = script->get_node_position(edited_func, id); } } } @@ -2951,7 +2999,7 @@ void VisualScriptEditor::_menu_option(int p_what) { List<int> nodes; script->get_node_list(edited_func, &nodes); for (List<int>::Element *E = nodes.front(); E; E = E->next()) { - Vector2 pos = script->get_node_pos(edited_func, E->get()).snapped(Vector2(2, 2)); + Vector2 pos = script->get_node_position(edited_func, E->get()).snapped(Vector2(2, 2)); existing_positions.insert(pos); } } @@ -3014,7 +3062,7 @@ void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) { TreeItem *root = members->get_root(); - Ref<Texture> del_icon = Control::get_icon("Del", "EditorIcons"); + Ref<Texture> del_icon = Control::get_icon("Remove", "EditorIcons"); Ref<Texture> edit_icon = Control::get_icon("Edit", "EditorIcons"); @@ -3065,7 +3113,7 @@ void VisualScriptEditor::_member_option(int p_option) { List<int> nodes; script->get_node_list(name, &nodes); for (List<int>::Element *E = nodes.front(); E; E = E->next()) { - undo_redo->add_undo_method(script.ptr(), "add_node", name, E->get(), script->get_node(name, E->get()), script->get_node_pos(name, E->get())); + undo_redo->add_undo_method(script.ptr(), "add_node", name, E->get(), script->get_node(name, E->get()), script->get_node_position(name, E->get())); } List<VisualScript::SequenceConnection> seq_connections; @@ -3147,7 +3195,6 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_update_members", &VisualScriptEditor::_update_members); ClassDB::bind_method("_change_base_type", &VisualScriptEditor::_change_base_type); ClassDB::bind_method("_change_base_type_callback", &VisualScriptEditor::_change_base_type_callback); - ClassDB::bind_method("_override_pressed", &VisualScriptEditor::_override_pressed); ClassDB::bind_method("_node_selected", &VisualScriptEditor::_node_selected); ClassDB::bind_method("_node_moved", &VisualScriptEditor::_node_moved); ClassDB::bind_method("_move_node", &VisualScriptEditor::_move_node); @@ -3166,6 +3213,8 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_button_resource_previewed", &VisualScriptEditor::_button_resource_previewed); ClassDB::bind_method("_port_action_menu", &VisualScriptEditor::_port_action_menu); ClassDB::bind_method("_selected_connect_node_method_or_setget", &VisualScriptEditor::_selected_connect_node_method_or_setget); + ClassDB::bind_method("_selected_new_virtual_method", &VisualScriptEditor::_selected_new_virtual_method); + ClassDB::bind_method("_cancel_connect_node_method_or_setget", &VisualScriptEditor::_cancel_connect_node_method_or_setget); ClassDB::bind_method("_expression_text_changed", &VisualScriptEditor::_expression_text_changed); @@ -3262,8 +3311,7 @@ VisualScriptEditor::VisualScriptEditor() { graph = memnew(GraphEdit); add_child(graph); - graph->set_area_as_parent_rect(); - graph->set_h_size_flags(SIZE_EXPAND_FILL); + graph->set_anchors_and_margins_preset(Control::PRESET_WIDE); graph->connect("node_selected", this, "_node_selected"); graph->connect("_begin_node_move", this, "_begin_node_move"); graph->connect("_end_node_move", this, "_end_node_move"); @@ -3344,9 +3392,6 @@ VisualScriptEditor::VisualScriptEditor() { undo_redo = EditorNode::get_singleton()->get_undo_redo(); - new_function_menu = memnew(PopupMenu); - new_function_menu->connect("id_pressed", this, "_override_pressed"); - add_child(new_function_menu); updating_members = false; set_process_input(true); //for revert on drag @@ -3366,6 +3411,11 @@ VisualScriptEditor::VisualScriptEditor() { new_connect_node_select->connect("selected", this, "_selected_connect_node_method_or_setget"); new_connect_node_select->get_cancel()->connect("pressed", this, "_cancel_connect_node_method_or_setget"); + new_virtual_method_select = memnew(PropertySelector); + add_child(new_virtual_method_select); + new_virtual_method_select->connect("selected", this, "_selected_new_virtual_method"); + new_virtual_method_select->get_cancel()->connect("pressed", this, "_selected_new_virtual_method"); + port_action_popup = memnew(PopupMenu); add_child(port_action_popup); port_action_popup->connect("id_pressed", this, "_port_action_menu"); @@ -3403,12 +3453,6 @@ void VisualScriptEditor::free_clipboard() { static void register_editor_callback() { ScriptEditor::register_create_script_editor_function(create_editor); - EditorSettings::get_singleton()->set("editors/visual_script/color_functions", Color(1, 0.9, 0.9)); - EditorSettings::get_singleton()->set("editors/visual_script/color_data", Color(0.9, 1.0, 0.9)); - EditorSettings::get_singleton()->set("editors/visual_script/color_operators", Color(0.9, 0.9, 1.0)); - EditorSettings::get_singleton()->set("editors/visual_script/color_flow_control", Color(1.0, 1.0, 1.0)); - EditorSettings::get_singleton()->set("editors/visual_script/color_custom", Color(0.8, 1.0, 1.0)); - EditorSettings::get_singleton()->set("editors/visual_script/color_constants", Color(1.0, 0.8, 1.0)); ED_SHORTCUT("visual_script_editor/delete_selected", TTR("Delete Selected")); ED_SHORTCUT("visual_script_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_F9); diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index dd051ef8e4..db54d10300 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -103,6 +103,7 @@ class VisualScriptEditor : public ScriptEditorBase { PropertySelector *method_select; PropertySelector *new_connect_node_select; + PropertySelector *new_virtual_method_select; VisualScriptEditorVariableEdit *variable_editor; @@ -135,10 +136,7 @@ class VisualScriptEditor : public ScriptEditorBase { Vector<Pair<Variant::Type, String> > args; }; - Map<int, VirtualInMenu> virtuals_in_menu; - - PopupMenu *new_function_menu; - + HashMap<StringName, Ref<StyleBox>, StringNameHasher> node_styles; StringName edited_func; void _update_graph_connections(); @@ -177,6 +175,7 @@ class VisualScriptEditor : public ScriptEditorBase { void _port_action_menu(int p_option); void _selected_connect_node_method_or_setget(const String &p_text); void _cancel_connect_node_method_or_setget(); + void _selected_new_virtual_method(const String &p_text); int error_line; @@ -188,7 +187,6 @@ class VisualScriptEditor : public ScriptEditorBase { void _change_base_type(); void _member_selected(); void _member_edited(); - void _override_pressed(int p_id); void _begin_node_move(); void _end_node_move(); diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp index 6bd052fe26..897e910f20 100644 --- a/modules/visual_script/visual_script_expression.cpp +++ b/modules/visual_script/visual_script_expression.cpp @@ -831,7 +831,6 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { case TK_BUILTIN_FUNC: { //builtin function - Variant::Type bt = Variant::Type(int(tk.value)); _get_token(tk); if (tk.type != TK_PARENTHESIS_OPEN) { _set_error("Expected '('"); @@ -1024,7 +1023,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { case TK_OP_OR: op = Variant::OP_OR; break; case TK_OP_NOT: op = Variant::OP_NOT; break; case TK_OP_ADD: op = Variant::OP_ADD; break; - case TK_OP_SUB: op = Variant::OP_SUBSTRACT; break; + case TK_OP_SUB: op = Variant::OP_SUBTRACT; break; case TK_OP_MUL: op = Variant::OP_MULTIPLY; break; case TK_OP_DIV: op = Variant::OP_DIVIDE; break; case TK_OP_MOD: op = Variant::OP_MODULE; break; @@ -1086,7 +1085,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { case Variant::OP_MODULE: priority = 2; break; case Variant::OP_ADD: priority = 3; break; - case Variant::OP_SUBSTRACT: priority = 3; break; + case Variant::OP_SUBTRACT: priority = 3; break; case Variant::OP_SHIFT_LEFT: priority = 4; break; case Variant::OP_SHIFT_RIGHT: priority = 4; break; @@ -1179,7 +1178,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { if (expression[next_op + 1].is_op) { // this is not invalid and can really appear // but it becomes invalid anyway because no binary op - // can be followed by an unary op in a valid combination, + // can be followed by a unary op in a valid combination, // due to how precedence works, unaries will always disappear first _set_error("Unexpected two consecutive operators."); diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index 5fcc5b0ad9..8d73de9889 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -42,7 +42,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const { - if (method_cache.flags & METHOD_FLAG_CONST || call_mode == CALL_MODE_BASIC_TYPE) + if (method_cache.flags & METHOD_FLAG_CONST || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) return 0; else return 1; @@ -50,7 +50,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const { bool VisualScriptFunctionCall::has_input_sequence_port() const { - if (method_cache.flags & METHOD_FLAG_CONST || call_mode == CALL_MODE_BASIC_TYPE) + if (method_cache.flags & METHOD_FLAG_CONST || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) return false; else return true; @@ -748,6 +748,13 @@ void VisualScriptFunctionCall::_bind_methods() { BIND_ENUM_CONSTANT(CALL_MODE_NODE_PATH); BIND_ENUM_CONSTANT(CALL_MODE_INSTANCE); BIND_ENUM_CONSTANT(CALL_MODE_BASIC_TYPE); + BIND_ENUM_CONSTANT(CALL_MODE_SINGLETON); + + BIND_ENUM_CONSTANT(RPC_DISABLED); + BIND_ENUM_CONSTANT(RPC_RELIABLE); + BIND_ENUM_CONSTANT(RPC_UNRELIABLE); + BIND_ENUM_CONSTANT(RPC_RELIABLE_TO_ID); + BIND_ENUM_CONSTANT(RPC_UNRELIABLE_TO_ID); } class VisualScriptNodeInstanceFunctionCall : public VisualScriptNodeInstance { @@ -756,7 +763,7 @@ public: NodePath node_path; int input_args; bool validate; - bool returns; + int returns; VisualScriptFunctionCall::RPCCallMode rpc_mode; StringName function; StringName singleton; @@ -849,7 +856,13 @@ public: } } else if (returns) { if (call_mode == VisualScriptFunctionCall::CALL_MODE_INSTANCE) { - *p_outputs[1] = v.call(function, p_inputs + 1, input_args, r_error); + if (returns >= 2) { + *p_outputs[1] = v.call(function, p_inputs + 1, input_args, r_error); + } else { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; + r_error_str = "Invalid returns count for call_mode == CALL_MODE_INSTANCE"; + return 0; + } } else { *p_outputs[0] = v.call(function, p_inputs + 1, input_args, r_error); } @@ -1487,6 +1500,19 @@ void VisualScriptPropertySet::_bind_methods() { BIND_ENUM_CONSTANT(CALL_MODE_SELF); BIND_ENUM_CONSTANT(CALL_MODE_NODE_PATH); BIND_ENUM_CONSTANT(CALL_MODE_INSTANCE); + BIND_ENUM_CONSTANT(CALL_MODE_BASIC_TYPE); + + BIND_ENUM_CONSTANT(ASSIGN_OP_NONE); + BIND_ENUM_CONSTANT(ASSIGN_OP_ADD); + BIND_ENUM_CONSTANT(ASSIGN_OP_SUB); + BIND_ENUM_CONSTANT(ASSIGN_OP_MUL); + BIND_ENUM_CONSTANT(ASSIGN_OP_DIV); + BIND_ENUM_CONSTANT(ASSIGN_OP_MOD); + BIND_ENUM_CONSTANT(ASSIGN_OP_SHIFT_LEFT); + BIND_ENUM_CONSTANT(ASSIGN_OP_SHIFT_RIGHT); + BIND_ENUM_CONSTANT(ASSIGN_OP_BIT_AND); + BIND_ENUM_CONSTANT(ASSIGN_OP_BIT_OR); + BIND_ENUM_CONSTANT(ASSIGN_OP_BIT_XOR); } class VisualScriptNodeInstancePropertySet : public VisualScriptNodeInstance { @@ -1526,7 +1552,7 @@ public: value = Variant::evaluate(Variant::OP_ADD, value, p_argument); } break; case VisualScriptPropertySet::ASSIGN_OP_SUB: { - value = Variant::evaluate(Variant::OP_SUBSTRACT, value, p_argument); + value = Variant::evaluate(Variant::OP_SUBTRACT, value, p_argument); } break; case VisualScriptPropertySet::ASSIGN_OP_MUL: { value = Variant::evaluate(Variant::OP_MULTIPLY, value, p_argument); diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index af7b334e46..d3cd839cf3 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -532,6 +532,7 @@ String VisualScriptOperator::get_text() const { L"A or B", //OP_OR, L"A xor B", //OP_XOR, L"not A", //OP_NOT, + L"A in B", //OP_IN, }; return op_names[op]; @@ -1109,7 +1110,7 @@ void VisualScriptConstant::_bind_methods() { } ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt), "set_constant_type", "get_constant_type"); - ADD_PROPERTY(PropertyInfo(Variant::NIL, "value"), "set_constant_value", "get_constant_value"); + ADD_PROPERTY(PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "set_constant_value", "get_constant_value"); } class VisualScriptNodeInstanceConstant : public VisualScriptNodeInstance { @@ -1493,7 +1494,7 @@ void VisualScriptGlobalConstant::_bind_methods() { cc += ","; cc += GlobalConstants::get_global_constant_name(i); } - ADD_PROPERTY(PropertyInfo(Variant::INT, "constant/constant", PROPERTY_HINT_ENUM, cc), "set_global_constant", "get_global_constant"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "constant", PROPERTY_HINT_ENUM, cc), "set_global_constant", "get_global_constant"); } VisualScriptGlobalConstant::VisualScriptGlobalConstant() { @@ -1598,7 +1599,7 @@ VisualScriptNodeInstance *VisualScriptClassConstant::instance(VisualScriptInstan void VisualScriptClassConstant::_validate_property(PropertyInfo &property) const { - if (property.name == "constant/constant") { + if (property.name == "constant") { List<String> constants; ClassDB::get_integer_constant_list(base_type, &constants, true); @@ -1622,7 +1623,7 @@ void VisualScriptClassConstant::_bind_methods() { ClassDB::bind_method(D_METHOD("get_base_type"), &VisualScriptClassConstant::get_base_type); ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "constant/constant", PROPERTY_HINT_ENUM, ""), "set_class_constant", "get_class_constant"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "constant", PROPERTY_HINT_ENUM, ""), "set_class_constant", "get_class_constant"); } VisualScriptClassConstant::VisualScriptClassConstant() { @@ -1727,7 +1728,7 @@ VisualScriptNodeInstance *VisualScriptBasicTypeConstant::instance(VisualScriptIn void VisualScriptBasicTypeConstant::_validate_property(PropertyInfo &property) const { - if (property.name == "constant/constant") { + if (property.name == "constant") { List<StringName> constants; Variant::get_numeric_constants_for_type(type, &constants); @@ -1760,7 +1761,7 @@ void VisualScriptBasicTypeConstant::_bind_methods() { } ADD_PROPERTY(PropertyInfo(Variant::INT, "basic_type", PROPERTY_HINT_ENUM, argt), "set_basic_type", "get_basic_type"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "constant/constant", PROPERTY_HINT_ENUM, ""), "set_basic_type_constant", "get_basic_type_constant"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "constant", PROPERTY_HINT_ENUM, ""), "set_basic_type_constant", "get_basic_type_constant"); } VisualScriptBasicTypeConstant::VisualScriptBasicTypeConstant() { @@ -1881,7 +1882,17 @@ void VisualScriptMathConstant::_bind_methods() { cc += ","; cc += const_name[i]; } - ADD_PROPERTY(PropertyInfo(Variant::INT, "constant/constant", PROPERTY_HINT_ENUM, cc), "set_math_constant", "get_math_constant"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "constant", PROPERTY_HINT_ENUM, cc), "set_math_constant", "get_math_constant"); + + BIND_ENUM_CONSTANT(MATH_CONSTANT_ONE); + BIND_ENUM_CONSTANT(MATH_CONSTANT_PI); + BIND_ENUM_CONSTANT(MATH_CONSTANT_2PI); + BIND_ENUM_CONSTANT(MATH_CONSTANT_HALF_PI); + BIND_ENUM_CONSTANT(MATH_CONSTANT_E); + BIND_ENUM_CONSTANT(MATH_CONSTANT_SQRT2); + BIND_ENUM_CONSTANT(MATH_CONSTANT_INF); + BIND_ENUM_CONSTANT(MATH_CONSTANT_NAN); + BIND_ENUM_CONSTANT(MATH_CONSTANT_MAX); } VisualScriptMathConstant::VisualScriptMathConstant() { @@ -2002,7 +2013,7 @@ void VisualScriptEngineSingleton::_bind_methods() { cc += E->get().name; } - ADD_PROPERTY(PropertyInfo(Variant::STRING, "constant/constant", PROPERTY_HINT_ENUM, cc), "set_singleton", "get_singleton"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "constant", PROPERTY_HINT_ENUM, cc), "set_singleton", "get_singleton"); } VisualScriptEngineSingleton::VisualScriptEngineSingleton() { @@ -2679,7 +2690,7 @@ VisualScriptNodeInstance *VisualScriptCustomNode::instance(VisualScriptInstance } void VisualScriptCustomNode::_script_changed() { - ports_changed_notify(); + call_deferred("ports_changed_notify"); } void VisualScriptCustomNode::_bind_methods() { @@ -2702,7 +2713,10 @@ void VisualScriptCustomNode::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_category")); BIND_VMETHOD(MethodInfo(Variant::INT, "_get_working_memory_size")); - BIND_VMETHOD(MethodInfo(Variant::NIL, "_step:Variant", PropertyInfo(Variant::ARRAY, "inputs"), PropertyInfo(Variant::ARRAY, "outputs"), PropertyInfo(Variant::INT, "start_mode"), PropertyInfo(Variant::ARRAY, "working_mem"))); + + MethodInfo stepmi(Variant::NIL, "_step", PropertyInfo(Variant::ARRAY, "inputs"), PropertyInfo(Variant::ARRAY, "outputs"), PropertyInfo(Variant::INT, "start_mode"), PropertyInfo(Variant::ARRAY, "working_mem")); + stepmi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + BIND_VMETHOD(stepmi); ClassDB::bind_method(D_METHOD("_script_changed"), &VisualScriptCustomNode::_script_changed); @@ -2839,7 +2853,9 @@ VisualScriptNodeInstance *VisualScriptSubCall::instance(VisualScriptInstance *p_ void VisualScriptSubCall::_bind_methods() { - BIND_VMETHOD(MethodInfo(Variant::NIL, "_subcall:Variant", PropertyInfo(Variant::NIL, "arguments:Variant"))); + MethodInfo scmi(Variant::NIL, "_subcall", PropertyInfo(Variant::NIL, "arguments")); + scmi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + BIND_VMETHOD(scmi); } VisualScriptSubCall::VisualScriptSubCall() { @@ -3530,6 +3546,11 @@ void VisualScriptInputAction::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "action"), "set_action_name", "get_action_name"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Pressed,Released,JustPressed,JustReleased"), "set_action_mode", "get_action_mode"); + + BIND_ENUM_CONSTANT(MODE_PRESSED); + BIND_ENUM_CONSTANT(MODE_RELEASED); + BIND_ENUM_CONSTANT(MODE_JUST_PRESSED); + BIND_ENUM_CONSTANT(MODE_JUST_RELEASED); } VisualScriptInputAction::VisualScriptInputAction() { @@ -3722,11 +3743,11 @@ void register_visual_script_nodes() { VisualScriptLanguage::singleton->add_register_func("data/preload", create_node_generic<VisualScriptPreload>); VisualScriptLanguage::singleton->add_register_func("data/action", create_node_generic<VisualScriptInputAction>); - VisualScriptLanguage::singleton->add_register_func("constant/constants/constant", create_node_generic<VisualScriptConstant>); - VisualScriptLanguage::singleton->add_register_func("constant/constants/math_constant", create_node_generic<VisualScriptMathConstant>); - VisualScriptLanguage::singleton->add_register_func("constant/constants/class_constant", create_node_generic<VisualScriptClassConstant>); - VisualScriptLanguage::singleton->add_register_func("constant/constants/global_constant", create_node_generic<VisualScriptGlobalConstant>); - VisualScriptLanguage::singleton->add_register_func("constant/constants/basic_type_constant", create_node_generic<VisualScriptBasicTypeConstant>); + VisualScriptLanguage::singleton->add_register_func("constants/constant", create_node_generic<VisualScriptConstant>); + VisualScriptLanguage::singleton->add_register_func("constants/math_constant", create_node_generic<VisualScriptMathConstant>); + VisualScriptLanguage::singleton->add_register_func("constants/class_constant", create_node_generic<VisualScriptClassConstant>); + VisualScriptLanguage::singleton->add_register_func("constants/global_constant", create_node_generic<VisualScriptGlobalConstant>); + VisualScriptLanguage::singleton->add_register_func("constants/basic_type_constant", create_node_generic<VisualScriptBasicTypeConstant>); VisualScriptLanguage::singleton->add_register_func("custom/custom_node", create_node_generic<VisualScriptCustomNode>); VisualScriptLanguage::singleton->add_register_func("custom/sub_call", create_node_generic<VisualScriptSubCall>); @@ -3742,7 +3763,7 @@ void register_visual_script_nodes() { VisualScriptLanguage::singleton->add_register_func("operators/compare/greater_equal", create_op_node<Variant::OP_GREATER_EQUAL>); //mathematic VisualScriptLanguage::singleton->add_register_func("operators/math/add", create_op_node<Variant::OP_ADD>); - VisualScriptLanguage::singleton->add_register_func("operators/math/subtract", create_op_node<Variant::OP_SUBSTRACT>); + VisualScriptLanguage::singleton->add_register_func("operators/math/subtract", create_op_node<Variant::OP_SUBTRACT>); VisualScriptLanguage::singleton->add_register_func("operators/math/multiply", create_op_node<Variant::OP_MULTIPLY>); VisualScriptLanguage::singleton->add_register_func("operators/math/divide", create_op_node<Variant::OP_DIVIDE>); VisualScriptLanguage::singleton->add_register_func("operators/math/negate", create_op_node<Variant::OP_NEGATE>); diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index 694fb96cfa..421409b265 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* visual_script_nodes.h */ +/* visual_script_nodes.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/modules/visual_script/visual_script_yield_nodes.cpp b/modules/visual_script/visual_script_yield_nodes.cpp index 4b8e17421b..3c9076246d 100644 --- a/modules/visual_script/visual_script_yield_nodes.cpp +++ b/modules/visual_script/visual_script_yield_nodes.cpp @@ -82,7 +82,7 @@ String VisualScriptYield::get_text() const { switch (yield_mode) { case YIELD_RETURN: return ""; break; case YIELD_FRAME: return "Next Frame"; break; - case YIELD_FIXED_FRAME: return "Next Fixed Frame"; break; + case YIELD_PHYSICS_FRAME: return "Next Physics Frame"; break; case YIELD_WAIT: return rtos(wait_time) + " sec(s)"; break; } @@ -122,7 +122,7 @@ public: ret = STEP_EXIT_FUNCTION_BIT; break; //return the yield case VisualScriptYield::YIELD_FRAME: state->connect_to_signal(tree, "idle_frame", Array()); break; - case VisualScriptYield::YIELD_FIXED_FRAME: state->connect_to_signal(tree, "fixed_frame", Array()); break; + case VisualScriptYield::YIELD_PHYSICS_FRAME: state->connect_to_signal(tree, "physics_frame", Array()); break; case VisualScriptYield::YIELD_WAIT: state->connect_to_signal(tree->create_timer(wait_time).ptr(), "timeout", Array()); break; } @@ -186,11 +186,11 @@ void VisualScriptYield::_bind_methods() { ClassDB::bind_method(D_METHOD("set_wait_time", "sec"), &VisualScriptYield::set_wait_time); ClassDB::bind_method(D_METHOD("get_wait_time"), &VisualScriptYield::get_wait_time); - ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Frame,FixedFrame,Time", PROPERTY_USAGE_NOEDITOR), "set_yield_mode", "get_yield_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Frame,Physics Frame,Time", PROPERTY_USAGE_NOEDITOR), "set_yield_mode", "get_yield_mode"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "wait_time"), "set_wait_time", "get_wait_time"); BIND_ENUM_CONSTANT(YIELD_FRAME); - BIND_ENUM_CONSTANT(YIELD_FIXED_FRAME); + BIND_ENUM_CONSTANT(YIELD_PHYSICS_FRAME); BIND_ENUM_CONSTANT(YIELD_WAIT); } @@ -516,7 +516,7 @@ public: } else { //yield - Object *object; + Object *object = NULL; switch (call_mode) { @@ -597,7 +597,7 @@ static Ref<VisualScriptNode> create_yield_signal_node(const String &p_name) { void register_visual_script_yield_nodes() { VisualScriptLanguage::singleton->add_register_func("functions/wait/wait_frame", create_yield_node<VisualScriptYield::YIELD_FRAME>); - VisualScriptLanguage::singleton->add_register_func("functions/wait/wait_fixed_frame", create_yield_node<VisualScriptYield::YIELD_FIXED_FRAME>); + VisualScriptLanguage::singleton->add_register_func("functions/wait/wait_physics_frame", create_yield_node<VisualScriptYield::YIELD_PHYSICS_FRAME>); VisualScriptLanguage::singleton->add_register_func("functions/wait/wait_time", create_yield_node<VisualScriptYield::YIELD_WAIT>); VisualScriptLanguage::singleton->add_register_func("functions/yield", create_yield_node<VisualScriptYield::YIELD_RETURN>); diff --git a/modules/visual_script/visual_script_yield_nodes.h b/modules/visual_script/visual_script_yield_nodes.h index d074962471..4a595a875a 100644 --- a/modules/visual_script/visual_script_yield_nodes.h +++ b/modules/visual_script/visual_script_yield_nodes.h @@ -39,7 +39,7 @@ public: enum YieldMode { YIELD_RETURN, YIELD_FRAME, - YIELD_FIXED_FRAME, + YIELD_PHYSICS_FRAME, YIELD_WAIT }; diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub index d3e4f7e15a..9d2d0feb92 100644 --- a/modules/vorbis/SCsub +++ b/modules/vorbis/SCsub @@ -6,7 +6,7 @@ Import('env_modules') env_vorbis = env_modules.Clone() # Thirdparty source files -if (env['builtin_libvorbis'] != 'no'): +if env['builtin_libvorbis']: thirdparty_dir = "#thirdparty/libvorbis/" thirdparty_sources = [ #"analysis.c", @@ -42,7 +42,7 @@ if (env['builtin_libvorbis'] != 'no'): env_vorbis.Append(CPPPATH=[thirdparty_dir]) # also requires libogg - if (env['builtin_libogg'] != 'no'): + if env['builtin_libogg']: env_vorbis.Append(CPPPATH=["#thirdparty/libogg"]) # Godot source files diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index 6389c286c2..6235799fc2 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -58,7 +58,7 @@ int AudioStreamPlaybackOGGVorbis::_ov_seek_func(void *_f, ogg_int64_t offs, int fa->seek(offs); } else if (whence == SEEK_CUR) { - fa->seek(fa->get_pos() + offs); + fa->seek(fa->get_position() + offs); } else if (whence == SEEK_END) { fa->seek_end(offs); @@ -89,10 +89,10 @@ long AudioStreamPlaybackOGGVorbis::_ov_tell_func(void *_f) { //printf("close %p\n",_f); FileAccess *fa = (FileAccess *)_f; - return fa->get_pos(); + return fa->get_position(); } -int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_bufer, int p_frames) { +int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_buffer, int p_frames) { if (!playing) return 0; @@ -109,9 +109,9 @@ int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_bufer, int p_frames) { //printf("to mix %i - mix me %i bytes\n",to_mix,to_mix*stream_channels*sizeof(int16_t)); #ifdef BIG_ENDIAN_ENABLED - long ret = ov_read(&vf, (char *)p_bufer, todo * stream_channels * sizeof(int16_t), 1, 2, 1, ¤t_section); + long ret = ov_read(&vf, (char *)p_buffer, todo * stream_channels * sizeof(int16_t), 1, 2, 1, ¤t_section); #else - long ret = ov_read(&vf, (char *)p_bufer, todo * stream_channels * sizeof(int16_t), 0, 2, 1, ¤t_section); + long ret = ov_read(&vf, (char *)p_buffer, todo * stream_channels * sizeof(int16_t), 0, 2, 1, ¤t_section); #endif if (ret < 0) { @@ -162,7 +162,7 @@ int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_bufer, int p_frames) { frames_mixed += ret; - p_bufer += ret * stream_channels; + p_buffer += ret * stream_channels; p_frames -= ret; } @@ -180,7 +180,7 @@ void AudioStreamPlaybackOGGVorbis::play(float p_from) { frames_mixed = 0; playing = true; if (p_from > 0) { - seek_pos(p_from); + seek(p_from); } } @@ -203,7 +203,7 @@ void AudioStreamPlaybackOGGVorbis::stop() { //_clear(); } -float AudioStreamPlaybackOGGVorbis::get_pos() const { +float AudioStreamPlaybackOGGVorbis::get_playback_position() const { int32_t frames = int32_t(frames_mixed); if (frames < 0) @@ -211,7 +211,7 @@ float AudioStreamPlaybackOGGVorbis::get_pos() const { return double(frames) / stream_srate; } -void AudioStreamPlaybackOGGVorbis::seek_pos(float p_time) { +void AudioStreamPlaybackOGGVorbis::seek(float p_time) { if (!playing) return; diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h index 03b3726b52..79eadec56e 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.h +++ b/modules/vorbis/audio_stream_ogg_vorbis.h @@ -96,14 +96,14 @@ public: virtual int get_loop_count() const; - virtual float get_pos() const; - virtual void seek_pos(float p_time); + virtual float get_playback_position() const; + virtual void seek(float p_time); virtual int get_channels() const { return stream_channels; } virtual int get_mix_rate() const { return stream_srate; } virtual int get_minimum_buffer_size() const { return 0; } - virtual int mix(int16_t *p_bufer, int p_frames); + virtual int mix(int16_t *p_buffer, int p_frames); AudioStreamPlaybackOGGVorbis(); ~AudioStreamPlaybackOGGVorbis(); diff --git a/modules/webm/SCsub b/modules/webm/SCsub index 889f5e83aa..2f1a28a54c 100644 --- a/modules/webm/SCsub +++ b/modules/webm/SCsub @@ -19,14 +19,14 @@ env_webm.add_source_files(env.modules_sources, thirdparty_libsimplewebm_sources) env_webm.Append(CPPPATH=[thirdparty_libsimplewebm_dir, thirdparty_libsimplewebm_dir + "libwebm/"]) # also requires libogg, libvorbis and libopus -if (env['builtin_libogg'] != 'no'): +if env['builtin_libogg']: env_webm.Append(CPPPATH=["#thirdparty/libogg"]) -if (env['builtin_libvorbis'] != 'no'): +if env['builtin_libvorbis']: env_webm.Append(CPPPATH=["#thirdparty/libvorbis"]) -if (env['builtin_opus'] != 'no'): +if env['builtin_opus']: env_webm.Append(CPPPATH=["#thirdparty/opus"]) -if (env['builtin_libvpx'] != 'no'): +if env['builtin_libvpx']: Export('env_webm') SConscript("libvpx/SCsub") diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub index 0ee2ed45b8..fd8d762a5e 100644 --- a/modules/webm/libvpx/SCsub +++ b/modules/webm/libvpx/SCsub @@ -265,7 +265,7 @@ if env["platform"] == 'uwp': else: import platform is_x11_or_server_arm = ((env["platform"] == 'x11' or env["platform"] == 'server') and platform.machine().startswith('arm')) - is_ios_x86 = (env["platform"] == 'iphone' and env["ios_sim"] == "yes") + is_ios_x86 = (env["platform"] == 'iphone' and env["ios_sim"]) is_android_x86 = (env["platform"] == 'android' and env["android_arch"] == 'x86') if is_android_x86: cpu_bits = '32' diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp index 26344877de..2ec6b27471 100644 --- a/modules/webm/video_stream_webm.cpp +++ b/modules/webm/video_stream_webm.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* av_stream_webm.cpp.cpp */ +/* video_stream_webm.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -59,7 +59,7 @@ public: if (file) { - if (file->get_pos() != (size_t)pos) + if (file->get_position() != (size_t)pos) file->seek(pos); if (file->get_buffer(buf, len) == len) return 0; @@ -204,11 +204,11 @@ float VideoStreamPlaybackWebm::get_length() const { return 0.0f; } -float VideoStreamPlaybackWebm::get_pos() const { +float VideoStreamPlaybackWebm::get_playback_position() const { return video_pos; } -void VideoStreamPlaybackWebm::seek_pos(float p_time) { +void VideoStreamPlaybackWebm::seek(float p_time) { //Not implemented } diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h index a48a6c6b9a..fc0720967a 100644 --- a/modules/webm/video_stream_webm.h +++ b/modules/webm/video_stream_webm.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* av_stream_webm.cpp.cpp */ +/* video_stream_webm.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -81,8 +81,8 @@ public: virtual float get_length() const; - virtual float get_pos() const; - virtual void seek_pos(float p_time); + virtual float get_playback_position() const; + virtual void seek(float p_time); virtual void set_audio_track(int p_idx); diff --git a/modules/webp/SCsub b/modules/webp/SCsub index aa3486a2c5..f9295fed47 100644 --- a/modules/webp/SCsub +++ b/modules/webp/SCsub @@ -6,7 +6,7 @@ Import('env_modules') env_webp = env_modules.Clone() # Thirdparty source files -if (env['builtin_libwebp'] != 'no'): +if env['builtin_libwebp']: thirdparty_dir = "#thirdparty/libwebp/" thirdparty_sources = [ "dec/alpha_dec.c", |